Skip to content

Instantly share code, notes, and snippets.

@owent
Last active June 4, 2022 02:14
Show Gist options
  • Save owent/78e1fea9a0221ddf9ed540f4adacf358 to your computer and use it in GitHub Desktop.
Save owent/78e1fea9a0221ddf9ed540f4adacf358 to your computer and use it in GitHub Desktop.
test_backtrace
/**
* traceback for cpp
*
* Created on: 2018-01-27
* Author: owent
*
* Released under the MIT license
*
* @note Required flag -rdynamic or addr2line to get the function name when using gcc/clang in unix like system
* @note Using addr2line -Cfpe <exe_path> [func_addr...] for more detail when using gcc/clang
* @note Required flag /DEBUG /Zi to generate .pdb file when using MSVC
*/
#include <cstdlib>
#include <cstdio>
#include <string>
#ifndef BACKTRACE_MAX_FRAME_NUMBER
#define BACKTRACE_MAX_FRAME_NUMBER 100
#endif
#ifndef USING_WIN_DBG_ENG_COM
#define USING_WIN_DBG_ENG_COM 1
#endif
#ifndef USING_WIN_DBG_HELP
#define USING_WIN_DBG_HELP 1
#endif
#ifndef USING_GNU_UNWIND
#define USING_GNU_UNWIND 1
#endif
#ifndef USING_GNU_EXECINFO
#define USING_GNU_EXECINFO 1
#endif
#ifdef __GLIBCXX__
#include <cxxabi.h>
#define USING_LIBSTDCXX_ABI 1
#elif defined(_LIBCPP_ABI_VERSION)
#include <cxxabi.h>
#define USING_LIBCXX_ABI 1
#endif
#if defined(__cplusplus) && __cplusplus >= 201103L
#define USING_CXX11_NOEXCEPT noexcept
#else
#define USING_CXX11_NOEXCEPT
#endif
#ifdef USING_LIBUNWIND
#include <libunwind.h>
void print_trace() {
unw_context_t unw_ctx;
unw_cursor_t unw_cur;
unw_proc_info_t unw_proc;
unw_getcontext(&unw_ctx);
unw_init_local(&unw_cur, &unw_ctx);
char func_name_cache[256];
unw_word_t unw_offset;
int frame_id = 0;
#if defined(USING_LIBSTDCXX_ABI) || defined(USING_LIBCXX_ABI)
int cxx_abi_status;
char* realfunc_name;
#endif
do {
unw_get_proc_name(&unw_cur, func_name_cache, sizeof(func_name_cache), &unw_offset);
unw_get_proc_info(&unw_cur, &unw_proc);
const char* func_name = func_name_cache;
#if defined(USING_LIBSTDCXX_ABI) || defined(USING_LIBCXX_ABI)
realfunc_name = abi::__cxa_demangle(func_name_cache, 0, 0, &cxx_abi_status);
if (NULL != realfunc_name) {
func_name = realfunc_name;
}
#endif
printf("Frame #%02d: (%s+0x%lx) [0x%lx]\n", frame_id, func_name, unw_offset, unw_proc.start_ip);
#if defined(USING_LIBSTDCXX_ABI) || defined(USING_LIBCXX_ABI)
if (NULL != realfunc_name) {
free(realfunc_name);
realfunc_name = NULL;
}
#endif
int next_res = unw_step(&unw_cur);
if (0 == next_res) {
puts("All frames.");
break;
}
if (UNW_EBADFRAME == next_res) {
puts("Stop with UNW_EBADFRAME");
break;
}
if (UNW_ESTOPUNWIND == next_res) {
puts("Stop with UNW_ESTOPUNWIND");
break;
}
if (UNW_EINVALIDIP == next_res) {
puts("Stop with UNW_EINVALIDIP");
break;
}
if (UNW_ENOINFO == next_res) {
puts("Stop with UNW_ENOINFO");
break;
}
++ frame_id;
} while(true);
}
#else
#if defined(_WIN32) && (!defined(__GNUC__) || (!defined(USING_GNU_UNWIND) || !USING_GNU_UNWIND))
#include <Windows.h>
#if defined(USING_WIN_DBG_HELP) && USING_WIN_DBG_HELP
#include <DbgHelp.h>
#endif
#if defined(USING_WIN_DBG_ENG_COM) && USING_WIN_DBG_ENG_COM
#include <DbgEng.h>
#endif
#ifdef _MSC_VER
#if defined(USING_WIN_DBG_ENG_COM) && USING_WIN_DBG_ENG_COM
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "dbgeng.lib")
#endif
#if defined(USING_WIN_DBG_HELP) && USING_WIN_DBG_HELP
#pragma comment(lib, "dbghelp.lib")
#endif
#endif
#if defined(USING_WIN_DBG_ENG_COM) && USING_WIN_DBG_ENG_COM
#ifdef __CRT_UUID_DECL // for __MINGW32__
__CRT_UUID_DECL(IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8)
__CRT_UUID_DECL(IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba)
__CRT_UUID_DECL(IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50)
#elif defined(DEFINE_GUID) && !defined(BOOST_MSVC)
DEFINE_GUID(IID_IDebugClient,0x27fe5639,0x8407,0x4f47,0x83,0x64,0xee,0x11,0x8f,0xb0,0x8a,0xc8);
DEFINE_GUID(IID_IDebugControl,0x5182e668,0x105e,0x416e,0xad,0x92,0x24,0xef,0x80,0x04,0x24,0xba);
DEFINE_GUID(IID_IDebugSymbols,0x8c31e98c,0x983a,0x48a5,0x90,0x16,0x6f,0xe5,0xd6,0x67,0xa9,0x50);
#endif
template <class T>
class print_trace_com_holder {
private:
T* holder_;
private:
print_trace_com_holder(const print_trace_com_holder&);
print_trace_com_holder& operator=(const print_trace_com_holder&);
public:
print_trace_com_holder() USING_CXX11_NOEXCEPT : holder_(NULL) {}
~print_trace_com_holder() USING_CXX11_NOEXCEPT {
if (holder_) {
holder_->Release();
}
}
T* operator->() const USING_CXX11_NOEXCEPT {
return holder_;
}
PVOID* to_pvoid_ptr() USING_CXX11_NOEXCEPT {
return reinterpret_cast<PVOID*>(&holder_);
}
bool is_inited() const USING_CXX11_NOEXCEPT {
return !!holder_;
}
};
#endif
void print_trace() USING_CXX11_NOEXCEPT {
void * stack[ BACKTRACE_MAX_FRAME_NUMBER ];
unsigned short frames;
frames = CaptureStackBackTrace( 0, BACKTRACE_MAX_FRAME_NUMBER, stack, NULL );
#if !defined(_MSC_VER)
for(unsigned short i = 0; i < frames; i++ ) {
printf( "Frame %02i: () [0x%p]\n", i, stack[ i ]);
}
#else // MSVC required flag: /DEBUG /Zi
#if defined(USING_WIN_DBG_HELP) && USING_WIN_DBG_HELP
SYMBOL_INFO * symbol;
HANDLE process;
DWORD64 displacement = 0;
process = GetCurrentProcess();
// SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
SymInitialize( process, NULL, TRUE );
symbol = ( SYMBOL_INFO * )malloc(sizeof( SYMBOL_INFO ) + (MAX_SYM_NAME + 1) * sizeof(TCHAR));
symbol->MaxNameLen = MAX_SYM_NAME;
symbol->SizeOfStruct = sizeof( SYMBOL_INFO );
for(unsigned short i = 0; i < frames; i++ ) {
if(SymFromAddr( process, reinterpret_cast<ULONG64>(stack[i]), &displacement, symbol )) {
printf( "Frame %02i: (%s) [0x%p]\n", i, &symbol->Name[0], symbol->Address );
} else {
printf( "Frame %02i: () [0x%p]\n", i, stack[i]);
}
}
free(symbol);
SymCleanup(process);
#endif
// ================== Using IDebugClient ==================
#if defined(USING_WIN_DBG_ENG_COM) && USING_WIN_DBG_ENG_COM
print_trace_com_holder<IDebugClient> dbg_cli;
print_trace_com_holder<IDebugControl> dbg_ctrl;
print_trace_com_holder<IDebugSymbols> dbg_sym;
if (S_OK != ::DebugCreate(__uuidof(IDebugClient), dbg_cli.to_pvoid_ptr())) {
return;
}
const bool res0 = (S_OK == dbg_cli->QueryInterface(
__uuidof(IDebugControl),
dbg_ctrl.to_pvoid_ptr()
));
if (!res0) {
return;
}
const bool res1 = (S_OK == dbg_cli->AttachProcess(
0,
::GetCurrentProcessId(),
DEBUG_ATTACH_NONINVASIVE | DEBUG_ATTACH_NONINVASIVE_NO_SUSPEND
));
if (!res1) {
return;
}
if (S_OK != dbg_ctrl->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE)) {
return;
}
// No cheking: QueryInterface sets the output parameter to NULL in case of error.
dbg_cli->QueryInterface(__uuidof(IDebugSymbols), dbg_sym.to_pvoid_ptr());
for(unsigned short i = 0; i < frames; i++ ) {
if (!dbg_sym.is_inited()) {
break;
}
const ULONG64 offset = reinterpret_cast<ULONG64>(stack[i]);
char name[256];
name[0] = '\0';
ULONG size = 0;
bool res = (S_OK == dbg_sym->GetNameByOffset(
offset,
name,
sizeof(name),
&size,
0
));
std::string result;
if (!res && size != 0) {
result.resize(size);
res = (S_OK == dbg_sym->GetNameByOffset(
offset,
&result[0],
static_cast<ULONG>(result.size()),
&size,
0
));
} else if (res) {
result = name;
}
printf( "Frame %02i: (%s) [0x%p]\n", i, result.c_str(), stack[i]);
}
#endif
#endif
}
#else
#if defined(USING_GNU_EXECINFO) && USING_GNU_EXECINFO
#include <execinfo.h>
#endif
#if defined(USING_GNU_UNWIND) && USING_GNU_UNWIND
#include <unwind.h>
struct print_trace_unwind_state_t {
size_t frames_to_skip;
_Unwind_Word* current;
_Unwind_Word* end;
};
static _Unwind_Reason_Code print_trace_unwind_callback(::_Unwind_Context* context, void* arg) {
// Note: do not write `::_Unwind_GetIP` because it is a macro on some platforms.
// Use `_Unwind_GetIP` instead!
print_trace_unwind_state_t* const state = reinterpret_cast<print_trace_unwind_state_t*>(arg);
if (state->frames_to_skip) {
--state->frames_to_skip;
return _Unwind_GetIP(context) ? ::_URC_NO_REASON : ::_URC_END_OF_STACK;
}
*state->current = _Unwind_GetIP(context);
++state->current;
if (!*(state->current - 1) || state->current == state->end) {
return ::_URC_END_OF_STACK;
}
return ::_URC_NO_REASON;
}
#endif
/* Obtain a backtrace and print it to stdout. */
struct print_trace_symbol_group_t {
std::string module_name;
std::string func_name;
std::string func_offset;
std::string func_address;
};
static const char* print_trace_skip_space(const char* name) {
if (NULL == name) {
return name;
}
while (*name && (' ' == *name || '\r' == *name || '\t' == *name || '\r' == *name)) {
++name;
}
return name;
}
inline bool print_trace_is_number_char(char c) {
return c >= '0' && c <= '9';
}
inline bool print_trace_is_ident_char(char c) {
return '_' == c || '$' == c || print_trace_is_number_char(c) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c & 0x80);// utf-8 or unicode
}
static const char* print_trace_get_ident_end(const char* name) {
if (NULL == name) {
return name;
}
while (*name && print_trace_is_ident_char(*name)) {
++ name;
}
return name;
}
static bool print_trace_pick_ident(const char* name, const char*& start, const char*& end, char& previous_c) {
previous_c = 0;
start = name;
end = name;
bool ret = false;
if (NULL == name) {
return false;
}
while(*name) {
name = print_trace_skip_space(name);
if (print_trace_is_ident_char(*name)) {
start = name;
end = print_trace_get_ident_end(name);
ret = true;
break;
} else {
previous_c = *name;
++name;
}
}
return ret;
}
static void print_trace_pick_symbol_info (const char* name, print_trace_symbol_group_t& out) {
out.module_name.clear();
out.func_name.clear();
out.func_offset.clear();
out.func_address.clear();
if (NULL == name || 0 == *name) {
return;
}
name = print_trace_skip_space(name);
while (name) {
const char* start;
char previous_c;
if (print_trace_pick_ident(name, start, name, previous_c)) {
if (print_trace_is_number_char(*start)) {
if ('+' == previous_c) {
out.func_offset = "+";
out.func_offset.insert(out.func_offset.end(), start, name);
} else {
out.func_address.assign(start, name);
}
} else {
if (out.module_name.empty()) {
out.module_name.assign(start, name);
} else {
out.func_name.assign(start, name);
}
}
} else {
break;
}
name = print_trace_skip_space(name);
}
}
void print_trace () {
#if defined(USING_GNU_EXECINFO) && USING_GNU_EXECINFO
{
void *array[BACKTRACE_MAX_FRAME_NUMBER];
size_t size;
char **func_name_cache;
size = backtrace (array, BACKTRACE_MAX_FRAME_NUMBER);
func_name_cache = backtrace_symbols (array, size);
for (size_t i = 0; i < size; i++) {
print_trace_symbol_group_t symbol;
print_trace_pick_symbol_info(func_name_cache[i], symbol);
#if defined(USING_LIBSTDCXX_ABI) || defined(USING_LIBCXX_ABI)
if (!symbol.func_name.empty()) {
int cxx_abi_status;
char* realfunc_name = abi::__cxa_demangle(symbol.func_name.c_str(), 0, 0, &cxx_abi_status);
if (NULL != realfunc_name) {
symbol.func_name = realfunc_name;
}
if (NULL != realfunc_name) {
free(realfunc_name);
}
}
#endif
printf("Frame #%02d: (%s%s) [%s]\n", static_cast<int>(i), symbol.func_name.c_str(), symbol.func_offset.c_str(), symbol.func_address.c_str());
}
free (func_name_cache);
}
#endif
#if defined(USING_GNU_UNWIND) && USING_GNU_UNWIND
{
_Unwind_Word stacks[BACKTRACE_MAX_FRAME_NUMBER];
print_trace_unwind_state_t state;
state.frames_to_skip = 0;
state.current = stacks;
state.end = stacks + BACKTRACE_MAX_FRAME_NUMBER;
::_Unwind_Backtrace(&print_trace_unwind_callback, &state);
size_t frames_count = state.current - &stacks[0];
for (size_t i = 0; i < frames_count; ++ i) {
printf("Frame #%02d: () [0x%lx]\n", static_cast<int>(i), stacks[i]);
}
}
#endif
}
#endif
#endif
void func1(int times) {
if (times > 0) {
func1(times - 1);
return;
}
print_trace();
}
class functor2 {
public:
void func2(int times) {
if (times & 0x01) {
func2(times - 1);
} else {
func1(times - 1);
}
}
};
class functor3 {
public:
static void func3(int times) {
if (times & 0x01) {
func3(times - 1);
} else {
functor2 f;
f.func2(times - 1);
}
}
};
struct functor4 {
void operator()(int times) {
if (times & 0x01) {
(*this)(times - 1);
} else {
functor3::func3(times - 1);
}
}
};
static void func5(int times) {
if (times & 0x01) {
func5(times - 1);
} else {
functor4 f;
f(times - 1);
}
}
void func6(int times) {
if (times & 0x01) {
func6(times - 1);
} else {
func5(times - 1);
}
}
int main (int argc, char* argv[]) {
int times = 15;
if (argc > 1) {
times = atoi(argv[1]);
}
func6(times);
return 0;
}

