2011-12-01 61 views
2

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

任何想法为什么会发生这种情况?

我在Windows上使用mingw工具链。

+4

是的,这听起来像是一个竞赛条件或堆腐败或其他通常负责海森虫的事情。问题在于你的代码在某些地方可能不正确,但即使被调试的应用程序做了有趣的事情,调试器也必须运行。在应用程序上尝试Valgrind。由于您使用的是MinGW,因此您的应用程序可能会在Valgrind可以运行的环境下编译。 – 0xC0000022L

+0

@STATUS_ACCESS_DENIED他如何在Windows上试用Valgrind? –

+0

@EmployedRussian如果你没有系统特定的库,你不能,但正如所说的,编译Linux下的mingw项目很容易 – Giann

回答

2

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

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

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

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

另一个提示:我已经指出,指针别名造成了一些悲伤。尽管Valgrind能够帮助我追踪它,但实际上我必须做最后一步并在反汇编中验证创建的代码。原来,在-O3 GCC优化器本身领先,并将一个循环复制字节转换为一个指令序列,一次复制8个字节,但假定对齐。最后一部分是问题。关于对齐的假设是错误的。从那以后,我们采用了-O2的建设方式 - 正如你在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. 你的应用程序有多个线程,具有竞争条件和GDB下运行以这样的方式崩溃不再发生
  2. 您的应用程序会影响时序有一个受内存布局影响的错误(通常读取未初始化的内存),并且在GDB下运行时布局会发生变化。

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

0

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

1

虽然有点晚,但可以通过阅读this问题的答案,以便能够设置系统来捕获coredump而不使用gdb。然后,他可以使用

gdb <path_to_core_file> <path_to_executable_file> 

加载核心文件,然后在gdb发出

thread apply all bt 

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