बूस्ट स्टट्रेस
यहाँ पर प्रलेखित: 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_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
उबंटू 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)
, जो फ़ंक्शन अधिभार के कारण खराब हो गए हैं, हमारे लिए अच्छी तरह से ध्वस्त हो गए थे।
ध्यान दें कि पहली int
कॉल एक लाइन से बंद है (27 के बजाय 28) और दूसरी एक दो लाइनों (29 के बजाय 27) से बंद है। टिप्पणियों में यह सुझाव दिया गया था कि ऐसा इसलिए है क्योंकि निम्नलिखित निर्देश पते पर विचार किया जा रहा है, जो 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
Backtraces सामान्य रूप से अनुकूलन द्वारा उत्परिवर्तित सामान्य रूप से होते हैं। टेल कॉल ऑप्टिमाइज़ेशन इसका एक उल्लेखनीय उदाहरण है: टेल कॉल ऑप्टिमाइज़ेशन क्या है?
बेंचमार्क चालू -O3
:
time ./boost_stacktrace.out 1000 >/dev/null
आउटपुट:
real 0m43.573s
user 0m30.799s
sys 0m13.665s
इसलिए जैसा कि अपेक्षित था, हम देखते हैं कि यह विधि बाहरी कॉल के लिए बेहद धीमी संभावना है addr2line
, और केवल तभी संभव होगा जब सीमित संख्या में कॉल किए जा रहे हों।
प्रत्येक बैकट्रेस प्रिंट में सैकड़ों मिलीसेकंड लगते हैं, इसलिए चेतावनी दी जाती है कि यदि बैकट्रेस बहुत बार होता है, तो कार्यक्रम के प्रदर्शन में काफी नुकसान होगा।
उबंटू 19.10, जीसीसी 9.2.1 पर परीक्षण किया गया, 1.67.0 को बढ़ावा दिया।
glibc backtrace
यहाँ पर प्रलेखित: https://www.gnu.org/software/libc/manual/html_node/Backtracesces.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
प्रमुख आवश्यक विकल्प है।
Daud:
./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 क्यों? लेकिन यह अभी भी प्रयोग करने योग्य हो सकता है।
निष्कर्ष: backtraces केवल संभवतः पूरी तरह से दिखा सकते हैं -O0
। अनुकूलन के साथ, संकलित कोड में मूल बैकट्रेस को मौलिक रूप से संशोधित किया जाता है।
मुझे इस के साथ 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 पर परीक्षण किया गया।
backtrace
C ++ के साथ glibc हैक 1: -export-dynamic
+ हैकिंगdladdr
से अनुकूलित: https://gist.github.com/fmela/591333/c64f4eb86037bb237862a8283df70cdfc25f01d3
यह एक "हैक" है क्योंकि इसमें ईएलएफ को बदलने की आवश्यकता होती है -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]
उबंटू 18.04 पर परीक्षण किया गया।
backtrace
C ++ के साथ glibc हैक हैक 2: पार्स बैकट्रेस आउटपुट
पर दिखाया गया: https://panthema.net/2008/0901-stacktrace-demangled/
यह एक हैक है क्योंकि इसमें पार्सिंग की आवश्यकता होती है।
TODO इसे संकलन और यहां दिखाने के लिए इसे प्राप्त करता है।
libunwind
TODO को ग्लिबक बैकट्रेस पर कोई लाभ नहीं है? बहुत समान आउटपुट के लिए, बिल्ड कमांड को संशोधित करने की भी आवश्यकता होती है, लेकिन ग्लिबक का हिस्सा नहीं है, इसलिए अतिरिक्त पैकेज की स्थापना की आवश्यकता होती है।
कोड से अनुकूलित: 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
:
Daud:
./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 ++ नाम के साथ लिंबिंगविंड डिमांग्लिंग
कोड से अनुकूलित: 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
TODO: लाइनें एक-एक करके बंद क्यों होती हैं?
उबंटू 18.04, जीसीसी 7.4.0, लिबुनविंड 1.2.1 पर परीक्षण किया गया।
GDB स्वचालन
हम GDB के साथ भी ऐसा कर सकते हैं बिना उपयोग किए बिना पुन: उपयोग करें : एक विशेष कार्रवाई कैसे करें जब एक निश्चित ब्रेकप्वाइंट GDB में मारा जाता है?
यद्यपि यदि आप बैकट्रेस को बहुत प्रिंट करने जा रहे हैं, तो यह संभवतः अन्य विकल्पों की तुलना में कम तेज़ होगा, लेकिन शायद हम मूल गति के साथ पहुंच सकते हैं 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]
TODO मैं केवल -ex
कमांड लाइन से ऐसा नहीं करना चाहता था, main.gdb
लेकिन मुझे commands
वहां काम करने के लिए नहीं मिला ।
उबंटू 19.04, GDB 8.2 में परीक्षण किया गया।
लिनक्स कर्नेल
लिनक्स कर्नेल के अंदर वर्तमान थ्रेड स्टैक ट्रेस कैसे प्रिंट करें?
libdwfl
इसका मूल रूप से उल्लेख किया गया था: https://stackoverflow.com/a/60713161/895245 और यह सबसे अच्छी विधि हो सकती है, लेकिन मुझे थोड़ा और बेंचमार्क करना होगा, लेकिन कृपया उस उत्तर को बढ़ाएं।
TODO: मैंने उस उत्तर में कोड को कम करने की कोशिश की, जो काम कर रहा था, एक एकल फ़ंक्शन के लिए, लेकिन यह सेगफॉल्टिंग है, मुझे बताएं कि क्या कोई भी ढूंढ सकता है।
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
इसलिए हम देखते हैं कि यह विधि बूस्ट के स्टैकट्रेस की तुलना में 10 गुना तेज है, और इसलिए अधिक उपयोग के मामलों पर लागू हो सकती है।
Ubuntu 19.10 amd64 में परीक्षण किया गया, libdw-dev 0.176-1.1।
यह सभी देखें