Boost stacktrace
Документировано по адресу: https://www.boost.org/doc/libs/1_66_0/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.how_to_print_current_call_stack
Это самый удобный вариант, который я когда-либо видел, потому что он:
может на самом деле распечатать номера строк.
Однако он просто делает вызовы на addr2line
, что уродливо и может быть медленным, если вы берете слишком много трасс.
по умолчанию разбирается
Ускорение - только заголовок, поэтому скорее всего не нужно изменять систему сборки
main.cpp
#include <iostream>
#define BOOST_STACKTRACE_USE_ADDR2LINE
#include <boost/stacktrace.hpp>
void my_func_2(void) {
std::cout << boost::stacktrace::stacktrace() << std::endl;
}
void my_func_1(double f) {
my_func_2();
}
void my_func_1(int i) {
my_func_2();
}
int main() {
my_func_1(1); /* line 19 */
my_func_1(2.0); /* line 20 */
}
К сожалению, это, кажется, более новое дополнение, и пакет libboost-stacktrace-dev
отсутствует в Ubuntu 16.04, только 18.04:
sudo apt-get install libboost-stacktrace-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o main.out -std=c++11 \
-Wall -Wextra -pedantic-errors main.cpp -ldl
У нас естьдобавить -ldl
в конце, иначе сборка не удалась.
Затем:
./main.out
дает:
0# my_func_2() at /root/lkmc/main.cpp:7
1# my_func_1(int) at /root/lkmc/main.cpp:16
2# main at /root/lkmc/main.cpp:20
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./main.out
0# my_func_2() at /root/lkmc/main.cpp:7
1# my_func_1(double) at /root/lkmc/main.cpp:12
2# main at /root/lkmc/main.cpp:21
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./main.out
И с -O3
:
0# my_func_2() at /usr/include/boost/stacktrace/stacktrace.hpp:217
1# my_func_1(double) at /root/lkmc/main.cpp:11
2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
3# _start in ./main.out
0# my_func_2() at /usr/include/boost/stacktrace/stacktrace.hpp:217
1# main at /root/lkmc/main.cpp:21
2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
3# _start in ./main.out
Имейте в виду, что следы в целом непоправимо изуродованы оптимизацией.Оптимизация Tail Call является ярким примером этого: Что такое оптимизация Tail Call?
Вывод и дальнейшее объяснение в разделе «glibc backtrace» ниже, который аналогичен.
Протестировано на Ubuntu 18.04, GCC 7.3.0, boost 1.65.1.
glibc backtrace
Документировано по адресу: https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
main.c
#include <stdio.h>
#include <stdlib.h>
/* Paste this on the file you want to debug. */
#include <stdio.h>
#include <execinfo.h>
void print_trace(void) {
char **strings;
size_t i, size;
enum Constexpr { MAX_SIZE = 1024 };
void *array[MAX_SIZE];
size = backtrace(array, MAX_SIZE);
strings = backtrace_symbols(array, size);
for (i = 0; i < size; i++)
printf("%s\n", strings[i]);
puts("");
free(strings);
}
void my_func_3(void) {
print_trace();
}
void my_func_2(void) {
my_func_3();
}
void my_func_1(void) {
my_func_3();
}
int main(void) {
my_func_1(); /* line 33 */
my_func_2(); /* line 34 */
return 0;
}
Компиляция:
gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -rdynamic -std=c99 \
-Wall -Wextra -pedantic-errors main.c
-rdynamic
является ключевым обязательным параметром.
Run:
./main.out
Выходы:
./main.out(print_trace+0x2d) [0x400a3d]
./main.out(main+0x9) [0x4008f9]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f35a5aad830]
./main.out(_start+0x29) [0x400939]
./main.out(print_trace+0x2d) [0x400a3d]
./main.out(main+0xe) [0x4008fe]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f35a5aad830]
./main.out(_start+0x29) [0x400939]
Итак, мы сразу видим, что произошла оптимизация встраивания, и некоторые функции были потеряны из трассировки.
Если мы попытаемся получить адреса:
addr2line -e main.out 0x4008f9 0x4008fe
мы получаем:
/home/ciro/main.c:21
/home/ciro/main.c:36
, который полностью выключен.
Если мы сделаем то же самое с -O0
, ./main.out
даст правильный полный след:
./main.out(print_trace+0x2e) [0x4009a4]
./main.out(my_func_3+0x9) [0x400a50]
./main.out(my_func_1+0x9) [0x400a68]
./main.out(main+0x9) [0x400a74]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f4711677830]
./main.out(_start+0x29) [0x4008a9]
./main.out(print_trace+0x2e) [0x4009a4]
./main.out(my_func_3+0x9) [0x400a50]
./main.out(my_func_2+0x9) [0x400a5c]
./main.out(main+0xe) [0x400a79]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f4711677830]
./main.out(_start+0x29) [0x4008a9]
и затем:
addr2line -e main.out 0x400a74 0x400a79
дает:
/home/cirsan01/test/main.c:34
/home/cirsan01/test/main.c:35
так что линии отключены только на один, TODO, почему?Но это все еще может быть пригодно для использования.
Вывод: обратные трассировки могут только отлично отображаться с -O0
.С оптимизацией исходная обратная трассировка кардинально модифицируется в скомпилированном коде.
Протестировано в Ubuntu 16.04, GCC 6.4.0, libc 2.23.
glibc backtrace_symbols_fd
Этот помощник немного удобнее, чем backtrace_symbols
, и выдает в основном идентичный вывод:
/* Paste this on the file you want to debug. */
#include <execinfo.h>
#include <stdio.h>
#include <unistd.h>
void print_trace(void) {
size_t i, size;
enum Constexpr { MAX_SIZE = 1024 };
void *array[MAX_SIZE];
size = backtrace(array, MAX_SIZE);
backtrace_symbols_fd(array, size, STDOUT_FILENO);
puts("");
}
Протестировано в Ubuntu 16.04, GCC 6.4.0, libc 2.23.
libunwind
TODO имеет ли это какое-то преимущество перед glibc backtrace?Очень похожий вывод, также требующий изменения команды сборки, но менее широко доступный.
Код адаптирован из: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/
main.c
/* This must be on top. */
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
/* Paste this on the file you want to debug. */
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <stdio.h>
void print_trace() {
char sym[256];
unw_context_t context;
unw_cursor_t cursor;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
while (unw_step(&cursor) > 0) {
unw_word_t offset, pc;
unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (pc == 0) {
break;
}
printf("0x%lx:", pc);
if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
printf(" (%s+0x%lx)\n", sym, offset);
} else {
printf(" -- error: unable to obtain symbol name for this frame\n");
}
}
puts("");
}
void my_func_3(void) {
print_trace();
}
void my_func_2(void) {
my_func_3();
}
void my_func_1(void) {
my_func_3();
}
int main(void) {
my_func_1(); /* line 46 */
my_func_2(); /* line 47 */
return 0;
}
Скомпилируйте и запустите:
sudo apt-get install libunwind-dev
gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -std=c99 \
-Wall -Wextra -pedantic-errors main.c -lunwind
Либо #define _XOPEN_SOURCE 700
должен быть сверху, либо мы должны использовать -std=gnu99
:
Выполнить:
./main.out
Вывод:
0x4007db: (main+0xb)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)
0x4007e2: (main+0x12)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)
и:
addr2line -e main.out 0x4007db 0x4007e2
дает:
/home/ciro/main.c:34
/home/ciro/main.c:49
С -O0
:
0x4009cf: (my_func_3+0xe)
0x4009e7: (my_func_1+0x9)
0x4009f3: (main+0x9)
0x7f7b84ad7830: (__libc_start_main+0xf0)
0x4007d9: (_start+0x29)
0x4009cf: (my_func_3+0xe)
0x4009db: (my_func_2+0x9)
0x4009f8: (main+0xe)
0x7f7b84ad7830: (__libc_start_main+0xf0)
0x4007d9: (_start+0x29)
и:
addr2line -e main.out 0x4009f3 0x4009f8
дает:
/home/ciro/main.c:47
/home/ciro/main.c:48
Протестировано на Ubuntu 16.04, GCC 6.4.0, libunwind 1.1.
C ++ demangling
Можно сделать с помощью abi::__cxa_demangle
для glibc backtrace
и libunwind, см. Пример по адресу: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/
Ядро Linux
Как напечатать текущую трассировку стека потоков внутри ядра Linux?
См. Также