Zusammenfassung der Backtrace-Erfassungsmethode für [Linux] [C / C ++]

Zusammenfassung der Backtrace-Erfassungsmethode zum Zeitpunkt des Debuggens Mit "LD_PRELOAD" können Sie die Rückverfolgung später an einer beliebigen Stelle einbetten, ohne das Ziel neu zu erstellen.

Verwenden Sie gdb

Ausgelassen, da es sich um die Verwendung von gdb handelt

Verwenden Sie libSegFault.so

Nur verfügbar, wenn SEGV im Programm auftritt.

wie benutzt man


$ LD_PRELOAD=/lib/libSegFault.so hoge

# or

$ catchsegv hoge

Verwenden Sie __builtin_return_address ()

Sie können die Adresse des Anrufers mit __builtin_return_address () ermitteln. Durch Erhöhen der Anzahl der Argumente können Sie auf den obigen Aufrufer verweisen.

test.c


#include <stdio.h>
#define  __USE_GNU
#include <dlfcn.h>

void hoge() {
	Dl_info info;
	dladdr(__builtin_return_address(0), &info);
	fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(0));
	fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
	fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
	fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
	fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);

	dladdr(__builtin_return_address(1), &info);
	fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(1));
	fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
	fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
	fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
	fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);
}

int main(int argc, char const* argv[])
{
	hoge();

	Dl_info info;
	dladdr(__builtin_return_address(0), &info);
	fprintf(stderr, "%s : __builtin_return_address => %p\n", __func__, __builtin_return_address(0));
	fprintf(stderr, "%s : Dl_info.dli_fname => %s\n", __func__, info.dli_fname);
	fprintf(stderr, "%s : Dl_info.dli_fbase => %p\n", __func__, info.dli_fbase);
	fprintf(stderr, "%s : Dl_info.dli_sname => %s\n", __func__, info.dli_sname);
	fprintf(stderr, "%s : Dl_info.dli_saddr => %p\n", __func__, info.dli_saddr);

	return 0;
}

Ergebnisse erstellen und ausführen


$ gcc test.c -ldl -rdynamic && ./a.out
hoge : __builtin_return_address => 0x4007d7
hoge : Dl_info.dli_fname => ./a.out
hoge : Dl_info.dli_fbase => 0x400000
hoge : Dl_info.dli_sname => (null)
hoge : Dl_info.dli_saddr => (nil)
hoge : __builtin_return_address => 0x7f8889a6c76d
hoge : Dl_info.dli_fname => /lib/x86_64-linux-gnu/libc.so.6
hoge : Dl_info.dli_fbase => 0x7f8889a4b000
hoge : Dl_info.dli_sname => __libc_start_main
hoge : Dl_info.dli_saddr => 0x7f8889a6c680
main : __builtin_return_address => 0x7f8889a6c76d
main : Dl_info.dli_fname => /lib/x86_64-linux-gnu/libc.so.6
main : Dl_info.dli_fbase => 0x7f8889a4b000
main : Dl_info.dli_sname => __libc_start_main
main : Dl_info.dli_saddr => 0x7f8889a6c680

Verwandte Themen: [Linux] [C / C ++] So ermitteln Sie den Wert der Rücksprungadresse einer Funktion und den Funktionsnamen des Aufrufers --Qiita

Wenn auf einen nicht vorhandenen Stapelrahmen verwiesen wird (SEGV tritt auf)

Beachten Sie, dass bei dieser Methode SEGV auftritt, wenn Sie auf eine Adresse zugreifen, die nicht vorhanden ist, wenn Sie die Anzahl erhöhen und zurückgehen.

Wenn Sie auf einen Stapelrahmen verweisen, der nicht vorhanden ist(SEGV tritt auf)


#include <stdio.h>

int main(int argc, char const* argv[])
{
	printf("%p\n", __builtin_return_address(0));
	printf("%p\n", __builtin_return_address(1));
	return 0;
}

Ergebnisse erstellen und ausführen


$ gcc test.c && ./a.out
0x7f6c9450076d
Segmentation fault (core dumped)

Verwenden Sie glibc backtrace

Ein Beispiel für die Verwendung von glibcs backtrace () und backtrace_symbols () zur Ausgabe eines Backtraces.

test.cpp


// backtrace, backtrace_symbols
#include <execinfo.h>

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>

using RetVec = std::vector<std::string>;

RetVec my_backtrace() {
	auto trace_size = 10;
	void* trace[trace_size];

	auto size = backtrace(trace, trace_size);

	auto symbols = backtrace_symbols(trace, size);

	RetVec result(symbols, symbols + size);

	free(symbols);

	return result;
}

RetVec hoge2() {
	return my_backtrace();
}

RetVec hoge1() {
	return hoge2();
}

