私の知る限り、 gdb には、 を介して保存されたデータへのポインターを取得するコマンドはありませんpthread_setspecific()
。ただし、メモリ アドレスを取得するためのオプションがいくつかあります。
- 各スレッドのバックトレースを調べ、各フレームをチェックして、結果
pthread_getspecific()
がまだスタック上にあるかどうかを確認します。
- 既存のコードを変更して、スレッド ID と の結果の両方をログに記録します
pthread_getspecific()
。
- スレッドの内部データ内でスレッド固有のデータ リストを見つけ、キーを使用してアドレスを含むレコードを見つけます。このアプローチは、pthread ライブラリの実装に依存します。したがって、使用されている pthread 実装の知識、または少しの忍耐力を伴うリバース エンジニアリングが必要です。
以下は、32 ビット マシンでの簡単なプログラムのデモです。
- g++ (GCC) 4.1.2 20080704 (レッドハット 4.1.2-48)
- libpthread-2.5.so
- GNU gdb Red Hat Linux (6.5-25.el5rh)
$cat example.cpp
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void* the_thread( void* );
void get_position();
struct position_t
{
int x;
int y;
};
namespace {
pthread_key_t position_key;
enum {
NUMBER_OF_THREADS = 2
};
} // unnamed
int main(int argc, char **argv)
{
int result = pthread_key_create( &position_key, NULL );
printf( "pthread_key_create -- key: %u, result: %i\n",
position_key, result );
pthread_t threads[NUMBER_OF_THREADS];
for (unsigned int i = 0; i < NUMBER_OF_THREADS; ++i )
{
// Allocate a position per threads.
position_t* position = new position_t();
// Set position values.
position->x = ( 1 + i ) * 11;
position->y = ( 1 + i ) * 13;
// Create the thread.
result = pthread_create( &threads[i], NULL, the_thread, position );
}
// Give time for threads to enter their forever loop.
sleep( 5 );
// Abort.
abort();
return 0;
}
void* the_thread( void* position )
{
int result = pthread_setspecific( position_key, position );
printf( "Thread: 0x%.8x, key: %u, value: 0x%.8x, result: %i\n",
pthread_self(), position_key, position, result );
get_position();
return 0;
}
void get_position()
{
position_t* position =
reinterpret_cast< position_t* >( pthread_getspecific( position_key ) );
printf( "Thread: 0x%.8x, key: %u, position: 0x%.8x, x: %i, y: %i\n",
pthread_self(), position_key, position, position->x, position->y );
// Wait forever.
while( true ) {};
}
$ g++ -g -lpthread example.cpp && gdb -q ./a.out
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r
Starting program: /tmp/a.out
[Thread debugging using libthread_db enabled]
[New Thread -1209043248 (LWP 17390)]
pthread_key_create -- key: 0, result: 0
[New Thread -1209046128 (LWP 17393)]
Thread: 0xb7ef6b90, key: 0, value: 0x09a35008, result: 0
Thread: 0xb7ef6b90, key: 0, position: 0x09a35008, x: 11, y: 13
[New Thread -1219535984 (LWP 17394)]
Thread: 0xb74f5b90, key: 0, value: 0x09a350b0, result: 0
Thread: 0xb74f5b90, key: 0, position: 0x09a350b0, x: 22, y: 26
Program received signal SIGABRT, Aborted.
[Switching to Thread -1209043248 (LWP 17390)]
0x00377402 in __kernel_vsyscall ()
スタック上にあるアドレスを使用する:
(gdb) info threads
3 Thread -1219535984 (LWP 17394) get_position () at example.cpp:71
2 Thread -1209046128 (LWP 17393) get_position () at example.cpp:71
* 1 Thread -1209043248 (LWP 17390) 0x00377402 in __kernel_vsyscall ()
(gdb) thread 3
[Switching to thread 3 (Thread -1219535984 (LWP 17394))]#0 get_position ()
at example.cpp:71
71 while( true ) {};
(gdb) list get_position
57
58 get_position();
59 return 0;
60 }
61
62 void get_position()
63 {
64 position_t* position =
65 reinterpret_cast< position_t* >( pthread_getspecific(
position_key ) );
66
(gdb) info locals
position = (position_t *) 0x9a350b0
(gdb) p position->x
$1 = 22
(gdb) p position->y
$2 = 26
stdout から出力されたアドレスの使用:
(gdb) p ((position_t*)(0x09a350b0))->x
$3 = 22
(gdb) p ((position_t*)(0x09a350b0))->y
$4 = 26
スレッドの内部データ内でスレッド固有のデータ リストを見つけます。
key
との値がある場合、このアプローチははるかに簡単ですpthread_t
。
必要に応じて、使用している pthread 実装の詳細を紹介します。
pthread
struct は、pthread によって内部的に使用されるスレッド記述子構造です。
pthread_create()
関連する構造体のアドレスを含む をpthread_t
返します。unsigned int
pthread
まず、pthread
スレッドの構造体を見つけます。
(gdb) info threads
* 3 Thread -1219535984 (LWP 17394) get_position () at example.cpp:71
2 Thread -1209046128 (LWP 17393) get_position () at example.cpp:71
1 Thread -1209043248 (LWP 17390) 0x00377402 in __kernel_vsyscall ()
(gdb) thread 1
[Switching to thread 1 (Thread -1209043248 (LWP 17390))]#0 0x00377402 in
__kernel_vsyscall ()
(gdb) bt
#0 0x00377402 in __kernel_vsyscall ()
#1 0x0080ec10 in raise () from /lib/libc.so.6
#2 0x00810521 in abort () from /lib/libc.so.6
#3 0x0804880f in main () at example.cpp:47
(gdb) frame 3
#3 0x0804880f in main () at example.cpp:47
47 abort();
(gdb) info locals
result = 0
threads = {3085921168, 3075431312}
(gdb) p/x threads[1]
$5 = 0xb74f5b90
多くのフィールドを無視すると、pthread
構造体の定義は次のようになります。
struct pthread
{
...
pid_t tid; // Thread ID (i.e. this thread descriptor).
pid_t pid; // Process ID.
...
struct pthread_key_data
{
uintptr_t seq;
void *data;
} specific_1stblock[PTHREAD_KEY_2NDLEVEL_SIZE];
struct pthread_key_data *specific[PTHREAD_KEY_1STLEVEL_SIZE];
...
};
pthread_key_data.seq
: かなり低く一致するはずのシーケンス番号が含まれています__pthread_keys[key].seq
。
pthread_key_data.data
: に提供された値が含まれますpthread_setspecific()
pthread.specific_1stblock
より多くのブロックを動的に割り当てようとする前に、スレッド固有のデータを格納するために使用されるブロックです。
pthread
スレッド固有のデータの 2 レベルの配列です。インデックス0
には のメモリ アドレスが含まれますpthread.specific_1stblock
。
PTHREAD_KEY_2NDLEVEL_SIZE
サイズは32です。
この定義は、メモリ内で何を探すべきかについてかなり良いアイデアを与えてくれます。
pthread
メモリアドレスの値を持つ整数( tid
) の後に、プロセス ID を持つ整数( ) が続きpid
ます。これは、調査中のメモリがpthread
構造体であるかどうかを示すのに役立ちます。
cancelhandling
とflags
フラグです。特定の値は重要ではありません。これらのフィールドは、それらの値がメモリ アドレスやカウンターを含むフィールドなど、他のフィールドと視覚的に区別できる可能性があるため、役立つ場合があります。
specific_1stblock
はサイズ 32 の配列です。構造体がゼロで初期化されている場合、コード例には 2 ワードのサイズのスレッド固有データが 1 つしかないため、62~ ワードの spthread
が繰り返されます。0
position_key
specific
メモリアドレスを含む配列です。構造体がゼロで初期化されている場合、 spthread
が繰り返される0
必要がありますが、最初の値は のメモリ アドレスである必要がありますspecific_1stblock
。
pthread
のメモリのチャンクを出力します。
(gdb) p/x *((int*)threads[1])@150
$6 = {0xb74f5b90, 0x9a350c8, 0xb74f5b90, 0x1, 0x377400, 0x7fb99100,
0xcb40329e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb7ef6bd0,
0x96b118, 0x43f2, 0x43ee, 0xb74f5be0, 0xffffffec, 0x0, 0x0, 0xb74f5470,
0x0, 0x1, 0x9a350b0, 0x0 <repeats 62 times>, 0xb74f5bf8,
0x0 <repeats 31 times>, 0x1000101, 0x0, 0x0, 0x0, 0xc2342345, 0xe0286,
0x0, 0x0, 0x0, 0x0, 0x0, 0x80486ca, 0x9a350b0, 0x0 <repeats 13 times>,
0xb6af5000, 0xa01000}
メモリ内のパターンを分析することにより、いくつかの単語が特定の pthread フィールドの適切な候補になります。
0xb74f5b90, 0x9a350c8, 0xb74f5b90 (pthread.tid), 0x1, 0x377400 (pthread.pid) ...
0x1, 0x9a350b0, 0x0 <repeats 62 times> (pthread.specific_1stblock) ...
0xb74f5bf8, 0x0 <repeats 31 times> (pthread.specific)
pthread.specific[0]
のアドレスが含まれているかどうかのチェックなど、いくつかの軽量の健全性チェックを実行できますpthread.specific_1stblock
。
(gdb) p/x *((int*)0xb74f5bf8)@64
$7 = {0x1, 0x9a350b0, 0x0 <repeats 62 times>} ## matches specific_1stblock
pthread.specific
が特定されたので、 からのワード オフセットをカウントしてそのメモリ アドレスを取得します&pthread
。この場合は 90 です。
(gdb) set $specific=(int*)threads[1] + 90
を介して最初と 2 番目のインデックスを計算しますposition_key
。
pthread_key_data
を見つけますposition_key
:
(gdb) set $level2=(int*)*($specific + $index1)
(gdb) p/x *($level2 + (2*$index2))@2
$8 = {0x1, 0x9a350b0}
したがって:
pthread_key_data.seq = 1
pthread_key_data.data = 0x9a350b0
最初の単語はseq
which と一致する必要がありpthread_key_struct[position_key].seq
ます。生メモリを扱うため、 sizeof を考慮して に__pthread_keys
キャストし、ポインタ演算を実行する必要があります。int*
pthread_key_struct
(gdb) p *(&((int*)&__pthread_keys)[2*position_key])@2
$9 = {1, 0}
したがって:
pthread_key_struct[position_key].seq = 1
pthread_key_struct[position_key].destr = NULL
seq
数字が一致しているので、すべてがうまく見えます。pthread_key_data.data
から返される値が含まれますpthread_getspecific( position_key )
。
(gdb) set $position=(position_t*)0x9a350b0
(gdb) p $position->x
$10 = 22
(gdb) p $position->y
$11 = 26
key
とのpthread_t
値を知らなくても、スレッド固有のデータを見つけることは技術的に可能です。
デストラクタ関数が に提供された場合pthread_key_create()
、そのメモリ アドレスは__pthread_keys
配列内に存在する可能性があります。メモリを調べて、オフセットを計算し、 sizeof で割りpthread_key_struct
ます。これにより、たまたまキーでもあるインデックスが得られるはずです。
void* destr_fn( void* );
pthread_key_create( key, destr_fn )
__pthread_keys[key].destr == destr_fn
pthread_t
が不明な場合は、スレッドのスタック上のレジスタ内に存在する可能性があります。これには、構造体を含むメモリ内のセクションを見つけるために、さまざまなメモリ アドレスを調べる必要がある場合がありますpthread
。
(gdb) info thread
3 Thread -1219535984 (LWP 17394) get_position () at example.cpp:71
2 Thread -1209046128 (LWP 17393) get_position () at example.cpp:71
* 1 Thread -1209043248 (LWP 17390) 0x00377402 in __kernel_vsyscall ()
(gdb) thread 3
[Switching to thread 3 (Thread -1219535984 (LWP 17394))]#0 g
get_position () at example.cpp:71
71 while( true ) {};
(gdb) bt
#0 get_position () at example.cpp:71
#1 0x0804871d in the_thread (position=0x9a350b0) at example.cpp:58
#2 0x0095c43b in start_thread () from /lib/libpthread.so.0
#3 0x008b3fde in clone () from /lib/libc.so.6
(gdb) frame 2
#2 0x0095c43b in start_thread () from /lib/libpthread.so.0
(gdb) info register
eax 0x3f 63
ecx 0xb74f52ac -1219538260
edx 0x0 0
ebx 0x96aff4 9875444
esp 0xb74f53c0 0xb74f53c0
ebp 0xb74f54a8 0xb74f54a8
esi 0x0 0
edi 0xb74f5b90 -1219535984
eip 0x95c43b 0x95c43b <start_thread+203>
eflags 0x200286 [ PF SF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
この例では、edi
レジスタにはpthread
構造体のアドレスが含まれています。
参照: descr.h、pthread_key_create.c、pthread_setspecific.c、pthreadP.h、 internaltypes.h