Linux下使用libunwind

g++ -Wall test_backtrace.cpp -O0 -g -ggdb -rdynamic -o test_backtrace.exe -DUSING_LIBUNWIND -lunwind -lunwind-x86_64
clang++ -Wall test_backtrace.cpp -O0 -g -ggdb -rdynamic -o test_backtrace.exe -DUSING_LIBUNWIND -lunwind -lunwind-x86_64

命令和输出:

./test_backtrace.exe

Frame #00: (print_trace()+0x17) [0x400c30]
Frame #01: (func1(int)+0x2c) [0x400e80]
Frame #02: (func1(int)+0x22) [0x400e80]
Frame #03: (func1(int)+0x22) [0x400e80]
Frame #04: (func1(int)+0x22) [0x400e80]
Frame #05: (func1(int)+0x22) [0x400e80]
Frame #06: (func1(int)+0x22) [0x400e80]
Frame #07: (functor2::func2(int)+0x49) [0x401040]
Frame #08: (functor2::func2(int)+0x37) [0x401040]
Frame #09: (functor3::func3(int)+0x3d) [0x400ff0]
Frame #10: (functor3::func3(int)+0x27) [0x400ff0]
Frame #11: (functor4::operator()(int)+0x49) [0x400fa0]
Frame #12: (functor4::operator()(int)+0x37) [0x400fa0]
Frame #13: (func5(int)+0x3d) [0x400f00]
Frame #14: (func5(int)+0x27) [0x400f00]
Frame #15: (func6(int)+0x39) [0x400ec0]
Frame #16: (func6(int)+0x27) [0x400ec0]
Frame #17: (main+0x3f) [0x400f50]
Frame #18: (__libc_start_main+0xf0) [0x7f2d3cf50740]
Frame #19: (_start+0x29) [0x400b30]
Frame #20: (+0x29) [0x0]
All frames.

