C++中如何打印调用栈

本文介绍了一种在C++程序中实现回溯调试的方法,通过使用backtrace函数记录调用堆栈信息,并配合编译选项-g和-rdynamic增强调试信息的可读性。此外,还提供了一个实用的宏定义,简化了回溯调试的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  if (your_condition_bool_val == true)
  {
    void * array[10];
    char ** strings;
    size_t size = backtrace(array, 10);
    strings = backtrace_symbols(array, size);
    for (size_t i = 0; i < size; ++i)
    { 
      TBSYS_LOG(WARN, "backtrace%lu:[%s]", i, strings[i]);
    }
    free(strings);
  }


然后在Makefile中保证有下面两个编译选项:

CXXFLAGS = -g  -rdynamic

如果没有rdynamic,只会打印出调用者的地址,通过addr2line工具可以从地址反查到函数,不过这么做实在是太麻烦,还是加上编译选项更方便。

输出结果如下:

[2012-12-26 01:00:14.659541] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace0:[/home/raywill/ob2/bin/rootserver(_ZN9oceanbase6common19ObSchemaServiceImpl4initEPNS0_12ObScanHelperEb+0x378) [0x5fa784]]
[2012-12-26 01:00:14.659552] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace1:[/home/raywill/ob2/bin/rootserver(_ZN9oceanbase10rootserver13ObRootServer210get_schemaEbbRNS_6common17ObSchemaManagerV2ERl+0x4ab) [0x50716f]]
[2012-12-26 01:00:14.659558] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace2:[/home/raywill/ob2/bin/rootserver(_ZN9oceanbase10rootserver14ObRootBalancer18nb_get_table_countEv+0x134) [0x4cfebc]]
[2012-12-26 01:00:14.659577] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace3:[/home/raywill/ob2/bin/rootserver(_ZN9oceanbase10rootserver14ObRootBalancer14do_new_balanceEv+0xdf) [0x4d0399]]
[2012-12-26 01:00:14.659582] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace4:[/home/raywill/ob2/bin/rootserver(_ZN9oceanbase10rootserver14ObRootBalancer10do_balanceERb+0x115) [0x4d08b3]]
[2012-12-26 01:00:14.659586] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace5:[/home/raywill/ob2/bin/rootserver(_ZN9oceanbase10rootserver22ObRootBalancerRunnable3runEPN5tbsys7CThreadEPv+0x180) [0x4d265a]]
[2012-12-26 01:00:14.659591] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace6:[/home/raywill/ob2/bin/rootserver(_ZN5tbsys7CThread4hookEPv+0x64) [0x69d134]]
[2012-12-26 01:00:14.659599] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace7:[/lib64/libpthread.so.0 [0x3cf70064a7]]
[2012-12-26 01:00:14.659604] WARN  init (ob_schema_service_impl.cpp:249) [1108461888] backtrace8:[/lib64/libc.so.6(clone+0x6d) [0x33950d3c2d]]

@leekayak  写了一个宏,更加好用!


