Tăng stacktrace
Tài liệu tại: https://www.boost.org/doc/libs/1_66_0/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.how_to_print_current_call_stack
Đây là tùy chọn thuận tiện nhất mà tôi đã thấy cho đến nay, bởi vì nó:
thực sự có thể in ra số dòng.
Tuy nhiên , nó chỉ thực hiện các cuộc gọiaddr2line
, điều này xấu và có thể chậm nếu bạn đang theo dõi quá nhiều dấu vết.
hình tam giác theo mặc định
Boost chỉ là tiêu đề, vì vậy rất có thể không cần sửa đổi hệ thống xây dựng của bạn
boost_stacktrace.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) {
(void)f;
my_func_2();
}
void my_func_1(int i) {
(void)i;
my_func_2();
}
int main(int argc, char **argv) {
long long unsigned int n;
if (argc > 1) {
n = strtoul(argv[1], NULL, 0);
} else {
n = 1;
}
for (long long unsigned int i = 0; i < n; ++i) {
my_func_1(1); // line 28
my_func_1(2.0); // line 29
}
}
Thật không may, nó có vẻ là một bổ sung gần đây hơn và gói libboost-stacktrace-dev
này không có trong Ubuntu 16.04, chỉ có 18.04:
sudo apt-get install libboost-stacktrace-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o boost_stacktrace.out -std=c++11 \
-Wall -Wextra -pedantic-errors boost_stacktrace.cpp -ldl
./boost_stacktrace.out
Chúng tôi phải thêm -ldl
vào cuối nếu không quá trình biên dịch không thành công.
Đầu ra:
0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::basic_stacktrace() at /usr/include/boost/stacktrace/stacktrace.hpp:129
1# my_func_1(int) at /home/ciro/test/boost_stacktrace.cpp:18
2# main at /home/ciro/test/boost_stacktrace.cpp:29 (discriminator 2)
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./boost_stacktrace.out
0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::basic_stacktrace() at /usr/include/boost/stacktrace/stacktrace.hpp:129
1# my_func_1(double) at /home/ciro/test/boost_stacktrace.cpp:13
2# main at /home/ciro/test/boost_stacktrace.cpp:27 (discriminator 2)
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./boost_stacktrace.out
Đầu ra và được giải thích thêm trong phần "glibc backtrace" bên dưới, tương tự.
Lưu ý cách my_func_1(int)
và my_func_1(float)
, những thứ bị sai lệch do quá tải chức năng , đã được gỡ rối một cách độc đáo cho chúng tôi.
Lưu ý rằng các int
cuộc gọi đầu tiên bị tắt bởi một dòng (28 thay vì 27 và cuộc gọi thứ hai bị tắt bởi hai dòng (27 thay vì 29). Nó được đề xuất trong các nhận xét rằng điều này là do địa chỉ hướng dẫn sau đang được xem xét, làm cho 27 trở thành 28, và 29 nhảy ra khỏi vòng lặp và trở thành 27.
Sau đó, chúng tôi quan sát thấy rằng với -O3
, đầu ra hoàn toàn bị cắt xén:
0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::size() const at /usr/include/boost/stacktrace/stacktrace.hpp:215
1# my_func_1(double) at /home/ciro/test/boost_stacktrace.cpp:12
2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
3# _start in ./boost_stacktrace.out
0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::size() const at /usr/include/boost/stacktrace/stacktrace.hpp:215
1# main at /home/ciro/test/boost_stacktrace.cpp:31
2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
3# _start in ./boost_stacktrace.out
Nói chung, các dấu hiệu tồn đọng bị cắt xén không thể sửa chữa được bằng cách tối ưu hóa. Tối ưu hóa cuộc gọi đuôi là một ví dụ đáng chú ý về điều đó: Tối ưu hóa cuộc gọi đuôi là gì?
Điểm chuẩn chạy trên -O3
:
time ./boost_stacktrace.out 1000 >/dev/null
Đầu ra:
real 0m43.573s
user 0m30.799s
sys 0m13.665s
Vì vậy, như mong đợi, chúng tôi thấy rằng phương pháp này có khả năng cực kỳ chậm đối với các cuộc gọi bên ngoài addr2line
và sẽ chỉ khả thi nếu một số lượng giới hạn cuộc gọi được thực hiện.
Mỗi lần in backtrace dường như mất hàng trăm mili giây, vì vậy hãy cảnh báo rằng nếu việc in backtrace thường xuyên xảy ra, hiệu suất chương trình sẽ bị ảnh hưởng đáng kể.
Đã thử nghiệm trên Ubuntu 19.10, GCC 9.2.1, boost 1.67.0.
glibc backtrace
Tài liệu tại: https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
C chính
#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;
}
Biên dịch:
gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -rdynamic -std=c99 \
-Wall -Wextra -pedantic-errors main.c
-rdynamic
là tùy chọn bắt buộc quan trọng.
Chạy:
./main.out
Kết quả đầu ra:
./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]
Vì vậy, chúng tôi ngay lập tức thấy rằng một tối ưu hóa nội tuyến đã xảy ra và một số chức năng đã bị mất khỏi dấu vết.
Nếu chúng tôi cố gắng lấy địa chỉ:
addr2line -e main.out 0x4008f9 0x4008fe
chúng tôi đạt được:
/home/ciro/main.c:21
/home/ciro/main.c:36
mà hoàn toàn tắt.
Nếu chúng tôi làm tương tự với -O0
thay vào đó, hãy ./main.out
cung cấp dấu vết đầy đủ chính xác:
./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]
và sau đó:
addr2line -e main.out 0x400a74 0x400a79
cho:
/home/cirsan01/test/main.c:34
/home/cirsan01/test/main.c:35
vì vậy các dòng chỉ bị tắt bởi một, CẦN LÀM tại sao? Nhưng điều này vẫn có thể sử dụng được.
Kết luận: dấu vết chỉ có thể hiển thị hoàn hảo với -O0
. Với tối ưu hóa, backtrace ban đầu được sửa đổi về cơ bản trong mã đã biên dịch.
Tuy nhiên, tôi không thể tìm thấy một cách đơn giản nào để tự động gỡ bỏ các ký hiệu C ++ bằng cách này, đây là một số thủ thuật:
Đã thử nghiệm trên Ubuntu 16.04, GCC 6.4.0, libc 2.23.
glibc backtrace_symbols_fd
Trình trợ giúp này thuận tiện hơn một chút backtrace_symbols
và tạo ra đầu ra về cơ bản giống hệt nhau:
/* 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("");
}
Đã thử nghiệm trên Ubuntu 16.04, GCC 6.4.0, libc 2.23.
glibc backtrace
với C ++ demangling hack 1: -export-dynamic
+dladdr
Phỏng theo: https://gist.github.com/fmela/591333/c64f4eb86037bb237862a8283df70cdfc25f01d3
Đây là một "hack" vì nó yêu cầu thay đổi ELF với -export-dynamic
.
glibc_ldl.cpp
#include <dlfcn.h> // for dladdr
#include <cxxabi.h> // for __cxa_demangle
#include <cstdio>
#include <string>
#include <sstream>
#include <iostream>
// This function produces a stack backtrace with demangled function & method names.
std::string backtrace(int skip = 1)
{
void *callstack[128];
const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
char buf[1024];
int nFrames = backtrace(callstack, nMaxFrames);
char **symbols = backtrace_symbols(callstack, nFrames);
std::ostringstream trace_buf;
for (int i = skip; i < nFrames; i++) {
Dl_info info;
if (dladdr(callstack[i], &info)) {
char *demangled = NULL;
int status;
demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
std::snprintf(
buf,
sizeof(buf),
"%-3d %*p %s + %zd\n",
i,
(int)(2 + sizeof(void*) * 2),
callstack[i],
status == 0 ? demangled : info.dli_sname,
(char *)callstack[i] - (char *)info.dli_saddr
);
free(demangled);
} else {
std::snprintf(buf, sizeof(buf), "%-3d %*p\n",
i, (int)(2 + sizeof(void*) * 2), callstack[i]);
}
trace_buf << buf;
std::snprintf(buf, sizeof(buf), "%s\n", symbols[i]);
trace_buf << buf;
}
free(symbols);
if (nFrames == nMaxFrames)
trace_buf << "[truncated]\n";
return trace_buf.str();
}
void my_func_2(void) {
std::cout << backtrace() << std::endl;
}
void my_func_1(double f) {
(void)f;
my_func_2();
}
void my_func_1(int i) {
(void)i;
my_func_2();
}
int main() {
my_func_1(1);
my_func_1(2.0);
}
Biên dịch và chạy:
g++ -fno-pie -ggdb3 -O0 -no-pie -o glibc_ldl.out -std=c++11 -Wall -Wextra \
-pedantic-errors -fpic glibc_ldl.cpp -export-dynamic -ldl
./glibc_ldl.out
đầu ra:
1 0x40130a my_func_2() + 41
./glibc_ldl.out(_Z9my_func_2v+0x29) [0x40130a]
2 0x40139e my_func_1(int) + 16
./glibc_ldl.out(_Z9my_func_1i+0x10) [0x40139e]
3 0x4013b3 main + 18
./glibc_ldl.out(main+0x12) [0x4013b3]
4 0x7f7594552b97 __libc_start_main + 231
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f7594552b97]
5 0x400f3a _start + 42
./glibc_ldl.out(_start+0x2a) [0x400f3a]
1 0x40130a my_func_2() + 41
./glibc_ldl.out(_Z9my_func_2v+0x29) [0x40130a]
2 0x40138b my_func_1(double) + 18
./glibc_ldl.out(_Z9my_func_1d+0x12) [0x40138b]
3 0x4013c8 main + 39
./glibc_ldl.out(main+0x27) [0x4013c8]
4 0x7f7594552b97 __libc_start_main + 231
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f7594552b97]
5 0x400f3a _start + 42
./glibc_ldl.out(_start+0x2a) [0x400f3a]
Đã thử nghiệm trên Ubuntu 18.04.
glibc backtrace
với C ++ demangling hack 2: phân tích cú pháp đầu ra backtrace
Được hiển thị tại: https://panthema.net/2008/0901-stacktrace-demangled/
Đây là một hack vì nó yêu cầu phân tích cú pháp.
CẦN LÀM nó để biên dịch và hiển thị nó ở đây.
libunwind
VIỆC CẦN LÀM điều này có bất kỳ lợi thế nào so với nền tảng glibc không? Đầu ra tương tự, cũng yêu cầu sửa đổi lệnh xây dựng, nhưng không phải là một phần của glibc, vì vậy yêu cầu cài đặt gói bổ sung.
Mã chuyển thể từ: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/
C chính
/* 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;
}
Biên dịch và chạy:
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
Hoặc #define _XOPEN_SOURCE 700
phải ở trên cùng, hoặc chúng ta phải sử dụng -std=gnu99
:
Chạy:
./main.out
Đầu ra:
0x4007db: (main+0xb)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)
0x4007e2: (main+0x12)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)
và:
addr2line -e main.out 0x4007db 0x4007e2
cho:
/home/ciro/main.c:34
/home/ciro/main.c:49
Với -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)
và:
addr2line -e main.out 0x4009f3 0x4009f8
cho:
/home/ciro/main.c:47
/home/ciro/main.c:48
Đã thử nghiệm trên Ubuntu 16.04, GCC 6.4.0, libunwind 1.1.
libunwind với cách tách tên C ++
Mã chuyển thể từ: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/
unwind.cpp
#define UNW_LOCAL_ONLY
#include <cxxabi.h>
#include <libunwind.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>
void backtrace() {
unw_cursor_t cursor;
unw_context_t context;
// Initialize cursor to current frame for local unwinding.
unw_getcontext(&context);
unw_init_local(&cursor, &context);
// Unwind frames one by one, going up the frame stack.
while (unw_step(&cursor) > 0) {
unw_word_t offset, pc;
unw_get_reg(&cursor, UNW_REG_IP, &pc);
if (pc == 0) {
break;
}
std::printf("0x%lx:", pc);
char sym[256];
if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
char* nameptr = sym;
int status;
char* demangled = abi::__cxa_demangle(sym, nullptr, nullptr, &status);
if (status == 0) {
nameptr = demangled;
}
std::printf(" (%s+0x%lx)\n", nameptr, offset);
std::free(demangled);
} else {
std::printf(" -- error: unable to obtain symbol name for this frame\n");
}
}
}
void my_func_2(void) {
backtrace();
std::cout << std::endl; // line 43
}
void my_func_1(double f) {
(void)f;
my_func_2();
}
void my_func_1(int i) {
(void)i;
my_func_2();
} // line 54
int main() {
my_func_1(1);
my_func_1(2.0);
}
Biên dịch và chạy:
sudo apt-get install libunwind-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o unwind.out -std=c++11 \
-Wall -Wextra -pedantic-errors unwind.cpp -lunwind -pthread
./unwind.out
Đầu ra:
0x400c80: (my_func_2()+0x9)
0x400cb7: (my_func_1(int)+0x10)
0x400ccc: (main+0x12)
0x7f4c68926b97: (__libc_start_main+0xe7)
0x400a3a: (_start+0x2a)
0x400c80: (my_func_2()+0x9)
0x400ca4: (my_func_1(double)+0x12)
0x400ce1: (main+0x27)
0x7f4c68926b97: (__libc_start_main+0xe7)
0x400a3a: (_start+0x2a)
và sau đó chúng ta có thể tìm thấy các dòng của my_func_2
và my_func_1(int)
với:
addr2line -e unwind.out 0x400c80 0x400cb7
mang lại:
/home/ciro/test/unwind.cpp:43
/home/ciro/test/unwind.cpp:54
VIỆC CẦN LÀM: tại sao các dòng lại bị lệch nhau?
Đã thử nghiệm trên Ubuntu 18.04, GCC 7.4.0, libunwind 1.2.1.
Tự động hóa GDB
Chúng tôi cũng có thể làm điều này với GDB mà không cần biên dịch lại bằng cách sử dụng: Làm thế nào để thực hiện một hành động cụ thể khi một điểm ngắt nhất định bị chạm trong GDB?
Mặc dù nếu bạn định in backtrace nhiều, điều này có thể sẽ kém nhanh hơn so với các tùy chọn khác, nhưng có lẽ chúng ta có thể đạt được tốc độ gốc compile code
, nhưng tôi lười thử nghiệm nó ngay bây giờ: Làm thế nào để gọi assembly trong gdb?
main.cpp
void my_func_2(void) {}
void my_func_1(double f) {
my_func_2();
}
void my_func_1(int i) {
my_func_2();
}
int main() {
my_func_1(1);
my_func_1(2.0);
}
main.gdb
start
break my_func_2
commands
silent
backtrace
printf "\n"
continue
end
continue
Biên dịch và chạy:
g++ -ggdb3 -o main.out main.cpp
gdb -nh -batch -x main.gdb main.out
Đầu ra:
Temporary breakpoint 1 at 0x1158: file main.cpp, line 12.
Temporary breakpoint 1, main () at main.cpp:12
12 my_func_1(1);
Breakpoint 2 at 0x555555555129: file main.cpp, line 1.
#0 my_func_2 () at main.cpp:1
#1 0x0000555555555151 in my_func_1 (i=1) at main.cpp:8
#2 0x0000555555555162 in main () at main.cpp:12
#0 my_func_2 () at main.cpp:1
#1 0x000055555555513e in my_func_1 (f=2) at main.cpp:4
#2 0x000055555555516f in main () at main.cpp:13
[Inferior 1 (process 14193) exited normally]
CẦN LÀM Tôi muốn làm điều này chỉ với -ex
dòng lệnh để không phải tạo main.gdb
nhưng tôi không thể commands
làm việc ở đó.
Đã thử nghiệm trong Ubuntu 19.04, GDB 8.2.
nền tảng Linux
Làm cách nào để in dấu vết ngăn xếp luồng hiện tại bên trong nhân Linux?
libdwfl
Điều này ban đầu được đề cập tại: https://stackoverflow.com/a/60713161/895245 và nó có thể là phương pháp tốt nhất, nhưng tôi phải đánh giá điểm chuẩn hơn một chút, nhưng hãy ủng hộ câu trả lời đó.
VIỆC CẦN LÀM: Tôi đã cố gắng thu nhỏ mã trong câu trả lời đó, đang hoạt động, thành một hàm duy nhất, nhưng nó đang ở chế độ mặc định, hãy cho tôi biết nếu ai đó có thể tìm thấy lý do.
dwfl.cpp
#include <cassert>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <cxxabi.h> // __cxa_demangle
#include <elfutils/libdwfl.h> // Dwfl*
#include <execinfo.h> // backtrace
#include <unistd.h> // getpid
// /programming/281818/unmangling-the-result-of-stdtype-infoname
std::string demangle(const char* name) {
int status = -4;
std::unique_ptr<char, void(*)(void*)> res {
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status==0) ? res.get() : name ;
}
std::string debug_info(Dwfl* dwfl, void* ip) {
std::string function;
int line = -1;
char const* file;
uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
Dwfl_Module* module = dwfl_addrmodule(dwfl, ip2);
char const* name = dwfl_module_addrname(module, ip2);
function = name ? demangle(name) : "<unknown>";
if (Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
Dwarf_Addr addr;
file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
}
std::stringstream ss;
ss << ip << ' ' << function;
if (file)
ss << " at " << file << ':' << line;
ss << std::endl;
return ss.str();
}
std::string stacktrace() {
// Initialize Dwfl.
Dwfl* dwfl = nullptr;
{
Dwfl_Callbacks callbacks = {};
char* debuginfo_path = nullptr;
callbacks.find_elf = dwfl_linux_proc_find_elf;
callbacks.find_debuginfo = dwfl_standard_find_debuginfo;
callbacks.debuginfo_path = &debuginfo_path;
dwfl = dwfl_begin(&callbacks);
assert(dwfl);
int r;
r = dwfl_linux_proc_report(dwfl, getpid());
assert(!r);
r = dwfl_report_end(dwfl, nullptr, nullptr);
assert(!r);
static_cast<void>(r);
}
// Loop over stack frames.
std::stringstream ss;
{
void* stack[512];
int stack_size = ::backtrace(stack, sizeof stack / sizeof *stack);
for (int i = 0; i < stack_size; ++i) {
ss << i << ": ";
// Works.
ss << debug_info(dwfl, stack[i]);
#if 0
// TODO intended to do the same as above, but segfaults,
// so possibly UB In above function that does not blow up by chance?
void *ip = stack[i];
std::string function;
int line = -1;
char const* file;
uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
Dwfl_Module* module = dwfl_addrmodule(dwfl, ip2);
char const* name = dwfl_module_addrname(module, ip2);
function = name ? demangle(name) : "<unknown>";
// TODO if I comment out this line it does not blow up anymore.
if (Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
Dwarf_Addr addr;
file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
}
ss << ip << ' ' << function;
if (file)
ss << " at " << file << ':' << line;
ss << std::endl;
#endif
}
}
dwfl_end(dwfl);
return ss.str();
}
void my_func_2() {
std::cout << stacktrace() << std::endl;
std::cout.flush();
}
void my_func_1(double f) {
(void)f;
my_func_2();
}
void my_func_1(int i) {
(void)i;
my_func_2();
}
int main(int argc, char **argv) {
long long unsigned int n;
if (argc > 1) {
n = strtoul(argv[1], NULL, 0);
} else {
n = 1;
}
for (long long unsigned int i = 0; i < n; ++i) {
my_func_1(1);
my_func_1(2.0);
}
}
Biên dịch và chạy:
sudo apt install libdw-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o dwfl.out -std=c++11 -Wall -Wextra -pedantic-errors dwfl.cpp -ldw
./dwfl.out
Đầu ra:
0: 0x402b74 stacktrace[abi:cxx11]() at /home/ciro/test/dwfl.cpp:65
1: 0x402ce0 my_func_2() at /home/ciro/test/dwfl.cpp:100
2: 0x402d7d my_func_1(int) at /home/ciro/test/dwfl.cpp:112
3: 0x402de0 main at /home/ciro/test/dwfl.cpp:123
4: 0x7f7efabbe1e3 __libc_start_main at ../csu/libc-start.c:342
5: 0x40253e _start at ../csu/libc-start.c:-1
0: 0x402b74 stacktrace[abi:cxx11]() at /home/ciro/test/dwfl.cpp:65
1: 0x402ce0 my_func_2() at /home/ciro/test/dwfl.cpp:100
2: 0x402d66 my_func_1(double) at /home/ciro/test/dwfl.cpp:107
3: 0x402df1 main at /home/ciro/test/dwfl.cpp:121
4: 0x7f7efabbe1e3 __libc_start_main at ../csu/libc-start.c:342
5: 0x40253e _start at ../csu/libc-start.c:-1
Điểm chuẩn chạy:
g++ -fno-pie -ggdb3 -O3 -no-pie -o dwfl.out -std=c++11 -Wall -Wextra -pedantic-errors dwfl.cpp -ldw
time ./dwfl.out 1000 >/dev/null
Đầu ra:
real 0m3.751s
user 0m2.822s
sys 0m0.928s
Vì vậy, chúng tôi thấy rằng phương pháp này nhanh hơn 10 lần so với stacktrace của Boost và do đó có thể áp dụng cho nhiều trường hợp sử dụng hơn.
Đã thử nghiệm trong Ubuntu 19.10 amd64, libdw-dev 0.176-1.1.
Xem thêm