Linux下使用backtrace

g++ -Wall test_backtrace.cpp -O0 -g -ggdb -rdynamic -o test_backtrace.exe
clang++ -Wall test_backtrace.cpp -O0 -g -ggdb -rdynamic -o test_backtrace.exe

命令和输出:

./test_backtrace.exe

Frame #00: (print_trace()+0x1c) [0x4013ec]
Frame #01: (func1(int)+0x2c) [0x4019dc]
Frame #02: (func1(int)+0x22) [0x4019d2]
Frame #03: (func1(int)+0x22) [0x4019d2]
Frame #04: (func1(int)+0x22) [0x4019d2]
Frame #05: (func1(int)+0x22) [0x4019d2]
Frame #06: (func1(int)+0x22) [0x4019d2]
Frame #07: (functor2::func2(int)+0x49) [0x402189]
Frame #08: (functor2::func2(int)+0x37) [0x402177]
Frame #09: (functor3::func3(int)+0x3d) [0x40212d]
Frame #10: (functor3::func3(int)+0x27) [0x402117]
Frame #11: (functor4::operator()(int)+0x49) [0x4020e9]
Frame #12: (functor4::operator()(int)+0x37) [0x4020d7]
Frame #13: () [0x401a6d]
Frame #14: () [0x401a57]
Frame #15: (func6(int)+0x39) [0x401a29]
Frame #16: (func6(int)+0x27) [0x401a17]
Frame #17: (main+0x3f) [0x401abf]
Frame #18: (__libc_start_main+0xf0) [0x7f7ffc390830]
Frame #19: (_start+0x29) [0x4012f9]

