多线程程序中的分段错误和 gdb 回溯信息不完整

Posted

技术标签:

【中文标题】多线程程序中的分段错误和 gdb 回溯信息不完整【英文标题】:Segmentation Fault in Multithreaded program and incomplete information on gdb backtrace 【发布时间】:2019-10-30 07:44:36 【问题描述】:

我正在编写一个同时使用操作系统线程和用户线程的程序(光纤,我已经编写了这个用户线程程序,通过汇编语言进行上下文切换)。问题是程序有时会以分段错误结束,但有时不会。

问题是由于调用了一个带有无效参数的函数,该函数不应被调用。我认为 gdb 回溯没有提供正确的信息。这是我的 gdb 程序的输出

#0  0x0000000000000000 in ?? ()
#1  0x0000555555555613 in thread_entry (fn=0x0, arg=0x0) at userThread2.cpp:243
#2  0x000055555555c791 in start_thread () at contextSwitch2.s:57
#3  0x0000000000000000 in ?? ()

fn 是我想作为用户线程运行的函数,arg 是传递给该函数的参数。 我的用户线程库代码中有一个函数 Spawn,它将两个参数(fn 和 arg)和指向 start_thread 的指针推送到堆栈上,因此调用了一个汇编函数 start_thread,它调用了 c++ 函数 thread_entry 来调用函数 fn带参数 arg。

我不希望在出错时调用 start_thread 或 thread_entry,所以我不确定 start_thread 是如何被调用的。即使它被调用,Spawn() 也应该调用 start_thread,因为它是唯一调用 start_thread 的函数。但是 Spawn 没有显示在 gdb 回溯中。

一些在线帖子提到了堆栈损坏或类似错误结果的可能性,并规定使用“record btrace pt”。我花了相当多的时间在内核/gdb 中设置 intel btrace pt 支持,但我无法设置它,所以我没有走那条路。

这是我的代码的链接以及编译说明: https://github.com/smartWaqar/userThreading

【问题讨论】:

如果您的代码编译时启用了调试信息?请出示minimal reproducible example @AlanBirtles 我添加了一个指向我的代码的链接。这是我对最小可复制示例的第一次介绍。据我了解,我在链接中提供的文件构成了最小的可重现示例。 多线程在 C++ 中没有太多的编译器支持。虽然很容易介绍 U.B.即使是简单的表达式,这对于多线程来说尤其如此。例如。访问不受保护的、非原子的共享变量是一个常见的错误——编译器丝毫没有抱怨。曾经,U.B.引入后,您不能对行为做出任何期望。甚至崩溃(以及当它发生时)也不被允许。我看到的唯一机会:“分而治之”并仔细观察您的代码(“代码凝视”)。 (在我的情况下,这是唯一有效的方法。) 【参考方案1】:

我在thread_entry上设置断点,观察到:

...
[Thread 0x7ffff7477700 (LWP 203995) exited]
parentId: 1 
OST 1 Hello A0 on CPU 2 
current_thread_num 0 next_thread_num 1
After Thread Exit 
After changeOSThread
OST 1 Hello C1 on CPU 2 ---------------
Before changeOSThread
**************** In changeOSThread **************
current_thread_num 1 next_thread_num 2

Thread 3 "a.out" hit Breakpoint 1, thread_entry (fn=0x0, arg=0x0) at userThread2.cpp:243
243     fn(arg) ;
(gdb) bt 
#0  thread_entry (fn=0x0, arg=0x0) at userThread2.cpp:243
#1  0x000055555555c181 in start_thread () at context.s:57
#2  0x0000000000000000 in ?? ()

结论:

    GDB 正在为您提供正确的崩溃堆栈跟踪。 您确实实际上用fn==0 调用thread_entry,这当然会立即崩溃。 有些事情正在发生,因为这种情况并非每次都会发生。

即使它被调用,Spawn() 也应该调用 start_thread,因为它是唯一调用 start_thread 的函数

我观察到以下对strart_thread 的“呼叫”:

Thread 2 "a.out" hit Breakpoint 1, start_thread () at context.s:53
53    push    %rbp
(gdb) bt 
#0  start_thread () at context.s:53
#1  0x0000555555555e4f in changeOSThread (parentId=<error reading variable>) at t.cc:196
#2  0x0000000000000000 in ?? ()

所以我认为你打电话给start_thread的心智模型是错误的。

这对我来说有点太多代码了。如果您需要更多帮助,请将测试用例减少到最低限度。

【讨论】:

“我认为你调用 start_thread 的心理模型是错误的”你是对的。 Spawn不调用start_thread,spawn通过参数将start_thread的地址压入栈中,当Spawn结束时调用。 “发生了一些不公平的事情”我如何检测竞争条件或有关导致竞争的原因的详细信息?

以上是关于多线程程序中的分段错误和 gdb 回溯信息不完整的主要内容,如果未能解决你的问题,请参考以下文章

Linux coredump 回溯丢失帧

libcurl 中的分段错误,多线程

分析分段错误核心转储 (gdb)

警报处理程序中交换上下文后的分段错误

gdb:在多线程程序中调用函数而不进行线程

gdb多线程调试