解决随机崩溃问题

时间:2010-08-09 06:34:54

标签: c++ debugging crash lua mingw

我的C ++应用程序出现随机崩溃,可能一个月没有崩溃,然后在一小时内崩溃10次,有时可能会在启动时崩溃,而有时可能会在运行几个小时后崩溃(或者根本没有崩溃。)

我在GNU / Linux上使用GCC,在Windows上使用MingW,因此我无法使用Visual Studio JIT Debug ......

我不知道如何继续,随机查看代码是行不通的,代码是巨大的(好的部分不是我的工作,也有很多遗留的东西),而且我也没有关于如何重现崩溃的线索。

编辑:很多人提到......我如何制作核心转储,小型转储或其他转储?这是我第一次需要进行事后调试。

EDIT2:实际上,DrMingw捕获了一个调用堆栈,没有内存信息......不幸的是,调用堆栈对我没什么帮助,因为接近结束突然它进入了一些我不知道的库(或其他东西)有调试信息,只导致一些十六进制数...所以我仍然需要一些体面的转储,提供更多的信息(特别是关于内存中的内容...具体来说,在那里给出了“访问冲突”错误的地方)

另外,我的应用程序使用Lua和Luabind,可能错误是由.lua脚本引起的,但我不知道如何调试它。

16 个答案:

答案 0 :(得分:29)

尝试Valgrind(它是免费的,开源的):

  

目前Valgrind发行   包括六个生产质量的工具:   内存错误检测器,两个线程   错误检测器,缓存和   分支预测分析器,a   调用图生成缓存分析器,   和一个堆分析器。它还包括   两个实验工具:a   堆/堆栈/全局数组溢出   检测器和SimPoint基本块   矢量发电机。它运行在   以下平台:X86 / Linux,   AMD64 / Linux,PPC32 / Linux,PPC64 / Linux,   和X86 / Darwin(Mac OS X)。

Valgrind Frequently Asked Questions

包的Memcheck部分可能是开始的地方:

  

Memcheck是一个内存错误检测器。   它可以检测到以下问题   这在C和C ++程序中很常见。

     
      
  • 访问内存你不应该,例如超越和低于堆   块,超越了顶部   堆栈,并在它之后访问内存   已被释放。

  •   
  • 使用未定义的值,即尚未初始化的值,或   来自其他的   未定义的值。

  •   
  • 不正确地释放堆内存,例如双重释放堆块,或者   malloc / new / new []的使用不匹配   与免费/删除/删除[]

  •   
  • 在memcpy及相关函数中重叠src和dst指针。

  •   
  • 内存泄漏。

  •   

答案 1 :(得分:14)

首先,您很幸运,您的流程会在短时间内多次崩溃。这应该很容易继续。

这是你继续下去的方式。

  • 获取崩溃转储
  • 隔离一组潜在的可疑功能
  • 收紧状态检查
  • 重复

获取崩溃转储

首先,你真的需要一个崩溃转储。

如果在崩溃时没有获得崩溃转储,请从编写产生可靠崩溃转储的测试开始。

使用调试符号重新编译二进制文件,或确保可以使用调试符号分析故障转储。

查找可疑功能

鉴于您有崩溃转储,请在gdb或您最喜欢的调试器中查看它,并记住显示所有线程!它可能不是您在gdb中看到的有问题的线程。

查看gdb说你的二进制文件崩溃的地方,隔离一些你认为可能导致问题的功能集。

查看多个崩溃并隔离在所有崩溃中通常处于活动状态的代码部分可以节省大量时间。

加强状态检查

通常会发生崩溃,因为某些状态不一致。最好的方法是收紧国家要求。您可以通过以下方式执行此操作。

对于您认为可能导致问题的每个函数,记录输入或对象在进入函数时必须具有的合法状态。 (对退出函数时必须具有的合法状态做同样的事情,但这并不太重要。)

如果函数包含循环,请记录每次循环迭代开始时需要具有的合法状态。

为所有合法国家的表达添加断言。

<强>重复

然后重复这个过程。如果它仍然在断言之外崩溃,则进一步收紧断言。在某些时候,进程将在一个断言上崩溃,而不是因为一些随机崩溃。在这一点上,你可以集中精力试图找出是什么让你的程序从进入函数的合法状态转变为断言发生时的非法状态。

如果将断言与详细日志记录配对,则应该更容易理解程序的作用。

答案 2 :(得分:13)

如果所有其他方法都失败(特别是如果调试器下的性能不可接受),则进行大量日志记录。从入口点开始 - 是应用程序事务吗?记录每个事务的进入。记录密钥对象的所有构造函数调用。由于崩溃是间歇性的,因此请记录每天可能无法调用的所有函数。

你至少会开始缩小崩溃可能

的范围。

答案 3 :(得分:8)

在调试器下启动程序(我确定调试器与GCC和MingW一起)并等到它在调试器下崩溃。在崩溃时,您将能够看到哪些特定操作失败,查看汇编代码,寄存器,内存状态 - 这通常可以帮助您找到问题的原因。

答案 4 :(得分:8)

在我工作的地方,崩溃程序通常会生成一个可以在windbg中加载的核心转储文件。

然后我们在程序崩溃时有一个内存映像。没有什么可以用它来做,但至少它给你最后一个调用堆栈。一旦你知道崩溃的功能,你就可以追踪问题,至少你可以将问题减少到更可重复的测试用例。

答案 5 :(得分:6)

这些类型的错误总是很棘手 - 除非您可以重现错误,否则您唯一的选择是更改您的应用程序,以便记录额外的信息,然后等到错误再次发生。