Linux下使用backtrace+addr2line

g++ -Wall test_backtrace.cpp -O0 -g -ggdb -o test_backtrace.exe
clang++ -Wall test_backtrace.cpp -O0 -g -ggdb -o test_backtrace.exe

命令和输出:

./test_backtrace.exe | eval 'while read -r line || [[ -n "$line" ]]; do ADDR=${line/*[}; ADDR=${ADDR%]*}; echo "${line}"; echo "    $(addr2line -Cfpe ./test_backtrace.exe $ADDR)"; done'

Frame #00: () [0x40167a]
    print_trace() at /mnt/d/workspace/test/test_backtrace.cpp:474
Frame #01: () [0x4019dc]
    func1(int) at /mnt/d/workspace/test/test_backtrace.cpp:493 (discriminator 1)
Frame #02: () [0x4019d2]
    func1(int) at /mnt/d/workspace/test/test_backtrace.cpp:489
Frame #03: () [0x4019d2]
    func1(int) at /mnt/d/workspace/test/test_backtrace.cpp:489
Frame #04: () [0x4019d2]
    func1(int) at /mnt/d/workspace/test/test_backtrace.cpp:489
Frame #05: () [0x4019d2]
    func1(int) at /mnt/d/workspace/test/test_backtrace.cpp:489
