处理 Coredumped 但看起来不像多线程程序中的非法引用

Posted

技术标签:

【中文标题】处理 Coredumped 但看起来不像多线程程序中的非法引用【英文标题】:Process Coredumped but does not look like illegal reference in a multithreaded program 【发布时间】:2012-03-24 10:21:07 【问题描述】:

核心转储的回溯:

#0  0x0000000000416228 in add_to_epoll (struct_fd=0x18d32760, lno=7901) at lbi.c:7092
#1  0x0000000000418b54 in connect_fc (struct_fd=0x18d32760, type=2) at lbi.c:7901
#2  0x0000000000418660 in poll_fc (arg=0x0) at lbi.c:7686
#3  0x00000030926064a7 in start_thread () from /lib64/libpthread.so.0
#4  0x0000003091ed3c2d in clone () from /lib64/libc.so.6

代码片段:

#define unExp(x) __builtin_expect((x),0)
... 
7087 int add_to_epoll( struct fdStruct * struct_fd, int lno)
7088 
7089    struct epoll_event ev;
7090    ev.events = EPOLLIN | EPOLLET | EPOLLPRI | EPOLLERR ;
7091    ev.data.fd = fd_st->fd;
7092    if (unExp(epoll_ctl(struct_fd->Hdr->info->epollfd, EPOLL_CTL_ADD,         struct_fd->fd,&ev) == -1))
7093    
7094        perror("client FD  ADD to epoll error:");
7095        return -1;
7096    
7097    else
7098    
            ...
7109    
7110    return 1;
7111 

反汇编违规行。我不擅长解释汇编代码,但已尽力:

        if (unExp(epoll_ctl(struct_fd->Hdr->info->epollfd, EPOLL_CTL_ADD, stuct_fd->fd,&ev) == -1))
  416210:       48 8b 45 d8             mov    0xffffffffffffffd8(%rbp),%rax // Storing struct_fd->fd
  416214:       8b 10                   mov    (%rax),%edx                   //  to EDX
  416216:       48 8b 45 d8             mov    0xffffffffffffffd8(%rbp),%rax // Storing struct_fd->Hdr->info->epollfd
  41621a:       48 8b 80 e8 01 00 00    mov    0x1e8(%rax),%rax              // to EDI which failed 
  416221:       48 8b 80 58 01 00 00    mov    0x158(%rax),%rax              // while trying to offset members of the structure
  416228:       8b 78 5c                mov    0x5c(%rax),%edi               // <--- failed here since Reg AX is 0x0
  41622b:       48 8d 4d e0             lea    0xffffffffffffffe0(%rbp),%rcx
  41622f:       be 01 00 00 00          mov    $0x1,%esi
  416234:       e8 b7 e1 fe ff          callq  4043f0 <epoll_ctl@plt>
  416239:       83 f8 ff                cmp    $0xffffffffffffffff,%eax
  41623c:       0f 94 c0                sete   %al
  41623f:       0f b6 c0                movzbl %al,%eax
  416242:       48 85 c0                test   %rax,%rax
  416245:       74 5e                   je     4162a5 <add_to_epoll+0xc9>

打印寄存器和结构成员值:

(gdb) i r $rax
rax            0x0      0
(gdb) p struct_fd
$3 = (struct fdStruct *) 0x18d32760
(gdb) p struct_fd->Hdr
$4 = (StHdr *) 0x3b990f30
(gdb) p struct_fd->Hdr->info
$5 = (struct Info *) 0x3b95b410    // Strangely, this is NOT NULL. Inconsistent with assembly dump.
(gdb) p ev
$6 = events = 2147483659, data = ptr = 0x573dc648000003d6, fd = 982, u32 = 982, u64= 6286398667419026390

如果我的反汇编解释正常,请告诉我。如果是,想了解为什么 gdb 在打印结构成员时不显示 NULL。

或者如果分析不完善想知道coredump的实际原因。如果您需要更多信息,请告诉我。

谢谢

----后面的部分已添加----

代理是一个多线程程序。做更多的挖掘才知道,当问题发生时,以下两个线程是并行运行的。当我避免这两个函数并行运行时,问题永远不会发生。但是,问题是我无法解释这种行为是如何导致最初的问题场景的:

Thread 1: 
------------------------------------------------------------
int new_connection() 
   ...
   struct_fd->Hdr->info=NULL; /* (line 1)  */
   ...
   <some code>
   ...
   struct_fd->Hdr->info=Golbal_InFo_Ptr; /* (line 2) */  // This is a malloced memory, once allocated never freed
   ...
   ...

------------------------------------------------------------

