2
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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

LD_PRELOADでprintfを後から差し替える - Qiita

gettidを使用する

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

#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を取得するようにした。

フック実装例
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"

実行サンプル

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;
}
ビルドと出力結果
$ 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
LD_PRELOADで差し替えた関数から元の関数を呼び出す - Qiita
[Linux][C/C++]関数の復帰アドレス値と呼び出し元の関数名を取得する方法 - Qiita

参考

スレッドIDを取得する - 揮発性のメモ

2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?