Процесс Coredumped, но не выглядит как недопустимая ссылка в многопоточной программе - PullRequest
1 голос
/ 24 марта 2012

Обратный след coredump:

#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, сцена может произойти. Я ожидаю, что всякий раз, когда встречается недопустимая ссылка, она должна немедленно сбросить, не пытаясь выполнить LINE 3, что делает его NON NULL. Это определенное поведение, потому что до сих пор у меня было около 12 coredumps одной и той же проблемы, и все они показывают ту же самую вещь.

Ответы [ 2 ]

1 голос
/ 24 марта 2012

Это - это ясно, что struct_fd->Hdr->info - это NULL, как уже ответил Пер Йоханссон.

Однако GDB считает, что это не так.Как это может быть?

Один из распространенных способов это происходит, когда

  1. вы меняете макет struct fdStruct, struct StHdr (или оба) и
  2. вы пренебрегаете восстановлением всех объектов, которые используют эти определения

Разборка показывает, что 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, новым значением.

I просто заметил, что вы обновили вопрос; -)

Я ожидаю, что всякий раз, когда встречается недопустимая ссылка, он должен немедленно выполнить дамп, не пытаясь выполнить LINE 3, что делает его NON NULL.

Ваше ожидание неверно: на любом современном процессоре у вас есть несколько ядер, и ваши потоки выполняют одновременно .То есть у вас есть этот код (время идет вдоль оси 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 даже не синхронизированы, они не обязательно идут в режиме блокировки.

Мораль истории: не получайте доступ к глобальным переменным из нескольких потоков без надлежащей блокировки.

0 голосов
/ 24 марта 2012

Часть строки C разборки не совпадает с той, что была в исходном коде. Но ясно

struct_fd->Hdr->info

- это NULL. У GDB не должно быть проблем с печатью, но иногда он запутывается, когда код компилируется с -O2 или выше.

...