GDB 防止错误

Posted

技术标签:

【中文标题】GDB 防止错误【英文标题】:GDB prevents errors 【发布时间】:2011-12-01 09:45:41 【问题描述】:

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

知道为什么会发生这种情况吗?

我在 Windows 上使用 mingw 工具链。

【问题讨论】:

是的,这听起来像是竞争条件或堆损坏或其他通常导致 Heisenbugs 的原因。问题是您的代码可能在某些地方不正确,但即使被调试的应用程序做了一些有趣的事情,调试器也必须正常运行。在应用程序上尝试 Valgrind。由于您使用的是 MinGW,因此您的应用程序很可能会在 Valgrind 可以运行的环境中编译。 @STATUS_ACCESS_DENIED 他如何在 Windows 上试用 Valgrind? @EmployedRussian 你不能,但如前所述,如果你没有系统特定的库,在 Linux 下编译一个 mingw 项目非常容易 @Ginn: 确实 :) ...如果这适用于您的情况,我也可以将其作为正式答案。 @STATUS_ACCESS_DENIED 请这样做 【参考方案1】:

是的,这听起来像是竞争条件或堆损坏或其他通常导致 Heisenbugs 的原因。问题是您的代码可能在某些地方不正确,但即使被调试的应用程序做了一些有趣的事情,调试器也必须表现得很好。这种方式问题往往会在调试器下消失。对于竞态条件,它们通常不会首先出现,因为一些调试器一次只能处理一个线程,并且所有调试器都会导致代码运行速度变慢,这可能已经使竞态条件消失了。

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

Valgrind,在您的特定情况下,它的默认工具 Memcheck 将通过代码进行模拟。每当您分配内存时,它都会将该内存中的所有字节标记为“受污染”,直到您实际显式初始化它为止。内存字节的污染状态将被memcpy-ing 未初始化的内存继承,一旦使用未初始化的字节做出决定(ifforwhile),就会导致来自 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】:

知道为什么会发生这种情况吗?

通常有几个原因:

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

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

【讨论】:

【参考方案3】:

虽然有点晚了,但可以阅读this 问题的答案,以便能够在不使用 gdb 的情况下设置系统来捕获核心转储。然后他可以使用

加载核心文件
gdb <path_to_core_file> <path_to_executable_file>

然后发出

thread apply all bt

在 gdb 中。

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

【讨论】:

【参考方案4】:

您的应用程序可能正在接收信号,而 gdb 可能不会根据其配置传递它们。您可以使用 info 信号或 info handle 命令检查这一点。它也可能有助于发布崩溃进程的堆栈跟踪。崩溃的进程应该生成一个可以用 gdb 分析的核心文件(如果它没有被禁用)。

【讨论】:

以上是关于GDB 防止错误的主要内容,如果未能解决你的问题,请参考以下文章

linux禁止gdb调试

gdb调试常用功能

如何防止VBS的错误提示

防止 Mongoose 堆栈跟踪错误

如果存在验证错误,防止引导模式关闭

为啥错误:重新渲染太多。 React 限制了渲染的数量以防止无限循环。?