Frame #06: () [0x4019d2]
    func1(int) at /mnt/d/workspace/test/test_backtrace.cpp:489
Frame #07: () [0x402189]
    functor2::func2(int) at /mnt/d/workspace/test/test_backtrace.cpp:503
Frame #08: () [0x402177]
    functor2::func2(int) at /mnt/d/workspace/test/test_backtrace.cpp:500
Frame #09: () [0x40212d]
    functor3::func3(int) at /mnt/d/workspace/test/test_backtrace.cpp:515
Frame #10: () [0x402117]
    functor3::func3(int) at /mnt/d/workspace/test/test_backtrace.cpp:511
Frame #11: () [0x4020e9]
    functor4::operator()(int) at /mnt/d/workspace/test/test_backtrace.cpp:525
Frame #12: () [0x4020d7]
    functor4::operator()(int) at /mnt/d/workspace/test/test_backtrace.cpp:522
Frame #13: () [0x401a6d]
    func5(int) at /mnt/d/workspace/test/test_backtrace.cpp:535
Frame #14: () [0x401a57]
    func5(int) at /mnt/d/workspace/test/test_backtrace.cpp:531
Frame #15: () [0x401a29]
    func6(int) at /mnt/d/workspace/test/test_backtrace.cpp:543
Frame #16: () [0x401a17]
    func6(int) at /mnt/d/workspace/test/test_backtrace.cpp:540
