[Linux] [C / C ++] Get tid (thread id) / Wrap pthread_create to get tid of child thread

For debugging purposes, I wanted to find out from which thread the pthread was created, so We investigated how to get tid and how to get tid of child thread created by pthread_create. Basically, it is assumed that LD_PRELOAD is used by the following method.

Replace printf later with LD_PRELOAD --Qiita

use gettid

It seems that gettid can only be used via syscall on Linux. Do as follows.

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

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

If you want to get the tid of a pthread's child thread (wrapping pthread_create)

It would be easier if you could get the tid of the child thread directly from pthread_t, but basically it seems that you can't get it directly (it seems possible to get the tid by force if you get the definition from glibc's struct pthread). (Not used this time), wrapping pthread_create, replacing the thread call target, and getting the tid withgettid ()written above.

Hook mounting example


extern "C" {
​
struct pthread_args {
	void* (*f)(void*);
	void* args;
};
​
void* thread_wrap(void* args) {
	//Output tid
	printf("%s : tid => %d\n", __func__, gettid());
	//Call the original function from the saved structure
	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_Wrap 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);
	}
​
	//Save the original function pointer and arguments to the structure once
	struct pthread_args* p  = new(std::nothrow) struct pthread_args();
	p->f = __start_routine;
	p->args = __arg;

	//Call the replacement function and pass the save structure as an argument
	auto ret = real_pthread_create(__newthread, __attr, &thread_wrap, (void*)p);
​
	return ret;
}

} // extern "C"

Execution sample

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;
}

Build and output results


$ 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

Now we have the parent and child tid.

Relation

Replace printf later with LD_PRELOAD --Qiita Call the original function from the function replaced by LD_PRELOAD --Qiita [Linux] [C / C ++] How to get the return address value of a function and the function name of the caller --Qiita

reference

Get Thread ID-Volatile Memo

Recommended Posts

[Linux] [C / C ++] Get tid (thread id) / Wrap pthread_create to get tid of child thread
[Linux] [C / C ++] Summary of how to get pid, ppid, tid
[Linux] [C] Get CPU time per process / thread
[C language] [Linux] Get the value of environment variable
[Linux] [C / C ++] How to get the return address value of a function and the function name of the caller
Get thread ID with python
[Linux] [C / C ++] Operation check memo of fork, thread and thread local variables
[Linux] Command to get a list of commands executed in the past