我如何调试Android NDK上的C++删除调用?
问题描述:
我有一个由Java层管理的Android C++应用程序。在这段代码中我使用的是旧的物理库(托卡马克),我几乎做了什么,我创建和删除这样的模拟器:我如何调试Android NDK上的C++删除调用?
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);
而摧毁它:
neSimulator::CreateSimulator(gSim);
这个作品中,
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);
在这种情况下,当我调用destroy(我只调用一次),我收到了SIGSEGV(空指针)deadbaad:当我开始添加几何出现问题。
我已经将所有的日志语句调试到析构函数方法中,析构函数中的代码完成到最后。所以有这样的代码:
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");
}
所以我登录的析构函数:
neFixedTimeStepSimulator::~neFixedTimeStepSimulator()
{
FreeAllBodies();
if (perf)
delete perf;
__android_log_print(ANDROID_LOG_INFO, "TOKAMAK", "dtor complete");
}
什么是搞乱我的是,我看到在日志中的析构函数完整的消息,但不删除消息和SIGSEGV后错误。
如何更好地调查它?
[经进一步调查后更多信息]
所以我用了addr2line工具来调查堆栈跟踪和追踪误差为默认的内存分配器。所以我加了登录到所有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
而且
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
所以SIGSEGV发生在设法释放0x1b48d0,有趣的是,有是返回指针之前的malloc并没有以前的免费。我现在更加困惑...
答
我怀疑reinterpret_cast
在DestroySimulator
是错的。该对象可能使用与实际不同的类型进行删除,这会调用错误的析构函数,从而损害分配程序元数据并导致free
崩溃。
有删除铸造对象时观察到两个重要的事情:
- 被称为析构函数是基于
delete
静态型表达的选择。因此,类型必须与传递给new的类型相同,或者仅当该对象具有虚拟析构函数时,才可以是传递给new的类型的基本类型。 - 在指向派生类的指针和指向它的基类的指针之间进行Casting可能在数值上不等效。
static_cast
知道如何调整指针,但通过使用reinterpret_cast
您明确告诉编译器不要调整它,所以指针可能是错误的(它应该只发生在涉及多个继承的情况下,但它可能位于库中的某处)。
在C++中使用reinterpret_cast
几乎总是错的。
答
发现问题。托卡马克使用特殊的分配方法来避免异常(我认为)。对于每个新的对象实例,它做类似于:
// 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);
因此,对于对象工作得很好,问题是数组。随着阵列,托卡马克代码是这样做的:
MyObject[] *obj = new (malloc(sizeof(MyObject) * elements + 4)) MyObject[];
... do something with the obj ...
free(obj); // Kaboom!!!
的事情在这里似乎是NDK编译器使用4字节相比(32位INT)数组索引的更多,如果我的代码扩展为+8那么一切都只是工作。
否reinterpret_cast是正确的,因为这两种类型是相关的,lib返回的是neFixedTimeStepSimulator的子类。做一些验证测试显示两个对象的完整性是正确的。 – 2011-03-23 16:23:59
@Paulo:如果它是一个子类,'reinterpret_cast'尤其不正确。如果这些类型是相关的,你应该至少使用'static_cast'。事实上,如果它返回声明类型的子类,它可能是多态的(具有虚拟方法,否则这将是无用的事情),您可以使用'dynamic_cast'在运行时检查该对象的确是给定的类型。 – 2011-03-24 06:10:27