Frame #17: () [0x401abf]
    main at /mnt/d/workspace/test/test_backtrace.cpp:550
Frame #18: () [0x7f3c44f90830]
    ?? ??:0
Frame #19: () [0x4012f9]
    _start at ??:?
Frame #20: () [0x0]
    ?? ??:0

Windows+MSVC使用dbghelp

cl /nologo /W4 /DEBUG /Zi test_backtrace.cpp

命令和输出:

test_backtrace.exe

Frame 00: (print_trace) [0x00007FF6C75768F0]
Frame 01: (func1) [0x00007FF6C7576EE0]
Frame 02: (func1) [0x00007FF6C7576EE0]
Frame 03: (func1) [0x00007FF6C7576EE0]
Frame 04: (func1) [0x00007FF6C7576EE0]
Frame 05: (func1) [0x00007FF6C7576EE0]
Frame 06: (func1) [0x00007FF6C7576EE0]
Frame 07: (functor2::func2) [0x00007FF6C7578850]
Frame 08: (functor2::func2) [0x00007FF6C7578850]
Frame 09: (functor3::func3) [0x00007FF6C75788A0]
Frame 10: (functor3::func3) [0x00007FF6C75788A0]
Frame 11: (functor4::operator()) [0x00007FF6C7577D20]
Frame 12: (functor4::operator()) [0x00007FF6C7577D20]
Frame 13: (func5) [0x00007FF6C7576F10]
Frame 14: (func5) [0x00007FF6C7576F10]
Frame 15: (func6) [0x00007FF6C7576F50]
Frame 16: (func6) [0x00007FF6C7576F50]
Frame 17: (main) [0x00007FF6C7576F90]
Frame 18: (__scrt_common_main_seh) [0x00007FF6C757A078]
Frame 19: (BaseThreadInitThunk) [0x00007FFCAA081FD0]
Frame 20: (RtlUserThreadStart) [0x00007FFCAC64EF90]

