[Linux][C/C++] tid (thread id) を取得する / pthread_createをラップして子スレッドのtidの取得

デバッグ用途にて、pthread がどこのスレッドから作成されたかを調べたいケースがあったので、
tidの取得方法と、pthread_create にて作成された子スレッドのtidを取得する方法を調査した。
基本的に以下の方法にて LD_PRELOAD を利用することを前提にしている。

[LD_PRELOADでprintfを後から差し替える - Qiita](http://qiita.com/koara-local/items/d5205f94decade3ffbf1)

# gettidを使用する

gettid は Linux では syscall 経由でしか使用できない様子。以下のようにする。

```c
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>

pid_t gettid(void) {
    return syscall(SYS_gettid);
}
```

# pthread の子スレッドのtidを取得したい場合 (pthread_createのラップ)

pthread_t から直接子スレッドのtidを取れれば楽なのだけれども、直接は基本的には取れないようなので (glibcのstruct pthreadから定義を取ってくれば無理やりtidを取得することは一応可能のようだけども今回は使用しない)、`pthread_create` をラップして、スレッドの呼び出し対象を差し替えて、上に書いた `gettid()` にてtidを取得するようにした。

```c:フック実装例
extern "C" {
​
struct pthread_args {
	void* (*f)(void*);
	void* args;
};
​
void* thread_wrap(void* args) {
	// tid を出力
	printf("%s : tid => %d\n", __func__, gettid());
	// 退避した構造体からもとの関数を呼び出し
	auto p = (struct pthread_args*)args;
	auto ret = p->f(p->args);
	delete p;
	return ret;
}
​
void* thread_excute(void* args) {
	printf("%s : tid => %d\n", __func__, gettid());
	return NULL;
}
​
// pthread_createをラップ
int pthread_create(pthread_t *__newthread, const pthread_attr_t *__attr,
		void *(*__start_routine) (void *), void *__arg) {
	using pthread_create_t =
		int (*)(pthread_t *, const pthread_attr_t *, void *(*) (void *), void *);
	static thread_local pthread_create_t real_pthread_create = nullptr;
	if (!real_pthread_create) {
		// create cache
		real_pthread_create
			 = reinterpret_cast<pthread_create_t>(dlsym(RTLD_NEXT, __func__));
		assert(real_pthread_create);
	}
​
	// もともとの関数ポインタと引数を構造体に一旦退避
	struct pthread_args* p  = new(std::nothrow) struct pthread_args();
	p->f = __start_routine;
	p->args = __arg;

	// 差し替え用の関数を呼び出して、引数で退避用の構造体を​渡す
	auto ret = real_pthread_create(__newthread, __attr, &thread_wrap, (void*)p);
​
	return ret;
}

} // extern "C"
```

# 実行サンプル

```c:tid_test.cpp
#include <stdio.h>
#include <assert.h>
#include <string.h>
​
#include <pthread.h>
#include <unistd.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/syscall.h>
​
#include <new>
​
// hook
extern "C" {
​
pid_t gettid(void) {
	return syscall(SYS_gettid);
}
​
struct pthread_args {
	void* (*f)(void*);
	void* args;
};
​
void* thread_wrap(void* args) {
	printf("%s : tid => %d\n", __func__, gettid());
	auto p = (struct pthread_args*)args;
	auto ret = p->f(p->args);
	delete p;
	return ret;
}
​
void* thread_excute(void* args) {
	printf("%s : tid => %d\n", __func__, gettid());
	return NULL;
}
​
int pthread_create(pthread_t *__newthread, const pthread_attr_t *__attr,
		void *(*__start_routine) (void *), void *__arg) {
	using pthread_create_t =
		int (*)(pthread_t *, const pthread_attr_t *, void *(*) (void *), void *);
	static thread_local pthread_create_t real_pthread_create = nullptr;
	if (!real_pthread_create) {
		// create cache
		real_pthread_create
			 = reinterpret_cast<pthread_create_t>(dlsym(RTLD_NEXT, __func__));
		assert(real_pthread_create);
	}
​
	Dl_info info;
	dladdr(__builtin_return_address(0), &info);
	printf("%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(0));
	printf("%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
	printf("%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
	printf("%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
	printf("%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
	printf("%s : tid => %d\n", __func__, gettid());
​
	struct pthread_args* p  = new(std::nothrow) struct pthread_args();
	p->f = __start_routine;
	p->args = __arg;
​
	auto ret = real_pthread_create(__newthread, __attr, &thread_wrap, (void*)p);
​
	return ret;
}
​
}
​
// main
​
int main(int argc, char const* argv[])
{
	pthread_t thread_handler_;
​
	puts("begin : pthread_create");
	pthread_create(&thread_handler_, NULL, &thread_excute, NULL);
	puts("end   : pthread_create");
​
	pthread_join(thread_handler_, NULL);
	
	return 0;
}
```

```bash:ビルドと出力結果
$ g++ -std=c++11 tid_test.cpp -ldl -pthread -rdynamic
$ ./a.out
begin : pthread_create
pthread_create : __builtin_return_address => 0x400f40
pthread_create : Dl_info.dli_fname => ./a.out
pthread_create : Dl_info.dli_fbase => 0x400000
pthread_create : Dl_info.dli_sname => main
pthread_create : Dl_info.dli_saddr => 0x400f0c
pthread_create : tid => 4303
end   : pthread_create
thread_wrap : tid => 4304
thread_excute : tid => 4304
```

これで親と子の tid が取得できた。

# 関連
[LD_PRELOADでprintfを後から差し替える - Qiita](http://qiita.com/koara-local/items/d5205f94decade3ffbf1)
[LD_PRELOADで差し替えた関数から元の関数を呼び出す - Qiita](http://qiita.com/koara-local/items/221508d4691c4502adce)
[[Linux][C/C++]関数の復帰アドレス値と呼び出し元の関数名を取得する方法 - Qiita](http://qiita.com/koara-local/items/07a4f76b2d44ac719b65)

# 参考
[スレッドIDを取得する - 揮発性のメモ](http://d.hatena.ne.jp/iww/20101217/p1)