স্ট্যাকট্রেস বুস্ট করুন
এখানে নথিবদ্ধ: https://www.boost.org/doc/libs/1_66_0/doc/html/stacktrace/getting_st সূত্র
এটি এখন পর্যন্ত আমি সবচেয়ে সুবিধাজনক বিকল্পটি দেখেছি কারণ এটি:
আসলে লাইন নম্বর মুদ্রণ করতে পারেন।
এটি কেবল কল করে 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
আউটপুটটি এবং নীচে "গ্লিবসি ব্যাকট্রেস" বিভাগে আরও ব্যাখ্যা করা হয়েছে, যা সাদৃশ্যপূর্ণ।
নোট কিভাবে my_func_1(int)
এবং my_func_1(float)
, যা ফাংশন জমিদার কারণে mangled হয় , সুন্দরভাবে আমাদের জন্য demangled হয়।
মনে রাখবেন যে প্রথম int
কলগুলি একটি লাইনের মাধ্যমে বন্ধ থাকে (২ 27 এর পরিবর্তে ২৮ এবং দ্বিতীয়টি দুটি লাইন দিয়ে বন্ধ থাকে (২৯ এর পরিবর্তে ২ is)। মন্তব্যগুলিতে পরামর্শ দেওয়া হয়েছিল যে এটি নীচের নির্দেশিকার ঠিকানা বিবেচনা করা হচ্ছে, যা 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
ব্যাকট্রেসগুলি অপ্টিমাইজেশন দ্বারা সাধারণ অপূরণীয়ভাবে বিকৃত হয়। টেইল কল অপ্টিমাইজেশন এর একটি উল্লেখযোগ্য উদাহরণ: টেল কল অপ্টিমাইজেশন কী?
বেঞ্চমার্ক চালু আছে -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/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
সুতরাং লাইন কেবল এক দ্বারা বন্ধ করা হয়, কেন? তবে এটি এখনও ব্যবহারযোগ্য হতে পারে।
উপসংহার: ব্যাকট্রেসগুলি কেবলমাত্র পুরোপুরি সাথে পুরোপুরি প্রদর্শিত হতে পারে -O0
। অপ্টিমাইজেশন সহ, মূল ব্যাকট্র্যাসটি সংকলিত কোডে মৌলিকভাবে সংশোধন করা হয়েছে।
আমি এর সাথে সি ++ চিহ্নগুলি স্বয়ংক্রিয়ভাবে ডিমেংল করার একটি সহজ উপায় খুঁজে পাইনি তবে এখানে কিছু হ্যাক রয়েছে:
উবুন্টু 16.04, জিসিসি 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("");
}
উবুন্টু 16.04, জিসিসি 6.4.0, libc 2.23 এ পরীক্ষিত।
glibc backtrace
সি সঙ্গে ++, হ্যাক demangling 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 এ পরীক্ষিত।
glibc backtrace
সি সঙ্গে ++, 2 হ্যাক demangling: পার্স ব্যাক-ট্রেস আউটপুট
এতে দেখানো হয়েছে: https://panthema.net/2008/0901-stacktrace-demangled/
এটি একটি হ্যাক কারণ এটি পার্সিংয়ের প্রয়োজন।
এটি সংকলন করতে এবং এটি এখানে দেখানোর জন্য TODO পান।
libunwind
টোডো কি গ্লাবসি ব্যাকট্রেসের চেয়ে কোনও সুবিধা রাখে? খুব অনুরূপ আউটপুট, এছাড়াও বিল্ড কমান্ড পরিবর্তন করতে হবে, কিন্তু গ্লিবসি অংশ নয় তাই অতিরিক্ত প্যাকেজ ইনস্টলেশন প্রয়োজন।
কোডটি রূপান্তরিত থেকে: 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
উবুন্টু 16.04, জিসিসি 6.4.0, লাইবুনউইন্ড 1.1 এ পরীক্ষিত।
সি ++ নাম ডিমেংলিংয়ের সাথে লাইবুনউইন্ড
কোডটি রূপান্তরিত থেকে: 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
টোডো: কেন একের পর এক লাইন বন্ধ?
উবুন্টু 18.04, জিসিসি 7.4.0, লাইবুনউইন্ড 1.2.2।
জিডিবি অটোমেশন
আমরা এটি ব্যবহার করে পুনরায় সংশোধন না করে জিডিবি দিয়ে এটিও করতে পারি: জিডিবিতে কোনও নির্দিষ্ট ব্রেকপয়েন্ট পাকানো হলে একটি নির্দিষ্ট ক্রিয়া কীভাবে করবেন?
যদিও আপনি ব্যাকট্র্যাসটি প্রচুর মুদ্রণ করতে চলেছেন তবে অন্যান্য বিকল্পগুলির তুলনায় এটি সম্ভবত কম দ্রুত হবে তবে আমরা সম্ভবত দেশীয় গতিতে পৌঁছাতে compile code
পারি তবে এখনই এটি পরীক্ষা করতে আমি অলস: জিডিবিতে অ্যাসেম্বলি কীভাবে ডাকব?
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
করতে চেয়েছিলাম কিন্তু আমি সেখানে কাজ করতে পারি না ।
উবুন্টু 19.04, জিডিবি 8.2 তে পরীক্ষিত।
লিনাক্স কার্নেল
লিনাক্স কার্নেলের ভিতরে কীভাবে বর্তমান থ্রেড স্ট্যাক ট্রেস প্রিন্ট করবেন?
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
সুতরাং আমরা দেখতে পাই যে এই পদ্ধতিটি বুস্টের স্ট্যাকট্রেসের চেয়ে 10x দ্রুত এবং এটি ব্যবহারের ক্ষেত্রে আরও কার্যকর হতে পারে।
উবুন্টু 19.10 amd64 এ পরীক্ষিত, libdw-dev 0.176-1.1।
আরো দেখুন