用于隔离堆栈粉碎bug的工具

时间:2012-10-03 15:44:15

标签: c++ c debugging memory-leaks

说得客气一点,我有一个小内存问题,并且没有工具和想法来隔离原因。

我有一个高度多线程(pthreads)的C / C ++程序,它在4.4.4之后和4.7.1之前的GCC优化编译下开发了堆栈粉碎问题。

症状是在创建其中一个线程期间,我获得了完整的堆栈粉碎,而不仅仅是%RIP,但是所有父帧和大多数寄存器都是0x00或其他非感测地址。 哪个线程导致问题似乎是随机的,但是通过日志消息判断它似乎被隔离到相同的Hunk代码,并且似乎在创建新线程时处于半可重复的点。

这使得很难将有问题的代码捕获和隔离到比可能数千行的单个编译单元更加狭窄,因为在违规文件中print()迄今为止在尝试缩小范围时证明是不可靠的活动部分。

导致最终破坏堆栈的线程的线程创建是:

 
extern "C"
{
static ThreadReturnVal ThreadAPI WriterThread(void *act)
{
   Recorder       *rec = reinterpret_cast  (act);
   xuint64        writebytes;
   LoggerHandle m_logger = XXGetLogger("WriterThread");

   if (SetThreadAffinity(rec->m_cpu_mask))
   { ... }
   SetThreadPrio((xint32)rec->m_thread_priority);

   while (true)
   {
     ... poll a ring buffer ... Hard Spin 100% use on a single core, this is that sort of crazy code. 
   }
}

我已经尝试过调试版本,但症状仅存在于优化版本中,-O2或更好。 我已经尝试过Valgrind / memcheck和DRD但是在堆栈被吹走之前都没有找到任何问题(并且需要大约12小时来达到失败)

使用-O2 -Wstack-protector的编译没有看错,  但是使用-fstack-protector-all的构建确实可以保护我免受错误的影响,但不会发出任何错误。

电围栏也陷阱,但只有在堆栈消失后才会陷阱。

问题:哪些其他工具或技术可用于缩小违规部分?

非常感谢, - 比尔

2 个答案:

答案 0 :(得分:4)

解决此类问题的几种方法:

您可以尝试在发生损坏之前在堆栈地址上设置硬件断点,并希望调试器在损坏中提前破坏,以提供模糊的有用调试状态。这里棘手的部分是选择正确的堆栈地址;根据违规线程的“选择”的随机性,这可能不实用。但是从你的一条评论中,听起来通常是新创建的线程被粉碎,所以这可能是可行的。尝试在创建线程期间中断,获取线程的堆栈位置,通过一些疯狂的猜测进行偏移,设置硬件BP,然后继续。根据您是否过早,太晚或根本不打破,调整偏移,冲洗和重复。这基本上是高级猜测和检查,如果腐败模式太随机,可能会严重阻碍或完全不实用,但令人惊讶的是,这通常会导致半清晰的堆栈和成功的调试工作。

另一种选择是开始收集崩溃转储。尝试在崩溃转储之间查找可能有助于使您更接近损坏源的模式。也许你会很幸运,其中一个崩溃转储会“更快”地“崩溃”/“接近来源”。

不幸的是,这两种技术都是科学的艺术;他们是非确定性的,依赖健康的运气等等(至少根据我的经验......据说,有人可以用崩溃转储做出惊人的事情,但需要花费很多时间达到那种技能水平。)

还有一点需要注意:正如其他人所指出的那样,未初始化的内存是调试与发布差异的典型来源,在这里很容易成为您的问题。但是,要记住的另一种可能性是时间差异。线程调度的顺序以及调试与发布之间的顺序通常有很大不同,并且很容易导致同步错误被屏蔽在一个而不是另一个屏蔽。这些差异可能只是由于执行速度的差异,但我认为一些运行时故意在调试环境中混乱线程调度。

答案 1 :(得分:2)

您可以使用静态分析工具检查一些sutble错误,可能找到的错误之一可能是您的错误的原因。您可以找到有关这些工具的一些信息here