int main(int argc, char const* argv[])
{
	auto vec = hoge1();

	int count = vec.size();
	for (auto v : vec) {
		count--;
		fprintf(stderr, "%s : backtrace [%d] %s\n",
				__func__,
				count,
				v.c_str()
			);
	}

	return 0;
}

Ergebnisse erstellen und ausführen


$ g++ -g -std=c++11 -o test test.cpp -rdynamic
$ ./test
main : backtrace [5] ./test(_Z12my_backtracev+0x97) [0x4021ff]
main : backtrace [4] ./test(_Z5hoge2v+0x18) [0x4022ac]
main : backtrace [3] ./test(_Z5hoge1v+0x18) [0x4022ca]
main : backtrace [2] ./test(main+0x1c) [0x4022ec]
main : backtrace [1] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7fc27153076d]
main : backtrace [0] ./test() [0x402079]

Verwenden Sie libunwind

Sie können Backtrace auch mit libunwind abrufen. (Libunwind ist eine Bibliothek, die Anrufketten steuert, siehe "BINARY HACKS # 73") Es gab einen Fall, in dem backtrace () auf dem ARM-Board nicht gut funktionierte, also habe ich es auch hier implementiert.

python


// backtrace, backtrace_symbols
#include <execinfo.h>

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>

#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <dlfcn.h>

#define UNW_LOCAL_ONLY
#include <libunwind.h>

void my_backtrace() {
	unw_cursor_t cursor;
	unw_context_t context;

	unw_getcontext(&context);
	unw_init_local(&cursor, &context);

	auto count = 0;

	do {
		unw_word_t  offset, pc;
		char        fname[64];

		unw_get_reg(&cursor, UNW_REG_IP, &pc);

		fname[0] = '\0';
		(void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);

		Dl_info info;
		dladdr((void*)pc, &info);

		fprintf(stderr, "%s : backtrace [%d] %s(%s+0x%lx) [%p]\n",
				__func__,
				count,
				info.dli_fname,
				fname,
				offset,
				(void*)pc
			   );

		count++;

	} while (unw_step(&cursor) > 0);
}

void hoge2() {
	my_backtrace();
}

void hoge1() {
	hoge2();
}

int main(int argc, char const* argv[])
{
	hoge1();
	return 0;
}

Ergebnisse erstellen und ausführen


$ g++ -g -std=c++11 -o test test.cpp -rdynamic -lunwind -ldl
$ ./test
my_backtrace : backtrace [0] ./test(_Z12my_backtracev+0x29) [0x400c81]
my_backtrace : backtrace [1] ./test(_Z5hoge2v+0x9) [0x400d92]
my_backtrace : backtrace [2] ./test(_Z5hoge1v+0x9) [0x400d9d]
my_backtrace : backtrace [3] ./test(main+0x14) [0x400db3]
my_backtrace : backtrace [4] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x7f12a138d76d]
my_backtrace : backtrace [5] ./test(_start+0x29) [0x400b69]

Bücher

Binary Hacks enthält viele Erklärungen für die obigen Beispiele.

Referenz

backtrace() The GNU C Library: Backtraces Return Address - Using the GNU Compiler Collection (GCC) [Morgen ist immer frisch ohne Fehler. @ Memorandum-Backtrace in C-Sprache (Stack-Trace)] (http://www35.atwiki.jp/futoyama/pages/101.html) Rückverfolgung in C ++ anzeigen - Tage der Memos (15.10.2010) How to generate a stacktrace when my gcc C++ app crashes - Stack Overflow

libunwind libunwind documentation c - GCC return address of calling function in ARM architecture - Stack Overflow Coding Relic: Pre-mortem Backtracing Backtrace mit ptrace in der ARM Linux-Umgebung --crimsonwoods blog

Recommended Posts

Zusammenfassung der Backtrace-Erfassungsmethode für [Linux] [C / C ++]
Linux Zusammenfassung
Linux-Befehlsübersicht
[Linux] Grundlegende Befehlsübersicht
Zusammenfassung der Testmethode
[Linux] [C / C ++] Zusammenfassung, wie man pid, ppid, tid bekommt
Zusammenfassung der Kaggle-Kernel-Methode [Bild]
Zusammenfassung der Abfrage der Python-Fitbit-Datenerfassung
Linux eine Zusammenfassung Tastenkombination
Zusammenfassung der Linux-Verteilungstypen
Zusammenfassung der Linux FD-Ereignis-API
Zusammenfassung der Unity IAP-Implementierungsmethode
[Linux] [Grundeinstellung] [Flattern] Zusammenfassung
Eine kurze Zusammenfassung von Linux
[Linux] Zusammenfassung der Benutzer- / Gruppenbefehle