使用gdb
时,我在不同的C项目中遇到此问题。
如果我在没有它的情况下运行程序,它会在给定事件中始终崩溃,可能是因为内存读取无效。我尝试用gdb
进行调试,但是当我这样做时,崩溃似乎永远不会发生!
知道为什么会这样吗?
我在Windows上使用mingw
工具链。
答案 0 :(得分:2)
知道为什么会这样吗?
通常有几个原因:
解决这个问题的一种方法是让应用程序捕获被杀死的任何未处理的异常,打印消息并永久旋转。一旦进入该状态,您应该能够将GDB附加到进程,并从那里进行调试。
答案 1 :(得分:2)
是的,这听起来像是竞争条件或堆腐败或其他通常负责Heisenbugs的东西。问题是您的代码在某些地方可能不正确,但即使调试的应用程序执行有趣的事情,调试器也必须表现出来。这样问题就会在调试器下消失。对于竞争条件,它们通常不会出现在第一个位置,因为一些调试器一次只能处理一个线程,并且统一所有调试器都会导致代码运行得更慢,这可能已经使竞争条件消失。
在应用程序上尝试Valgrind。由于您使用的是MinGW,因此您的应用程序可能会在Valgrind可以运行的环境中编译(即使它不直接在Windows上运行)。我已经使用Valgrind大约三年了,它很快就解决了很多谜团。当我得到关于我正在使用的代码(在AIX,Solaris,BSD,Linux,Windows上运行)的崩溃报告时,我将首先在x64和x86 Linux下对Valgrind下的代码进行一次测试运行分别
Valgrind,在您的特定情况下,它的默认工具Memcheck将通过代码模拟。无论何时分配内存,它都会将该内存中的所有字节标记为“受污染”,直到您明确初始化它为止。内存字节的污染状态将由memcpy
继承 - 未初始化的内存,并且一旦使用未初始化的字节做出决定(if
,{{1},将导致Valgrind的报告},for
...)。此外,它会跟踪孤立的内存块,并在运行结束时报告泄漏。但这还不是全部,更多工具是Valgrind系列的一部分,并测试代码的各个方面,包括线程之间的竞争条件(Helgrind,DRD)。
现在假设Linux:确保安装了支持库的所有调试符号。通常这些包含在while
版本的包中或*-debug
中。此外,请确保在代码中关闭优化并包含调试符号。对于GCC而言*-devel
。
另一个提示:我已经知道指针别名引起了一些悲伤。虽然Valgrind能够帮助我跟踪它,但实际上我必须完成最后一步并在其反汇编中验证创建的代码。事实证明,在-ggdb -g3 -O0
,GCC优化器超前了,并将一个循环复制字节转换为一系列指令,一次复制8个字节,但假设对齐。最后一部分是问题所在。关于对齐的假设是错误的。从那以后,我们一直在-O3
建造 - 正如你在this Gentoo Wiki article中看到的那样,这不是最糟糕的主意。引用相关部分。
-O3:这是可能的最高级别的优化,也是最危险的。使用它编译代码需要更长的时间 选项,实际上它不应该在系统范围内使用gcc 4.x. 自版本3.x以来,gcc的行为发生了重大变化。在 3.x,-O3已被证明导致-O2超过了稍快的执行时间,但gcc 4.x不再是这种情况。编译所有 使用-O3的包将导致需要更大的二进制文件 更多的内存,并将显着增加编译的几率 失败或意外的程序行为(包括错误)。该 缺点超过了好处;记住减少的原则 回报。对于gcc 4.x,建议不要使用-O3。
由于你在MinGW中使用GCC,我认为这也适用于你的情况。
答案 2 :(得分:1)
虽然有点晚,但是可以阅读this问题的答案,以便能够设置系统来捕获coredump而不使用gdb。然后他可以使用
加载核心文件gdb <path_to_core_file> <path_to_executable_file>
然后发出
thread apply all bt
在gdb中。
这将显示应用程序崩溃时正在运行的所有线程的堆栈跟踪,并且可以找到导致非法访问的最后一个函数和相应的线程。
答案 3 :(得分:0)
您的应用程序可能正在接收信号,gdb可能无法传递它们,具体取决于其配置。您可以使用info signals或info handle命令进行检查。它也可能有助于发布崩溃进程的堆栈跟踪。崩溃的进程应生成一个核心文件(如果它尚未被禁用),可以使用gdb进行分析。