Khusus Linux backtrace()
dan backtrace_symbols()
memungkinkan Anda membuat jejak panggilan program. Namun, itu hanya mencetak alamat fungsi, bukan nama mereka untuk program saya. Bagaimana saya bisa membuatnya mencetak nama fungsi juga? Saya sudah mencoba menyusun program dengan -g
juga -ggdb
. Kasus uji di bawah hanya mencetak ini:
LATAR BELAKANG ------------ ./a.out () [0x8048616] ./a.out () [0x8048623] /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413] ./a.out () [0x8048421] ----------------------
Saya ingin 2 item pertama juga menampilkan nama fungsi, foo
danmain
Kode:
#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
melakukan operasi yang sama sepertibacktrace_symbols()
, tetapi string yang dihasilkan segera ditulis ke deskriptor filefd
.Jawaban:
Simbol diambil dari tabel simbol dinamis; Anda memerlukan
-rdynamic
opsi untukgcc
, yang membuatnya meneruskan bendera ke linker yang memastikan bahwa semua simbol ditempatkan di tabel.(Lihat halaman Link Options dari manual GCC , dan / atau halaman Backtraces dari manual glibc .)
sumber
libunwind
yang disebutkan @Nemo, berfungsi untuk fungsi statis.ADD_COMPILE_OPTIONS(-rdynamic)
. Sebaliknya, itu perluADD_LINK_OPTIONS(-rdynamic)
atau setara untuk memiliki perilaku yang diinginkan.Gunakan perintah addr2line untuk memetakan alamat yang dapat dieksekusi ke nama file kode sumber + nomor baris. Berikan
-f
opsi untuk mendapatkan nama fungsi juga.Atau, coba libunwind .
sumber
addr2line
bekerja dengan andal bahkan untuk alamat di objek bersama (?) Tapi ya, pada platform modern Anda perlu mengetahui alamat pemuatan sebenarnya dari objek yang dapat direlokasi bahkan untuk melakukan operasi ini pada prinsipnya .Libbacktrace yang luar biasa oleh Ian Lance Taylor memecahkan masalah ini. Ini menangani pelepasan tumpukan dan mendukung simbol ELF biasa dan simbol debugging DWARF.
Libbacktrace tidak perlu mengekspor semua simbol, yang akan jelek, dan ASLR tidak merusaknya.
Libbacktrace awalnya adalah bagian dari distribusi GCC. Sekarang, versi mandiri dapat ditemukan di Github:
https://github.com/ianlancetaylor/libbacktrace
sumber
jawaban di atas memiliki bug jika ret == -1 dan errno adalah EINTER Anda harus mencoba lagi, tetapi tidak menghitung ret sebagai disalin (tidak akan membuat akun hanya untuk ini, jika Anda tidak suka sulit)
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; } }
sumber
Tingkatkan lacak balik
Sangat nyaman karena mencetak keduanya:
otomatis untukmu.
Ringkasan penggunaan:
#define BOOST_STACKTRACE_USE_ADDR2LINE #include <boost/stacktrace.hpp> std::cout << boost::stacktrace::stacktrace() << std::endl;
Saya telah memberikan contoh runnable minimal untuk itu dan banyak metode lain di: print call stack di C atau C ++
sumber