Windows+MSVC使用dbgeng组件

cl /nologo /W4 /DEBUG /Zi test_backtrace.cpp

命令和输出:

test_backtrace.exe

Frame 00: (test_backtrace!print_trace) [0x00007FF6C7576921]
Frame 01: (test_backtrace!func1) [0x00007FF6C7576F03]
Frame 02: (test_backtrace!func1) [0x00007FF6C7576EFC]
Frame 03: (test_backtrace!func1) [0x00007FF6C7576EFC]
Frame 04: (test_backtrace!func1) [0x00007FF6C7576EFC]
Frame 05: (test_backtrace!func1) [0x00007FF6C7576EFC]
Frame 06: (test_backtrace!func1) [0x00007FF6C7576EFC]
Frame 07: (test_backtrace!functor2::func2) [0x00007FF6C7578889]
Frame 08: (test_backtrace!functor2::func2) [0x00007FF6C757887A]
Frame 09: (test_backtrace!functor3::func3) [0x00007FF6C75788D4]
Frame 10: (test_backtrace!functor3::func3) [0x00007FF6C75788C0]
Frame 11: (test_backtrace!functor4::operator()) [0x00007FF6C7577D59]
Frame 12: (test_backtrace!functor4::operator()) [0x00007FF6C7577D4A]
Frame 13: (test_backtrace!func5) [0x00007FF6C7576F44]
Frame 14: (test_backtrace!func5) [0x00007FF6C7576F30]
Frame 15: (test_backtrace!func6) [0x00007FF6C7576F7F]
Frame 16: (test_backtrace!func6) [0x00007FF6C7576F70]
Frame 17: (test_backtrace!main) [0x00007FF6C7576FD0]
Frame 18: (test_backtrace!__scrt_common_main_seh) [0x00007FF6C757A188]
Frame 19: (KERNEL32!BaseThreadInitThunk) [0x00007FFCAA081FE4]
Frame 20: (ntdll!RtlUserThreadStart) [0x00007FFCAC64EFB1]

Windows+Mingw64+addr2line

g++ test_backtrace.cpp -O0 -g -ggdb -o test_backtrace.exe -ldbghelp -ldbgeng -Wall -DUSING_GNU_UNWIND=0
clang++ test_backtrace.cpp -O0 -g -ggdb -o test_backtrace.exe -ldbghelp -ldbgeng -Wall -DUSING_GNU_UNWIND=0

命令和输出:

./test_backtrace.exe | eval 'while read -r line || [[ -n "$line" ]]; do ADDR=${line/*[}; ADDR=${ADDR%]*}; echo "${line}"; echo "    $(addr2line -Cfpe ./test_backtrace.exe $ADDR)"; done'

Frame 00: () [0x00000000004015b4]
    print_trace() 于 D:\workspace\test/test_backtrace.cpp:176
Frame 01: () [0x000000000040163b]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:353
Frame 02: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 03: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 04: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 05: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 06: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 07: () [0x000000000040189d]
    functor2::func2(int) 于 D:\workspace\test/test_backtrace.cpp:363
Frame 08: () [0x000000000040188a]
    functor2::func2(int) 于 D:\workspace\test/test_backtrace.cpp:360
Frame 09: () [0x000000000040183e]
    functor3::func3(int) 于 D:\workspace\test/test_backtrace.cpp:375
Frame 10: () [0x0000000000401826]
    functor3::func3(int) 于 D:\workspace\test/test_backtrace.cpp:371
