GDB可以防止错误

使用gdb时,我在不同的C项目上遇到了这个问题。 如果我在没有它的情况下运行我的程序,它会在给定的事件中始终崩溃,可能是因为内存读取无效。 我尝试用gdb调试它,但是当我这样做时,崩溃似乎永远不会发生!

知道为什么会这样吗?

我在Windows上使用mingw工具链。

是的,这听起来像竞争条件或堆腐败或其他通常负责Heisenbugs的东西。 问题是你的代码在某些地方可能不正确,但即使被调试的应用程序做了有趣的事情,调试器也必须表现出来。 这样问题就会在调试器下消失。 对于竞争条件,它们通常不会出现在第一位,因为一些调试器一次只能处理一个线程,并且统一所有调试器都会导致代码运行速度变慢,这可能已经使竞争条件消失。

在应用程序上尝试Valgrind 。 由于您使用的是MinGW,因此您的应用程序可能会在Valgrind可以运行的环境中编译(即使它不直接在Windows上运行)。 我已经使用Valgrind大约三年了,它很快就解决了很多谜团。 当我得到关于我正在使用的代码(在AIX,Solaris,BSD,Linux,Windows上运行)的崩溃报告时,我将首先在x64和x86 Linux下对Valgrind下的代码进行一次测试运行分别。

Valgrind,在您的特定情况下,它的默认工具Memcheck将通过代码模拟。 无论何时分配内存,它都会将该内存中的所有字节标记为“受污染”,直到您明确初始化它为止。 内存字节的污染状态将由memcpy -ing未初始化的内存inheritance,并且一旦使用未初始化的字节做出决定( ifforwhile ……),将导致Valgrind的报告。 此外,它会跟踪孤立的内存块,并在运行结束时报告泄漏。 但这还不是全部,更多工具是Valgrind系列的一部分,并测试代码的各个方面,包括线程之间的竞争条件(Helgrind,DRD)。

现在假设Linux:确保安装了支持库的所有调试符号。 通常那些包含*-debug版本的包或*-devel 。 此外,请确保关闭代码中的优化并包含调试符号。 对于GCC来说是-ggdb -g3 -O0

另一个提示:我已经知道指针别名引起了一些悲伤。 虽然Valgrind能够帮助我追踪它,但实际上我必须完成最后一步并在反汇编中validation创建的代码。 事实certificate,在-O3 ,GCC优化器领先于自己,并将一个循环复制字节转换为一系列指令,一次复制8个字节,但假定对齐。 最后一部分是问题所在。 关于对齐的假设是错误的。 从那时起,我们就开始使用-O2 – 正如你将在Gentoo Wiki文章中看到的那样, 这不是最糟糕的主意。 引用相关部分

-O3:这是可能的最高级别的优化,也是最危险的。 使用此选项编译代码需要更长的时间,实际上它不应该在系统范围内与gcc 4.x一起使用。 自版本3.x起,gcc的行为发生了重大变化。 在3.x中,-O3已被certificate导致-O2的执行时间略微加快,但gcc 4.x不再是这种情况。 使用-O3编译所有包将导致需要更多内存的更大二进制文件,并将显着增加编译失败或意外程序行为(包括错误)的几率。 缺点超过了好处; 记住收益递减的原则。 对于gcc 4.x,不建议使用-O3。

由于你在MinGW中使用GCC,我认为这也适用于你的情况。

知道为什么会这样吗?

通常有几个原因:

  1. 您的应用程序具有多个线程,具有竞争条件,并且在GDB下运行会影响计时,从而不再发生崩溃
  2. 您的应用程序有一个受内存布局影响的错误(通常是读取未初始化的内存),并且在GDB下运行时布局会发生变化。

解决这个问题的一种方法是让应用程序捕获被杀死的任何未处理的exception,打印消息并永久旋转。 一旦进入该状态,您应该能够将GDB附加到进程,并从那里进行调试。

虽然有点晚,但是可以阅读这个问题的答案,以便能够设置一个系统来捕获coredump而不使用gdb。 然后他可以使用加载核心文件

 gdb   

然后发出

 thread apply all bt 

在gdb中。

这将显示应用程序崩溃时正在运行的所有线程的堆栈跟踪,并且可以找到导致非法访问的最后一个函数和相应的线程。

您的应用程序可能正在接收信号,gdb可能不会传递它们,具体取决于其配置。 您可以使用info signals或info handle命令进行检查。 发布崩溃进程的堆栈跟踪也可能有所帮助。 崩溃的进程应生成一个核心文件(如果它尚未被禁用),可以使用gdb进行分析。