[Linux] [C / C ++] Get tid (Thread-ID) / Wrap pthread_create, um tid des untergeordneten Threads abzurufen

Für Debugging-Zwecke gab es einen Fall, in dem ich herausfinden wollte, aus welchem Thread der Pthread erstellt wurde. Wir haben untersucht, wie man tid bekommt und wie man tid von untergeordnetem Thread bekommt, der von pthread_create erstellt wurde. Grundsätzlich wird davon ausgegangen, dass LD_PRELOAD von der folgenden Methode verwendet wird.

Ersetzen Sie printf später durch LD_PRELOAD --Qiita

benutze gettid

Es scheint, dass gettid nur über syscall unter Linux verwendet werden kann. Gehen Sie wie folgt vor.

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

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

Wenn Sie die Nachricht eines untergeordneten Threads von pthread abrufen möchten (umschließen von pthread_create)

Es wäre einfacher, wenn ich die Nachricht des untergeordneten Threads direkt von pthread_t abrufen könnte, aber im Grunde scheint es, dass ich sie nicht direkt abrufen kann (es scheint möglich, die Nachricht mit Gewalt zu erhalten, wenn ich die Definition von glibcs Struktur pthread erhalte). (Diesmal nicht verwendet), "pthread_create" umbrechen, das Aufrufziel des Threads ersetzen und die tid mit "gettid ()" abrufen, wie oben geschrieben.

Beispiel für die Hakenmontage


extern "C" {
​
struct pthread_args {
	void* (*f)(void*);
	void* args;
};
​
void* thread_wrap(void* args) {
	//Ausgabe tid
	printf("%s : tid => %d\n", __func__, gettid());
	//Rufen Sie die ursprüngliche Funktion aus der gespeicherten Struktur auf
	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 erstellen
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);
	}
​
	//Speichern Sie den ursprünglichen Funktionszeiger und die Argumente einmal in der Struktur
	struct pthread_args* p  = new(std::nothrow) struct pthread_args();
	p->f = __start_routine;
	p->args = __arg;

	//Rufen Sie die Ersetzungsfunktion auf und übergeben Sie die Sicherungsstruktur als Argument
	auto ret = real_pthread_create(__newthread, __attr, &thread_wrap, (void*)p);
​
	return ret;
}

} // extern "C"

Ausführungsbeispiel

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

Ergebnisse erstellen und ausgeben


$ 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

Jetzt haben wir die Nachricht für Eltern und Kind.

Beziehung

Ersetzen Sie printf später durch LD_PRELOAD --Qiita Rufen Sie die ursprüngliche Funktion von der durch LD_PRELOAD --Qiita ersetzten Funktion auf [Linux] [C / C ++] So ermitteln Sie den Wert der Rücksprungadresse einer Funktion und den Funktionsnamen des Aufrufers --Qiita

Referenz

Thread ID-Volatile Note abrufen

Recommended Posts

[Linux] [C / C ++] Get tid (Thread-ID) / Wrap pthread_create, um tid des untergeordneten Threads abzurufen
[Linux] [C / C ++] Zusammenfassung, wie man pid, ppid, tid bekommt
[Linux] [C] CPU-Zeit pro Prozess / Thread abrufen
[C-Sprache] [Linux] Ruft den Wert der Umgebungsvariablen ab
[Linux] [C / C ++] So ermitteln Sie den Wert der Rücksprungadresse einer Funktion und den Funktionsnamen des Aufrufers
Holen Sie sich die Thread-ID mit Python
[Linux] [C / C ++] Operation prüft Memo von Fork, Thread und Thread-lokalen Variablen
[Linux] Ein Befehl zum Abrufen einer Liste der in der Vergangenheit ausgeführten Befehle