Frame 11: () [0x00000000004017ed]
    functor4::operator()(int) 于 D:\workspace\test/test_backtrace.cpp:385
Frame 12: () [0x00000000004017da]
    functor4::operator()(int) 于 D:\workspace\test/test_backtrace.cpp:382
Frame 13: () [0x00000000004016ce]
    func5(int) 于 D:\workspace\test/test_backtrace.cpp:395
Frame 14: () [0x00000000004016b6]
    func5(int) 于 D:\workspace\test/test_backtrace.cpp:391
Frame 15: () [0x0000000000401689]
    func6(int) 于 D:\workspace\test/test_backtrace.cpp:403
Frame 16: () [0x0000000000401676]
    func6(int) 于 D:\workspace\test/test_backtrace.cpp:400
Frame 17: () [0x0000000000401734]
    main 于 D:\workspace\test/test_backtrace.cpp:410
Frame 18: () [0x00000000004013f7]
    __tmainCRTStartup 于 C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:343
Frame 19: () [0x000000000040152b]
    mainCRTStartup 于 C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:221
Frame 20: () [0x00007ffcaa081fe4]
    ?? ??:0
Frame 21: () [0x00007ffcac64efb1]
    ?? ??:0

Linux+gcc/clang+unwind+addr2line

g++ test_backtrace.cpp -O0 -g -ggdb -o test_backtrace.exe -Wall -DUSING_GNU_EXECINFO=0
clang++ test_backtrace.cpp -O0 -g -ggdb -o test_backtrace.exe -Wall -DUSING_GNU_EXECINFO=0

命令和输出:

./test_backtrace.exe | eval 'while read -r line || [[ -n "$line" ]]; do ADDR=${line/*[}; ADDR=${ADDR%]*}; echo "${line}"; echo "    $(addr2line -Cfpe ./test_backtrace.exe $ADDR)"; done'

Frame 00: () [0x00000000004015b4]
    print_trace() 于 D:\workspace\test/test_backtrace.cpp:176
Frame 01: () [0x000000000040163b]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:353
Frame 02: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 03: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 04: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 05: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 06: () [0x0000000000401631]
    func1(int) 于 D:\workspace\test/test_backtrace.cpp:349
Frame 07: () [0x000000000040189d]
    functor2::func2(int) 于 D:\workspace\test/test_backtrace.cpp:363
Frame 08: () [0x000000000040188a]
    functor2::func2(int) 于 D:\workspace\test/test_backtrace.cpp:360
Frame 09: () [0x000000000040183e]
    functor3::func3(int) 于 D:\workspace\test/test_backtrace.cpp:375
Frame 10: () [0x0000000000401826]
    functor3::func3(int) 于 D:\workspace\test/test_backtrace.cpp:371
Frame 11: () [0x00000000004017ed]
    functor4::operator()(int) 于 D:\workspace\test/test_backtrace.cpp:385
Frame 12: () [0x00000000004017da]
    functor4::operator()(int) 于 D:\workspace\test/test_backtrace.cpp:382
Frame 13: () [0x00000000004016ce]
    func5(int) 于 D:\workspace\test/test_backtrace.cpp:395
Frame 14: () [0x00000000004016b6]
    func5(int) 于 D:\workspace\test/test_backtrace.cpp:391
Frame 15: () [0x0000000000401689]
    func6(int) 于 D:\workspace\test/test_backtrace.cpp:403
Frame 16: () [0x0000000000401676]
    func6(int) 于 D:\workspace\test/test_backtrace.cpp:400
Frame 17: () [0x0000000000401734]
    main 于 D:\workspace\test/test_backtrace.cpp:410
Frame 18: () [0x00000000004013f7]
    __tmainCRTStartup 于 C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:343
Frame 19: () [0x000000000040152b]
    mainCRTStartup 于 C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/crtexe.c:221
Frame 20: () [0x00007ffcaa081fe4]
    ?? ??:0
Frame 21: () [0x00007ffcac64efb1]
    ?? ??:0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment