2016-11-29 13 views
0

Возможно ли установить точку наблюдения на локальном хранилище данных pthread с помощью GDB? У меня есть программа, которая работает:установить точку наблюдения в потоке pthread-local в gdb

struct stored_type *res = pthread_getspecific(tls_key); 

... и после нескольких тысяч звонков он возвращает 0 вместо действительного указателя. Мне очень хотелось бы выяснить, что задает значение 0. Я попытался установить точки останова на pthread_setspecific и pthread_delete_key (единственное, что я мог подумать, что разумно заставит ключ изменить значение), и эти точки останова не получат хит, поэтому я думаю, что происходит какое-то переполнение.

Я использую Linux x86_64 с glibc 2.23.

+0

Да, это возможно. Точные детали зависят от платформы, хотя вы сохранили тайну. Это Linux/x86_64/GLIBC или что-то еще? –

+0

Я действительно, и я отредактировал вопрос, чтобы включить это. Благодаря! –

ответ

1

... единственное, что я мог думать, что достаточно привести бы ключ, чтобы изменить значение

Наиболее вероятные причины pthread_getspecific вернуться NULL:

  • вы в факт, выполняющийся в новом потоке, в котором pthread_setspecific не был вызван,
  • вы вызываете pthread_getspecific, а текущий поток находится в процессе уничтожается (т. pthread_exit находится где-то в стеке),
  • вы вызываете pthread_getspecific в обработчик сигнала (ни одна из функций pthread_* не является безопасным асинхронным сигналом).

Предполагая, что ни одна из приведенных выше причин не верна в вашем случае, на выставке.

Для начала нам нужен тестовый пример.

#include <assert.h> 
#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

pthread_key_t key; 

void *thrfn(void *p) { 
    int rc = pthread_setspecific(key, &p); 
    assert(rc == 0); 
    sleep(60); 
    rc = pthread_setspecific(key, (void*)0x112233); 
    assert(rc == 0); 
    return p; 
} 

int main() 
{ 
    pthread_t thr; 
    int rc = pthread_key_create(&key, NULL); 
    assert(rc == 0); 

    rc = pthread_create(&thr, NULL, thrfn, NULL); 
    assert(rc == 0); 

    sleep(90); 
    return 0; 
} 

gcc -g -pthread t.c 

gdb -q ./a.out 
(gdb) start 

Теперь это очень помогает иметь GLIBC, который скомпилирован с информацией об отладке. Большинство дистрибутивов предоставляют libc-dbg или аналогичный пакет, который обеспечивает это. Посмотрев на pthread_setspecific source, вы можете видеть, что внутри дескриптора потока (self) есть массив specific_1stblock, где пространство для первого PTHREAD_KEY_2NDLEVEL_SIZE == 32 ключевых слота предварительно выделено (32 отдельных ключа обычно более чем достаточно).

Значение, которое мы передаем, будет храниться в self->specific_1stblock[key].data, и это именно то место, где вы хотите установить точку наблюдения.

В нашей примерной программе key == 0 (так как это самый первый ключ). Собираем все вместе:

Starting program: /tmp/a.out 
[Thread debugging using libthread_db enabled] 
Using host libthread_db library "/lib64/libthread_db.so.1". 

Temporary breakpoint 1, main() at t.c:21 
21  int rc = pthread_key_create(&key, NULL); 
(gdb) b pthread_setspecific 
Breakpoint 2 at 0x7ffff7bc9460: file pthread_setspecific.c, line 28. 
(gdb) c 
Continuing. 
[New Thread 0x7ffff77f6700 (LWP 58683)] 
[Switching to Thread 0x7ffff77f6700 (LWP 58683)] 

Breakpoint 2, __GI___pthread_setspecific (key=0, value=0x7ffff77f5ef8) at pthread_setspecific.c:28 
28 pthread_setspecific.c: No such file or directory. 
(gdb) n 
35 in pthread_setspecific.c 
(gdb) n 
28 in pthread_setspecific.c 
(gdb) p self 
$1 = (struct pthread *) 0x7ffff77f6700 
(gdb) watch -l self.specific_1stblock[key].data 
Hardware watchpoint 3: -location self.specific_1stblock[key].data 
(gdb) c 
Continuing. 
Hardware watchpoint 3: -location self.specific_1stblock[key].data 

Old value = (void *) 0x0 
New value = (void *) 0x7ffff77f5ef8 
__GI___pthread_setspecific (key=<optimized out>, value=0x7ffff77f5ef8) at pthread_setspecific.c:89 
89 in pthread_setspecific.c 

Обратите внимание, что новое значение точно значение, которое мы перешли к pthread_setspecific.

(gdb) c 
Continuing. 

Breakpoint 2, __GI___pthread_setspecific (key=0, value=0x112233) at pthread_setspecific.c:28 
28 in pthread_setspecific.c 
(gdb) c 
Continuing. 
Hardware watchpoint 3: -location self.specific_1stblock[key].data 

Old value = (void *) 0x7ffff77f5ef8 
New value = (void *) 0x112233 
__GI___pthread_setspecific (key=<optimized out>, value=0x112233) at pthread_setspecific.c:89 
89 in pthread_setspecific.c 

Это наш второй pthread_setspecific вызов

(gdb) c 
Continuing. 
Hardware watchpoint 3: -location self.specific_1stblock[key].data 

Old value = (void *) 0x112233 
New value = (void *) 0x0 
__nptl_deallocate_tsd() at pthread_create.c:152 
152 pthread_create.c: No such file or directory. 

И это разрушение нить, которая освобождает сам дескриптор потока.

(gdb) c 
Continuing. 
[Thread 0x7ffff77f6700 (LWP 58683) exited] 
[Inferior 1 (process 58677) exited normally] 
+0

Это замечательный ответ. Спасибо огромное! –