#define BACKTRACE(LEVEL, cond, _fmt_, args...) \
  do \
  { \
    if (cond) \
    { \
      void *buffer[100]; \
      int size = backtrace(buffer, 100); \
      char **strings = backtrace_symbols(buffer, size); \
      if (NULL != strings) \
      { \
        TBSYS_LOG(LEVEL, _fmt_ " BackTrace Start: ", ##args); \
        for (int i = 0; i < size; i++) \
        { \
          TBSYS_LOG(LEVEL, "BT[%d] @[%s]", i, strings[i]); \
        } \
        free(strings); \
      } \
    } \
  } while (false)


用法:
BACKTRACE(INFO, c1==c2, "%s, %d, %d. here is trace:",  "found c1 equals to c2", c1, c2);  //当c1==c2时打印backtrace

BACKTRACE(INFO, true, "here is trace:"); //总是打印backtrace

【搞定】


附:

addr2line用法: addr2line --exe=my_app 0x111111 0x2222222 0x3333333 


<think>我们正在讨论在Android C++打印调用堆栈的方法。根据之前的错误,用户可能遇到了头文件缺失的问题,但现在用户明确询问如何在C++打印调用堆栈。 在Android C++中,常用的方法有几种: 1. 使用Android NDK提供的<unwindstack/Unwinder.h>库(需要NDK r18+,推荐r21+) 2. 使用libcorkscrew(旧版NDK,已废弃) 3. 使用libunwind(跨平台,但在Android中不如unwindstack稳定) 由于用户之前遇到'unwindstack/Unwinder.h'找不到的问题,我们已经给出了配置NDK路径的方法。现在,假设用户已经配置好NDK,我们将重点介绍使用unwindstack的方法。 步骤: 1. 包含头文件 2. 初始化Unwinder对象 3. 调用Unwinder的Unwind方法 4. 遍历并打印堆栈帧 注意:需要链接libunwindstack库。 示例代码: ```cpp #include <unwindstack/Unwinder.h> #include <unwindstack/AndroidUnwinder.h> #include <android/log.h> #define LOG_TAG "StackUnwind" #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) void printStackTrace() { // 获取当前线程的调用栈,最大深度为64 unwindstack::AndroidLocalUnwinder unwinder; std::vector<unwindstack::FrameData> frames; // 执行堆栈展开 if (unwinder.Unwind(frames, 64)) { ALOGE("Unwind failed"); return; } // 打印堆栈帧 for (const auto& frame : frames) { // 格式化帧信息 std::string frame_line = unwindstack::FrameToString(frame); ALOGE("%s", frame_line.c_str()); } } ``` 但是,请注意: - 此方法需要Android 9(API 28)及以上版本才能获得完整的符号信息,在旧版本上可能只能获取地址。 - 在Android 10(API 29)及以上,可以使用`unwindstack::AndroidUnwinder`,它提供了更简洁的接口。 对于旧版Android(API < 28),可以使用以下替代方法: 方法2:使用libcorkscrew(已废弃,但适用于旧设备) 注意:从NDK r18开始,libcorkscrew被移除。 方法3:使用libunwind(跨平台) 示例代码: ```cpp #include <libunwind.h> #include <unwind.h> #include <dlfcn.h> #include <android/log.h> #define LOG_TAG "StackUnwind" #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) struct BacktraceState { void** current; void** end; }; static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) { BacktraceState* state = static_cast<BacktraceState*>(arg); uintptr_t pc = _Unwind_GetIP(context); if (pc) { if (state->current == state->end) { return _URC_END_OF_STACK; } else { *state->current++ = reinterpret_cast<void*>(pc); } } return _URC_NO_REASON; } size_t captureBacktrace(void** buffer, size_t max) { BacktraceState state = {buffer, buffer + max}; _Unwind_Backtrace(unwindCallback, &state); return state.current - buffer; } void printStackTrace() { const size_t max = 64; void* buffer[max]; size_t count = captureBacktrace(buffer, max); for (size_t i = 0; i < count; i++) { const void* addr = buffer[i]; const char* symbol = ""; Dl_info info; if (dladdr(addr, &info) && info.dli_sname) { symbol = info.dli_sname; } ALOGE("#%02zd: %p %s", i, addr, symbol); } } ``` 注意:使用libunwind需要链接libdl和libunwind(或使用_Unwind_Backtrace,它通常由编译器提供,但需要链接libgcc或libunwind)。 总结: 推荐使用unwindstack(Android专用,功能更强大),如果目标平台较新(API>=28)。对于旧平台,可以考虑使用libunwind方法。 请根据你的Android版本和NDK版本选择合适的方法。 相关问题: 1. 如何在Android Java打印调用堆栈? 2. 如何将C++层获取的堆栈地址转换为可读的函数名? 3. 在Android Native崩溃处理中如何自动打印堆栈? 4. unwindstack库支持哪些功能?</think>### 在 Android C++打印调用堆栈的方法 在 Android C++打印调用堆栈主要有两种方法,根据不同的 NDK 版本和需求选择: #### 方法 1:使用 `libunwind`(跨平台方案) ```cpp #include <unwind.h> #include <dlfcn.h> #include <android/log.h> #define LOG_TAG "STACKTRACE" #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) struct BacktraceState { void** current; void** end; }; static _Unwind_Reason_Code unwindCallback( struct _Unwind_Context* context, void* arg) { BacktraceState* state = static_cast<BacktraceState*>(arg); uintptr_t pc = _Unwind_GetIP(context); if (pc) { if (state->current == state->end) { return _URC_END_OF_STACK; } else { *state->current++ = reinterpret_cast<void*>(pc); } } return _URC_NO_REASON; } size_t captureBacktrace(void** buffer, size_t max) { BacktraceState state = {buffer, buffer + max}; _Unwind_Backtrace(unwindCallback, &state); return state.current - buffer; } void printStackTrace() { const size_t max = 30; void* buffer[max]; // 获取堆栈地址 size_t count = captureBacktrace(buffer, max); // 解析并打印符号 for (size_t i = 0; i < count; i++) { const void* addr = buffer[i]; const char* symbol = ""; Dl_info info; if (dladdr(addr, &info) && info.dli_sname) { symbol = info.dli_sname; } ALOGE("#%zd: %p %s", i, addr, symbol); } } // 使用示例 void myFunction() { printStackTrace(); // 在此处打印调用堆栈 } ``` #### 方法 2:使用 `unwindstack`(Android 专用方案) ```cpp #include <unwindstack/Unwinder.h> #include <unwindstack/AndroidUnwinder.h> #include <android/log.h> #define LOG_TAG "STACKTRACE" #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) void printNativeStackTrace() { unwindstack::AndroidLocalUnwinder unwinder; std::vector<unwindstack::FrameData> frames; // 获取堆栈帧 if (auto result = unwinder.Unwind(frames, 64); result != unwindstack::ERROR_NONE) { ALOGE("Unwind failed: %d", result); return; } // 打印堆栈信息 for (size_t i = 0; i < frames.size(); i++) { const auto& frame = frames[i]; ALOGE("#%02zu: pc %" PRIPTR " %s", i, frame.pc, unwindstack::FrameToString(frame).c_str()); } } // 使用示例 void criticalFunction() { printNativeStackTrace(); // 打印详细堆栈 } ``` #### 配置步骤(解决头文件错误): 1. **在 CMakeLists.txt 中添加配置**: ```cmake # 设置 unwindstack 头文件路径 include_directories(${ANDROID_NDK}/sources/android/unwindstack/include) # 链接必要库 target_link_libraries(your_target unwindstack dl # 用于 dladdr 函数 ) ``` 2. **在 Android.mk 中添加配置**: ```makefile LOCAL_C_INCLUDES += $(NDK_ROOT)/sources/android/unwindstack/include LOCAL_LDLIBS += -lunwindstack -ldl ``` #### 两种方案对比: | **特性** | `libunwind` | `unwindstack` | |----------|-------------|---------------| | 跨平台性 | ✅ 通用方案 | ❌ Android 专用 | | 符号解析 | 需配合 `dladdr` | ✅ 内置完整解析 | | 需求版本 | 所有 NDK 版本 | NDK r18+ (推荐 r21+) | | 调试信息 | 需要 `-g` 编译选项 | 支持内联函数解析 | | 性能 | ⚡️ 较快 | ⚡️ 高度优化 | > **实际应用建议**: > 1. 对于崩溃处理:在 `signal handler` 中调用堆栈打印 > 2. 添加编译选项:在 `CMakeLists.txt` 中设置 `add_compile_options(-g -fno-omit-frame-pointer)` > 3. 符号化工具:使用 `ndk-stack` 解析日志:`adb logcat | ndk-stack -sym your/obj/path`[^1] ### 示例输出 ``` #00: pc 0x7895a4d8c4 /data/app/com.example.app/lib/arm64/libnative.so (myFunction()+24) #01: pc 0x7895a4d914 /data/app/com.example.app/lib/arm64/libnative.so (callerFunction()+36) #02: pc 0x7895a4d950 /data/app/com.example.app/lib/arm64/libnative.so (main()+64) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值