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

时间:2012-03-24 10:21:08

标签: c multithreading assembly gdb coredump

coredump的Backtrace:

#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) */
   ...
}
------------------------------------------------------------

在上面的代码片段中,如果按顺序执行, LI 1, 3号线, 第2行, 场景可以发生。我期望的是每当遇到非法引用时,它应该立即转储而不试图执行LINE 3,这使得它变为NON NULL。 这是一个明确的行为,因为到目前为止我已经有大约12个相同问题的coredumps,所有这些都表现出完全相同的东西。

2 个答案:

答案 0 :(得分:1)

明确struct_fd->Hdr->infoNULL,正如Per Johansson已经回答的那样。

然而,GDB认为事实并非如此。怎么会这样?

这种情况的一种常见方式是

  1. 您更改了struct fdStructstruct StHdr(或两者)的布局, 和
  2. 您忽略了重建使用这些定义的所有对象
  3. 反汇编显示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 && 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之间的时钟甚至没有同步,它们不一定是锁定步骤。

    故事的道德:如果没有适当的锁定,请不要从多个线程访问全局变量。

答案 1 :(得分:0)

反汇编的C行部分与原始代码中的C行部分不匹配。但显然

struct_fd->Hdr->info

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