简体   繁体   English

如何在Android NDK上调试C ++删除调用?

[英]how can i debug a C++ delete call on android NDK?

I've a Android C++ application that is manged by the Java layer. 我有一个由Java层管理的Android C ++应用程序。 In this code i use a old physics library (tokamak) and i do almost nothing, i create and delete the simulator like this: 在此代码中,我使用了一个旧的物理库(tokamak),并且几乎什么也不做,我创建并删除了模拟器,如下所示:

static neSimulator *gSim;
neV3 gravity; gravity.Set(0.0f, -10.f, 0.0f);
neSimulatorSizeInfo sizeInfo;
sizeInfo.rigidBodiesCount = 1;
sizeInfo.animatedBodiesCount = 1;
sizeInfo.geometriesCount = 2;
sizeInfo.overlappedPairsCount = 2;
gSim = neSimulator::CreateSimulator(sizeInfo, NULL, &gravity);

And the destroy it: 然后销毁它:

neSimulator::CreateSimulator(gSim);

This works, the problem appears when i start adding geometry: 这可行,当我开始添加几何时出现问题:

neV3 ballPos;
rgdBall = gSim->CreateRigidBody();
neGeometry *geoBall = rgdBall->AddGeometry();
geoBall->SetSphereDiameter(1.5f);
rgdBall->UpdateBoundingInfo();
rgdBall->SetMass(2.0f);
rgdBall->SetInertiaTensor(neSphereInertiaTensor(1.5f, 2.0f));
ballPos.Set(0.0f, 5.0f, 0.0f);
rgdBall->SetPos(ballPos);

In this case when i call the destroy (and i only call it once) i get a SIGSEGV (Null Pointer) deadbaad. 在这种情况下,当我调用销毁(并且只调用一次)时,我得到了SIGSEGV(空指针)deadbaad。

I've all debugging log statements to the destructor method and the code inside the destructor completes to the end. 我已经全部调试日志报表的析构函数方法和析构函数中的代码完成到最后。 So there is this code: 因此,有以下代码:

void neSimulator::DestroySimulator(neSimulator * sim)
{
    __android_log_print(ANDROID_LOG_INFO, "TOKAMAK", "Before cast");
    neFixedTimeStepSimulator * s = reinterpret_cast<neFixedTimeStepSimulator *>(sim);
    __android_log_print(ANDROID_LOG_INFO, "TOKAMAK", "After cast");
    __android_log_print(ANDROID_LOG_INFO, "TOKAMAK", "Before delete");
    delete s;
    __android_log_print(ANDROID_LOG_INFO, "TOKAMAK", "After delete");
}

So i log the destructor: 所以我登录析构函数:

neFixedTimeStepSimulator::~neFixedTimeStepSimulator()
{
    FreeAllBodies();

    if (perf)
        delete perf;
        __android_log_print(ANDROID_LOG_INFO, "TOKAMAK", "dtor complete");
}

What is messing me is that i see the dtor complete message on the log but not the After delete message and a SIGSEGV error. 让我感到困惑的是,我在日志中看到dtor complete消息,但没有After After消息和SIGSEGV错误。

How can i investigate it better? 我如何更好地进行调查?

[More info after further investigation] [进一步调查后的更多信息]

So i used the addr2line tool to investigate the stack trace and traced the error to the default memory allocator. 因此,我使用addr2line工具调查了堆栈跟踪,并将错误跟踪到默认的内存分配器。 So i added logging to all alloc and free calls: 所以我加了登录到所有alloc和免费电话:

03-23 13:31:14.617: INFO/neAllocatorDefault(326): malloc 0x1b3fd8 size 2292
03-23 13:31:14.617: INFO/neAllocatorDefault(326): malloc 0x1b48d0 size 488
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x44ae3008 size 114404
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1a58b8 size 8
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1b4ac0 size 800
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1b4de8 size 416
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1b4f90 size 836
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1aca10 size 44
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1b52d8 size 2500
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1b5ca0 size 2500
03-23 13:31:14.627: INFO/neAllocatorDefault(326): malloc 0x1b6668 size 2500
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1b7030 size 400
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1b71c8 size 800
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x424ed008 size 72404
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1b74f0 size 4004
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1b8498 size 2044
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1b8c98 size 6044
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1ba438 size 5004
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1bb7c8 size 11204
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1be390 size 340
03-23 13:31:14.637: INFO/neAllocatorDefault(326): malloc 0x1be4e8 size 4000
03-23 13:31:14.647: INFO/neAllocatorDefault(326): malloc 0x1bf490 size 4000
03-23 13:31:14.647: INFO/neAllocatorDefault(326): malloc 0x1c0438 size 38800
03-23 13:31:14.647: INFO/neAllocatorDefault(326): malloc 0x1c9bd0 size 38800

And

03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b71c8
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b7030
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b6668
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b5ca0
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b52d8
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1aca10
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b4f90
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b4de8
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b4ac0
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1a58b8
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x44ae3008
03-23 13:31:19.508: INFO/neAllocatorDefault(326): free 0x1b48d0

So the SIGSEGV happens when trying to free 0x1b48d0, the funny thing is that there is a previous malloc that returned that pointer and no previous free. 因此,SIGSEGV在尝试释放0x1b48d0时发生,有趣的是,有一个先前的malloc返回了该指针,而没有先前的释放。 I am even more puzzled now... 我现在更加困惑

I'd suspect the reinterpret_cast in DestroySimulator to be wrong. 我怀疑DestroySimulatorreinterpret_cast错误。 The object probably gets deleted using different type than it actually is, which invokes the wrong destructor, which in turn damages the allocator metadata and causes free to crash. 可能被使用不同类型的比它实际上被删除的对象,调用析构函数错误,这反过来又损害分配器元数据,并导致free崩溃。

There are two important things to observe when deleting casted objects: 删除强制转换的对象时,需要注意两个重要事项:

  1. The destructor to be called is selected based on static type of the expression in delete . 根据delete表达式的静态类型选择要调用的析构函数。 So the type has to be the same as the type passed to new, or only if the object has virtual destructor, may be a base type of the type passed to new. 因此,该类型必须与传递给new的类型相同,或者仅在对象具有虚拟析构函数的情况下才可以是传递给new的类型的基本类型。
  2. Casting between pointer to derived class and pointer to it's base class may not be numerically equivalent. 指向派生类的指针和指向它的基类的指针之间的转换可能在数值上不等效。 The static_cast knows how to adjust the pointer, but by using reinterpret_cast you explicitly tell the compiler to not adjust it, so the pointer may be wrong (it should only happen if multiple inheritance is involved, but it may be somewhere deep in the library). static_cast知道如何调整指针,但是通过使用reinterpret_cast您明确告诉编译器不要调整指针,因此指针可能是错误的(仅在涉及多个继承的情况下才会发生,但它可能在库的深处) 。

Using reinterpret_cast in C++ is almost always wrong. 在C ++中使用reinterpret_cast几乎总是错误的。

Found the problem. 找到了问题。 Tokamak uses a special allocation method to avoid exception (i think). 托卡马克使用一种特殊的分配方法来避免异常(我认为)。 For each new object instantiation it does something like: 对于每个新对象实例化,它都执行以下操作:

// assume you have a class MyObject
// and the default allocator is the C malloc/free functions:
MyObject *obj = new (malloc(sizeof(MyObject))) MyObject;
... do something with the obj ...
free(obj);

So that works just fine for object the problem is with arrays. 所以这对于对象来说很好,问题出在数组上。 With arrays, Tokamak code was doing: 对于数组,托卡马克代码正在执行:

MyObject[] *obj = new (malloc(sizeof(MyObject) * elements + 4)) MyObject[];
... do something with the obj ...
free(obj); // Kaboom!!!

The thing here seems to be that ndk compiler uses more than 4bytes (32bit int) for array indexes, if i expand the code to be +8 then everything just works. 这里的事情似乎是ndk编译器对数组索引使用了超过4bytes(32bit int),如果我将代码扩展为+8,那么一切都将正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM