正常崩溃,但 GDB 不崩溃?

Posted

技术标签:

【中文标题】正常崩溃,但 GDB 不崩溃?【英文标题】:Crashes normally, but not with GDB? 【发布时间】:2011-11-22 08:50:21 【问题描述】:

我的程序在正常运行时因分段错误而崩溃。所以我用 gdb 运行它,但是当我这样做时它不会崩溃。有谁知道为什么会发生这种情况?我知道 Valgrind 的常见问题解答提到了这一点(在 valgrind 下没有崩溃),但我在谷歌中找不到与 gdb 相关的任何信息。如果有人能告诉我原因,或者在发生这种情况时推荐一些要寻找的东西,我将非常感激。

【问题讨论】:

当您找出问题所在时,您会发表评论并进行更新吗?我很想知道问题是什么。 【参考方案1】:

我以前也遇到过这种情况(你并不孤单),但我不记得我做了什么来解决问题(我认为这是双重免费的)。

我的建议是设置您的环境以创建核心转储,然后在程序崩溃后使用 GDB 调查核心转储。在 bash 中,这是通过 ulimit -c size 完成的,其中 size 可以是任何东西;我个人将 50000 用于 25MB 的最大大小;单位以 512 字节为增量。

您可以使用 GDB 通过 gdb program core 调查核心转储。

【讨论】:

+1 有用性。就我而言,它原来是一个未初始化的成员指针。通常,指针会包含垃圾,所以我的 if(bob) delete bob;代码会崩溃,但在 GDB 下我很幸运,得到的值为 0,因此程序正常运行。【参考方案2】:

听起来像Heisenbug :-)

如果您使用的平台能够生成核心文件,则应该可以使用核心文件和 gdb 来查明程序崩溃的位置 - 可以在 here 找到简短说明。

尽管让它崩溃几次,当崩溃是由堆栈粉碎或变量覆盖引起的时,错误可能看起来“四处走动”。

【讨论】:

【参考方案3】:

嗯,我追踪到了一个 pthread_detach 调用。我在做 pthread_detach(&thethread)。我只是拿走了引用并将其更改为 pthread_detach(thethread) 并且它工作正常。我不是很肯定,但也许它是通过分离引用然后在超出范围时再次销毁它来实现双重释放?

【讨论】:

您不应将pthread_t* 传递给pthread_detach 调用。还要检查pthread_detach 的返回值。特别检查ESRCH 错误。 man pthread_detach. 好吧,我的 pthread 只是堆栈上的全局变量,而不是 pthread*。 您应该将您的 pthread 传递给 pthread_detach,就像您在此答案中所做的那样。通过传递thethread 变量的地址,我猜pthread_detach 会因ESRCH 错误而失败,并且程序稍后会在某处崩溃。您可以轻松检查返回值来确认这一点。【参考方案4】:

尝试附加到gdb 中的正在运行的进程,继续,然后重现崩溃。也就是说,不要在gdb内启动程序;而是正常启动程序,然后attach <pid>

有时在逐行单步执行时,不会出现导致程序崩溃的竞争条件,因为竞争风险已被消除或因步骤之间的“长时间”停顿而变得极不可能。

【讨论】:

我不确定您所说的“正常启动程序然后附加 ”是什么意思。只需添加 'attach ' 就像它的命令行参数一样? @Sterling:attach <pid> 是一个gdb 命令。假设您的程序名为my_program。在 shell(或命令)提示符下,使用 ./my_program 运行它。打开另一个shell提示符,启动gdb,确定my_program的运行实例的PID,在gdb的提示符下输入attach <pid>gdb 将附加到进程并暂停它。然后您可以设置断点和/或continue。当my_program segfaults 时,gdb 会让你看到它崩溃的确切位置。 这个技巧让我找到了程序中的问题。【参考方案5】:

检查pthread_detach 调用的返回值。根据your answer,您可能将无效线程句柄传递给pthread_detach

【讨论】:

我检查了返回值,没有得到任何错误。 (回复有点晚,忘了这个帖子了)【参考方案6】:

如果错误取决于时间,gdb 可能会阻止它重复。

【讨论】:

【参考方案7】:

我也遇到过这种情况。

我的解决方案:清理并重建所有内容。

并不是说这总能解决所有问题(在 OP 的情况下,问题是真的错误的),但是如果你在遇到这样的问题时先这样做,你可以为自己节省一些麻烦和时间奇怪的“元”错误。至少根据我的经验,这些事情往往来自应该重建但没有重建的旧目标文件。在 MinGW 和常规 GCC 中。

【讨论】:

【参考方案8】:

我刚刚遇到了一个类似的问题,在我的例子中,它连接到我的链表数据结构中的指针。当我动态创建一个新列表而不初始化结构内的所有指针时,我的程序在GDB之外崩溃了

这是我原来的数据结构:

typedef struct linked_list 
    node *head;
    node *tail;
 list;

typedef struct list_node 
    char *string;
    struct list_node *next;
 node;

当我创建 list 的新“实例”并指定其 headtail 时,程序在 DGB 外部崩溃:

list *createList(void) 
    list *newList = (list *) malloc(sizeof(list));
    if (newList == NULL) return;

    return newList;

在我将createList 函数更改为此之后,一切都开始正常工作了:

list *createList(void) 
    list *newList = (list *) malloc(sizeof(list));
    if (newList == NULL) return;

    newList->head = (node *) 0;
    newList->tail = (node *) 0;

    return newList;

希望在遇到与我的示例类似的未初始化指针的情况时,它可能对某人有所帮助。

【讨论】:

【参考方案9】:

当您使用 gdb 运行代码时,它会四处移动。现在,您之前尝试引用的非法地址——导致段错误的地址——突然变得合法了。这肯定是一种痛苦。但我所知道的追查此类错误的最佳方法是开始在各处放置 printf()s,逐渐缩小范围。

【讨论】:

没有 code 被“移动”,但数据会。如果您不相信我,请尝试在 GDB 中而不是在 GDB 中打印 malloc() 的结果。在 GDB 中,对于 32 字节分配,您始终会得到 0x601010,但在正常运行时会得到“随机”值。 反之亦然:GDB 默认情况下禁用地址随机化,因此在 GDB 下数据确实移动(这通常是你调试时需要)。禁用随机化实际上可以使错误消失。您可以使用set disable-address-randomizaiton off 在 GDB 下启用随机化 感谢俄罗斯人。正确的命令是set disable-randomization off。它确实允许我在 gdb 中复制我的崩溃。

以上是关于正常崩溃,但 GDB 不崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

我的 Qt 项目中的 gdb 崩溃

通过析构函数删除时崩溃

使用 minidumps 和 GDB 分析 mingw 编译的可执行文件的崩溃?

C程序在GDB中工作,单独运行时崩溃

linux下使用gdb调试崩溃和死锁实例

GDB:列出崩溃进程的所有映射内存区域