เพิ่ม 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
แต่ที่น่าเกลียดและอาจจะช้าถ้าคุณมีการร่องรอยมากเกินไป
demangles โดยค่าเริ่มต้น
Boost เป็นส่วนหัวเท่านั้นดังนั้นจึงไม่จำเป็นต้องแก้ไขระบบบิลด์ของคุณ
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
}
}
น่าเสียดายที่ดูเหมือนว่าจะเป็นส่วนเพิ่มเติมที่ใหม่กว่าและไม่มีแพ็คเกจlibboost-stacktrace-dev
ใน Ubuntu 16.04 เพียง 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
เราต้องเพิ่ม-ldl
ในตอนท้ายไม่เช่นนั้นการคอมไพล์จะล้มเหลว
เอาท์พุท:
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
ผลลัพธ์และมีคำอธิบายเพิ่มเติมในส่วน "glibc backtrace" ด้านล่างซึ่งคล้ายคลึงกัน
สังเกตวิธีการmy_func_1(int)
และmy_func_1(float)
, ซึ่งมี mangled เนื่องจากเกินพิกัดฟังก์ชั่นถูก demangled อย่างสำหรับเรา
สังเกตว่าประการแรก int
โทรถูกปิดทีละบรรทัด (28 แทนที่จะเป็น 27 และการโทรครั้งที่สองถูกปิดโดยสองบรรทัด (27 แทนที่จะเป็น 29) แนะนำในความคิดเห็นว่านี่เป็นเพราะที่อยู่คำสั่งต่อไปนี้กำลังพิจารณาอยู่ ทำให้ 27 กลายเป็น 28 และ 29 กระโดดออกจากวงและกลายเป็น 27
จากนั้นเราจะสังเกตว่าด้วย -O3
ผลลัพธ์จะถูกตัดขาดอย่างสมบูรณ์:
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
โดยทั่วไปแล้วการย้อนกลับจะถูกตัดทอนอย่างไม่สามารถแก้ไขได้โดยการเพิ่มประสิทธิภาพ การเพิ่มประสิทธิภาพการโทรหางเป็นตัวอย่างที่น่าสังเกตว่า:การเพิ่มประสิทธิภาพการโทรหางคืออะไร?
Benchmark ทำงานบน -O3
:
time ./boost_stacktrace.out 1000 >/dev/null
เอาท์พุท:
real 0m43.573s
user 0m30.799s
sys 0m13.665s
ดังที่คาดไว้เราจะเห็นว่าวิธีนี้มีแนวโน้มที่จะโทรไปภายนอกได้ช้ามาก addr2line
และจะทำได้ก็ต่อเมื่อมีการโทรออกในจำนวน จำกัด
การพิมพ์ backtrace แต่ละครั้งดูเหมือนจะใช้เวลาหลายร้อยมิลลิวินาทีดังนั้นโปรดเตือนว่าหากเกิดการย้อนกลับบ่อยครั้งประสิทธิภาพของโปรแกรมจะได้รับผลกระทบอย่างมาก
ทดสอบบน Ubuntu 19.10, GCC 9.2.1 เพิ่ม 1.67.0
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
เป็นตัวเลือกที่สำคัญที่จำเป็น
วิ่ง:
./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
. ด้วยการปรับให้เหมาะสม backtrace ดั้งเดิมจะได้รับการแก้ไขโดยพื้นฐานในโค้ดที่คอมไพล์
ฉันไม่พบวิธีง่ายๆในการแยกสัญลักษณ์ C ++ โดยอัตโนมัติด้วยวิธีนี้นี่คือแฮ็กบางส่วน:
ทดสอบบน 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
glibc backtrace
กับ C ++ demangling hack 1: -export-dynamic
+dladdr
ดัดแปลงมาจาก: https://gist.github.com/fmela/591333/c64f4eb86037bb237862a8283df70cdfc25f01d3
นี่คือ "แฮ็ก" เนื่องจากต้องเปลี่ยน ELF ด้วย-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);
}
รวบรวมและเรียกใช้:
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
เอาท์พุท:
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]
ทดสอบบน Ubuntu 18.04
glibc backtrace
ด้วย C ++ demangling hack 2: แยกวิเคราะห์เอาต์พุต backtrace
แสดงที่: https://panthema.net/2008/0901-stacktrace-demangled/
นี่เป็นการแฮ็กเนื่องจากต้องมีการแยกวิเคราะห์
สิ่งที่ต้องทำนำไปรวบรวมและแสดงที่นี่
libunwind
TODO สิ่งนี้มีข้อได้เปรียบเหนือ glibc backtrace หรือไม่? ผลลัพธ์ที่คล้ายกันมากยังต้องการการแก้ไขคำสั่ง build แต่ไม่ใช่ส่วนหนึ่งของ glibc ดังนั้นจึงต้องมีการติดตั้งแพ็คเกจเพิ่มเติม
โค้ดดัดแปลงมาจาก: 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
libunwind ด้วยชื่อ C ++ demangling
โค้ดดัดแปลงมาจาก: 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);
}
รวบรวมและเรียกใช้:
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
เอาท์พุท:
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)
จากนั้นเราจะพบบรรทัดmy_func_2
และmy_func_1(int)
ด้วย:
addr2line -e unwind.out 0x400c80 0x400cb7
ซึ่งจะช่วยให้:
/home/ciro/test/unwind.cpp:43
/home/ciro/test/unwind.cpp:54
สิ่งที่ต้องทำ: ทำไมเส้นออกทีละเส้น?
ทดสอบบน Ubuntu 18.04, GCC 7.4.0, libunwind 1.2.1
ระบบอัตโนมัติ GDB
นอกจากนี้เรายังสามารถทำได้ด้วย GDB โดยไม่ต้องคอมไพล์ใหม่โดยใช้: จะดำเนินการเฉพาะอย่างไรเมื่อมีการกดเบรกพอยต์ใน GDB
แม้ว่าคุณจะพิมพ์ backtrace เป็นจำนวนมาก แต่ก็น่าจะเร็วน้อยกว่าตัวเลือกอื่น ๆ แต่บางทีเราสามารถเข้าถึงความเร็วดั้งเดิมcompile code
ได้ แต่ฉันขี้เกียจที่จะทดสอบตอนนี้: จะเรียกแอสเซมบลีใน 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
รวบรวมและเรียกใช้:
g++ -ggdb3 -o main.out main.cpp
gdb -nh -batch -x main.gdb main.out
เอาท์พุท:
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]
สิ่งที่ต้องทำฉันต้องการทำสิ่งนี้โดยใช้เพียงแค่-ex
จากบรรทัดคำสั่งเพื่อไม่ต้องสร้างmain.gdb
แต่ฉันไม่commands
สามารถทำงานที่นั่นได้
ทดสอบใน Ubuntu 19.04, GDB 8.2
เคอร์เนลลินุกซ์
จะพิมพ์เธรดสแต็กติดตามปัจจุบันภายในเคอร์เนล Linux ได้อย่างไร
libdwfl
แต่เดิมมีการกล่าวถึงที่: https://stackoverflow.com/a/60713161/895245และอาจเป็นวิธีที่ดีที่สุด แต่ฉันต้องเปรียบเทียบอีกเล็กน้อย แต่โปรดเพิ่มคะแนนคำตอบนั้น
สิ่งที่ต้องทำ: ฉันพยายามย่อโค้ดในคำตอบนั้นซึ่งใช้งานได้เป็นฟังก์ชันเดียว แต่มันผิดพลาดโปรดแจ้งให้เราทราบหากมีใครสามารถหาสาเหตุได้
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);
}
}
รวบรวมและเรียกใช้:
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
เอาท์พุท:
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
การทดสอบเกณฑ์มาตรฐาน:
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
เอาท์พุท:
real 0m3.751s
user 0m2.822s
sys 0m0.928s
ดังนั้นเราจึงเห็นว่าวิธีนี้เร็วกว่า stacktrace ของ Boost 10 เท่าและอาจใช้ได้กับกรณีการใช้งานที่มากขึ้น
ทดสอบใน Ubuntu 19.10 amd64, libdw-dev 0.176-1.1
ดูสิ่งนี้ด้วย