有一个名为Process Dumper的优秀工具可用于获取遇到异常或意外退出的进程的故障转储 - 您可以要求用户安装该应用程序并为您的应用程序配置规则。

或者,如果您不想要求用户安装其他应用程序,您可以让应用程序监视异常并通过调用MiniDumpWriteDump创建转储本身。

另一种选择是改进日志记录,但是要弄清楚要记录哪些信息(不仅记录所有内容)可能会很棘手,因此可能需要多次迭代崩溃 - 更改记录来追捕问题。

正如我所说,这些类型的错误总是难以诊断 - 根据我的经验,它通常需要花费数小时的时间查看日志和崩溃转储,直到突然间你得到所有的一切感觉 - 关键是收集正确的信息。

答案 6 :(得分:6)

听起来你的程序正在遭受内存损坏。如前所述,你在Linux上的最佳选择可能是valgrind。但这里还有两个选择:

  • 首先使用debug malloc。几乎所有的C库都提供了一个初始化内存的调试malloc实现(正常malloc在内存中保留“旧”内容),检查已分配块的边界是否存在损坏等等。如果这还不够,可以选择第三方实施。

  • 您可能希望了解VMWare Workstation。我没有这样设置,但是从他们的营销材料中他们支持一种相当有趣的调试方式:在“录制”虚拟机中运行debugee。当发生内存损坏时,在损坏的地址处设置内存断点,然后在VM中将转回时间,直到该内存被覆盖的那一刻。有关如何使用Linux / gdb设置重播调试,请参阅this PDF。我相信Workstation 7有15或30天的演示,可能足以摆脱代码中的错误。

答案 7 :(得分:4)

valgrind下的Linux上运行应用程序以查找内存错误。随机崩溃通常会破坏内存。

使用valgrind的memcheck工具修复您发现的每个错误,然后希望崩溃将消失。

如果整个程序在valgrind下运行时间太长,那么将功能拆分为单元测试,并在valgrind下运行那些,希望你能找到导致问题的内存错误。

如果没有,则确保已启用coredump(ulimit -a),然后当它崩溃时,您将能够找到gdb的位置。

答案 8 :(得分:4)

你已经听说过如何在linux下处理这个问题:检查核心转储并在valgrind下运行你的代码。所以你的第一步可能是在Linux下找到错误,然后检查它们是否在mingw下消失。既然没有人在这里提到 mudflap ,我会这样做:如果您的Linux发行版提供它,请使用mudflap。 mudflap通过跟踪实际允许指针指向的信息来帮助您捕获指针滥用和缓冲区溢出:

对于Windows: mingw有一个JIT调试器,名为DrMingw:

答案 9 :(得分:3)

听起来像竞争条件一样棘手。

我建议你创建一个调试版本并使用它。您还应该确保在程序崩溃时创建核心转储。

下次程序崩溃时,您可以在coredump上启动gdb并查看问题所在。它可能是一个连续的错误,但这应该让你开始。

答案 10 :(得分:3)

我要做的第一件事是使用gdb(Windows和Linux)调试核心转储。第二个是运行像Lint,Prefast(Windows),Clang Analyzer或其他一些静态分析程序(为许多误报做好准备)的程序。第三件事是某种运行时检查,如Valgrind(或其近似变体),Microsoft Application VerifierGoogle Perftools

并记录。哪个不必去磁盘。例如,您可以登录到全局std::list<std::string>,这将被修剪到最后100个条目。捕获异常时显示该列表的内容。

答案 11 :(得分:3)

  1. 开始记录。将日志记录语句放在您认为代码片段的位置。专注于测试代码,并重复,直到您将问题缩小到模块或功能。

  2. 把断言放在任何地方!

  3. 当你在它时,只在断言中放入一个表达式。

  4. 为您认为失败的代码编写单元测试。这样,您就可以独立于运行时环境的其余部分来运行代码。

  5. 编写更多自动化测试来解决有问题的代码。

  6. 不要在失败的错误代码之上添加更多代码。这只是一个愚蠢的想法。

  7. 了解如何编写小型转储并进行事后调试。看起来这里的其他人已经很好地解释了这一点。

  8. 从尽可能多的不同方法中运用不良代码,以便您可以隔离错误。

  9. 使用调试版本。如果可能,在调试器下运行调试版本。

  10. 通过删除二进制文件,模块等来修剪您的应用程序......如果可能,以便您可以更轻松地尝试重现该错误。

答案 12 :(得分:1)

你可能犯了一个内存错误,你把某些值放到了没有分配空间的地方,这是随机崩溃的一个很好的理由,很长一段时间没有人试图使用那个内存所以没有错误,你可以采取看看你分配内存的地方,并检查你在哪里广泛使用指针。 除此之外,正如其他人指出的那样,你应该在屏幕和文件中使用大量的日志记录。

答案 13 :(得分:1)

另一项基本检查:确保您对项目进行完全重建。如果您一直在调整各种文件(特别是头文件)并进行部分构建,那么如果您的构建依赖关系不完美,事情就会变得混乱。完全重建只是消除了这种可能性。

对于Windows,请查看Microsoft的Debugging tools for Windows,特别是他们的gflags工具。

答案 14 :(得分:0)

另外两个指针/想法(除了Linux上的核心转储和valgrind):

1)尝试诺基亚的“Qt Creator”。它支持mingw,可以作为事后调试器。

2)如果可行,也许只是在gdb中不断运行应用程序?

答案 15 :(得分:0)

如果您的应用程序不是Windows特定的,您可以尝试在其他平台上编译和运行您的程序,例如Linux(不同的发行版,32/64位,......如果您有奢侈品)。这可能有助于触发程序的错误。当然你应该使用其他帖子中提到的工具,如gdb,valgrind等。