Thread 2 executing add_to_epoll():
------------------------------------------------------------
int add_to_epoll( struct fdStruct * struct_fd, int lno)

   ...
   if (unExp(epoll_ctl(struct_fd->Hdr->info->epollfd,...)  /* (line 3) */
   ...

------------------------------------------------------------

在上面的sn-ps中如果按顺序执行, 第 1 行, 3号线, 2号线, 可能发生的场景。我期望的是,每当遇到非法引用时,它应该立即转储而不尝试执行 LINE 3,这使它成为 NON NULL。 这是一个明确的行为,因为到目前为止,我已经收到了大约 12 个相同问题的核心转储,都显示了完全相同的内容。

【问题讨论】:

赞成,因为我不知道我在谷歌上搜索了多少次错误输出并没有找到任何结果。这可能对某人非常有用。 【参考方案1】:

很明显,struct_fd-&gt;Hdr-&gt;infoNULL,正如 Per Johansson 已经回答的那样。

但是,GDB 认为不是。怎么可能?

发生这种情况的一种常见方式是,当

    您更改了struct fdStructstruct StHdr(或两者)的布局, 和 您忽略了重建所有使用这些定义的对象

反汇编显示offsetof(struct fdStruct, Hdr) == 0x1e8offsetof(struct StHdr, info) == 0x158。查看 GDB 打印的以下内容:

(gdb) print/x (char*)&struct_fd->Hdr - (char*)struct_fd
(gdb) print/x (char*)&struct_fd->Hdr->info - (char*)struct_fd->Hdr

我打赌它会打印除0x1e80x158 之外的其他内容。

如果是这种情况,make clean &amp;&amp; make 可能会解决问题。

更新:

(gdb) print/x (char*)&struct_fd->Hdr - (char*)struct_fd
$1 = 0x1e8
(gdb) print/x (char*)&struct_fd->Hdr->info - (char*)struct_fd->Hdr
$3 = 0x158

这证明了 GDB 关于对象在内存中的布局方式的想法与编译后的代码相匹配。

我们仍然不知道 GDB 对struct_fd 值的想法是否符合现实。这些命令打印什么?

(gdb) print struct_fd
(gdb) x/gx $rbp-40

它们应该产生相同的值 (0x18d32760)。假设他们这样做了,我能想到的唯一其他解释是您有多个线程访问 struct_fd,而另一个线程用新值覆盖了曾经为 NULL 的值。

刚刚注意到您对问题的更新;-)

我期望的是,每当遇到非法引用时,它应该立即转储,而不是尝试执行 LINE 3,这使它成为 NON NULL。

您的期望不正确:在任何现代 CPU 上,您都有多个内核,并且您的线程正在同时执行。也就是说,你有这个代码(时间沿 Y 轴下降):

char *p;  // global


Time     CPU0                  CPU1
0        p = NULL
1        if (*p)               p = malloc(1)
2                              *p = 'a';
...

在 T1,CPU0 陷入操作系统,但 CPU1 继续。最终,操作系统处理硬件陷阱,并转储当时的内存状态。在 CPU1 上,数百条指令可能在 T1 之后执行。 CPU0 和 CPU1 之间的时钟甚至不同步,它们不一定同步。

故事的寓意:不要在没有适当锁定的情况下从多个线程访问全局变量。

【讨论】:

感谢您的回复,实际上已经尝试过了。 (gdb) print/x (char*)&struct_fd->Hdr - (char*)struct_fd $1 = 0x1e8 (gdb) print/x (char*)&struct_fd->Hdr->info - (char*)struct_fd->Hdr $3 = 0x158 尝试 'make clean' 和 make too 嗨@Employed Russian,已向问题添加了更多信息。 @Saj_Rk 我几乎同时更新了答案。不清楚你是否阅读了更新。 您的更新没有立即反映。感谢您的详细解释。在我的情况下,这两个线程实际上绑定到不同的 CPU。您说“在 T1,CPU0 陷入操作系统”,您的意思是 CPU1,因为 T1 的 CPU0 只是取消引用 p ? ......打印您要求的值...... (gdb) p struct_fd $1 = (struct fdStruct *) 0x18d32760 (gdb) x/gx $rbp-40 0x573dc5f8: 0x0000000018d32760 .... 它们匹配。跨度> @Saj_Rk “你是说 CPU1 吗?”不,我确实是指 CPU0。 “简单地取消引用 p ...” 由于 p 是 NULL,取消引用它将导致 Linux(和大多数其他操作系统)上的硬件陷阱。【参考方案2】:

反汇编的C行部分与原代码中的不匹配。但是很明显

struct_fd->Hdr->info

NULL。 gdb 打印它应该没有问题,但是当代码使用 -O2 或更高版本编译时,它有时会感到困惑。

【讨论】:

您好,感谢您的回复。我认为不匹配的部分是反汇编中的函数名称,这是我为简单起见重命名函数的失败尝试;-)。但是,我确信二进制文件没有经过任何优化并且 -g 已打开。还是gdb会糊涂?? ....顺便说一句,想了解为什么这个问题被否决了,以便我可以准确地构建它们。

以上是关于处理 Coredumped 但看起来不像多线程程序中的非法引用的主要内容,如果未能解决你的问题,请参考以下文章

线程的停止与暂停

简单线程池实现 (C版本)

Laravel 5.1 中 3 个模型之间的关系(“像多对多通过”)

node特点

Unity协程和线程的区别

Unity3d 和其他看起来不像传统应用程序的软件使用的 GUI 库是啥?