Specyficzne dla Linuksa backtrace()
i backtrace_symbols()
pozwala na utworzenie śladu wywołania programu. Jednak dla mojego programu wypisuje tylko adresy funkcji, a nie ich nazwy. Jak mogę sprawić, by wyświetlały również nazwy funkcji? Próbowałem skompilować program -g
również z -ggdb
. Poniższy przypadek testowy po prostu drukuje to:
BACKTRACE ------------ ./a.out () [0x8048616] ./a.out () [0x8048623] /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413] ./a.out () [0x8048421] ----------------------
Chciałbym, aby pierwsze 2 elementy zawierały również nazwy funkcji, foo
amain
Kod:
#include <execinfo.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
static void full_write(int fd, const char *buf, size_t len)
{
while (len > 0) {
ssize_t ret = write(fd, buf, len);
if ((ret == -1) && (errno != EINTR))
break;
buf += (size_t) ret;
len -= (size_t) ret;
}
}
void print_backtrace(void)
{
static const char start[] = "BACKTRACE ------------\n";
static const char end[] = "----------------------\n";
void *bt[1024];
int bt_size;
char **bt_syms;
int i;
bt_size = backtrace(bt, 1024);
bt_syms = backtrace_symbols(bt, bt_size);
full_write(STDERR_FILENO, start, strlen(start));
for (i = 1; i < bt_size; i++) {
size_t len = strlen(bt_syms[i]);
full_write(STDERR_FILENO, bt_syms[i], len);
full_write(STDERR_FILENO, "\n", 1);
}
full_write(STDERR_FILENO, end, strlen(end));
free(bt_syms);
}
void foo()
{
print_backtrace();
}
int main()
{
foo();
return 0;
}
backtrace_symbols_fd
wykonuje tę samą operację, cobacktrace_symbols()
, ale wynikowe ciągi są natychmiast zapisywane do deskryptora plikufd
.Odpowiedzi:
Symbole są pobierane z dynamicznej tablicy symboli; potrzebujesz
-rdynamic
opcjigcc
, która sprawia, że przekazuje flagę do konsolidatora, która zapewnia, że wszystkie symbole są umieszczone w tabeli.(Zobacz stronę Opcje łącza w podręczniku GCC i / lub stronę Backtraces w podręczniku glibc .)
źródło
libunwind
o którym wspomina @Nemo, działa dla funkcji statycznych.ADD_COMPILE_OPTIONS(-rdynamic)
. Zamiast tego musi byćADD_LINK_OPTIONS(-rdynamic)
lub równoważne, aby mieć pożądane zachowanie.Użyj polecenia addr2line, aby odwzorować adresy wykonywalne na kod źródłowy nazwa pliku + numer linii. Podaj również
-f
opcję pobierania nazw funkcji.Alternatywnie wypróbuj libunwind .
źródło
addr2line
działały niezawodnie nawet w przypadku adresów w obiektach współdzielonych (?) Ale tak, na nowoczesnych platformach musiałbyś znać rzeczywisty adres ładowania przenoszonego obiektu, nawet aby wykonać tę operację w zasadzie .Doskonały Libbacktrace autorstwa Iana Lance'a Taylora rozwiązuje ten problem. Obsługuje rozwijanie stosu i obsługuje zarówno zwykłe symbole ELF, jak i symbole debugowania DWARF.
Libbacktrace nie wymaga eksportowania wszystkich symboli, co byłoby brzydkie, a ASLR tego nie łamie.
Libbacktrace był pierwotnie częścią dystrybucji GCC. Teraz samodzielną wersję można znaleźć na Github:
https://github.com/ianlancetaylor/libbacktrace
źródło
odpowiedź na górze ma błąd, jeśli ret == -1 i errno jest EINTER powinieneś spróbować ponownie, ale nie licz ret jako skopiowany (nie zakładaj konta tylko po to, jeśli nie lubisz tego trudne)
static void full_write(int fd, const char *buf, size_t len) { while (len > 0) { ssize_t ret = write(fd, buf, len); if ((ret == -1) { if (errno != EINTR)) break; //else continue; } buf += (size_t) ret; len -= (size_t) ret; } }
źródło
Wzmocnij ślad
Bardzo wygodne, ponieważ drukuje zarówno:
automatycznie dla Ciebie.
Podsumowanie użytkowania:
#define BOOST_STACKTRACE_USE_ADDR2LINE #include <boost/stacktrace.hpp> std::cout << boost::stacktrace::stacktrace() << std::endl;
Podałem minimalny przykład, który można uruchomić, i wiele innych metod w: print stos wywołań w C lub C ++
źródło