核心转储的回溯:
#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) */
...
}
------------------------------------------------------------
在上面的代码片段中,如果按顺序执行, 1号线, 第 3 行, 2号线, 场景可以发生。我期望的是,无论何时遇到非法引用,它都应该立即转储,而不是尝试执行使其成为 NON NULL 的 LINE 3。 这是一个明确的行为,因为到目前为止,我有大约 12 个相同问题的核心转储,所有这些都显示完全相同的东西。
最佳答案
很明显,struct_fd->Hdr->info
是 NULL
,正如 Per Johansson 已经回答的那样。
但是,GDB认为不是。怎么可能?
发生这种情况的一种常见方式是,当
- 您更改了
struct fdStruct
、struct StHdr
(或两者)的布局, 和 - 你忽略了重建所有使用这些定义的对象
反汇编显示 offsetof(struct fdStruct, Hdr) == 0x1e8
和 offsetof(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
我打赌它会打印出 0x1e8
和 0x158
以外的东西。
如果是这种情况,make clean && 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 的值。
我刚注意到您对问题的更新;-)
What I expect is whenever an illegal reference is encountered it should dump immediately without trying to execute LINE 3 which makes it 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 之间的时钟甚至不同步,它们不一定同步。
故事的寓意:不要在没有适当锁定的情况下从多个线程访问全局变量。
关于c - Process Coredumped 但看起来不像多线程程序中的非法引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9850920/