android native 调试 打印调用栈

本文详细介绍在不同Android版本中,如何使用libunwind.so等库文件进行调用栈打印,包括5.0以上版本的替代方案及具体实现代码。
在调试 android 某些应用时,需要打印调用栈, 但是高版本的5.0 
以上已经去掉了libcorkscrew.so 和 libcutils.so 两个库,
改用其他的google 库文件,但是可以使用andorid 4.4 , 4.3 系统使用。
使用时可以直接调用 getCallStack()该方法即可。
android 6.0 可用 

typedef int (*Unw_BackTrace_Func)(void**, int);
void print_backtrace() {
    static Unw_BackTrace_Func unw_backtrace = NULL;
    if (!unw_backtrace) {
        void *hanle = dlopen("libunwind.so", RTLD_NOW);
        unw_backtrace = (Unw_BackTrace_Func)dlsym(hanle, "unw_backtrace");
    }

    void *buffer[32] = { 0 };
    int n = unw_backtrace((void**)&buffer, 32);
    for(int i = 1; i < n; i++) {
        const char *file = "\t\t\t\t";
        const char *symbol = "\t\t\t\t";
        Dl_info info;
        if (dladdr(buffer[i], &info)) {
            if (info.dli_sname) {
                symbol = info.dli_sname;
            }
            if (info.dli_fname) {
                file = info.dli_fname;
            }
        }
        __android_log_print(ANDROID_LOG_INFO, TAG, "uuu:#%02d: %p,%s,%s", i, buffer[i], symbol, file);
    }
}



/********************************hh********************************/

#include <utils/CallStack.h>
#include <utils/Trace.h>
#include <dlfcn.h>
#include <unwind.h>
#include <dlfcn.h>
#define MAX_DEPTH                       66
#define MAX_BACKTRACE_LINE_LENGTH   800
#define PATH "/system/lib/libcorkscrew.so"
#define PATH_uu "/system/lib/libcutils.so"

typedef ssize_t (*unwindFn)(backtrace_frame_t *, size_t, size_t);

typedef void (*unwindSymbFn)(const backtrace_frame_t *, size_t,
                             backtrace_symbol_t *);

typedef void (*unwindSymbFreeFn)(backtrace_symbol_t *, size_t);

static void *gHandle = NULL;

int getCallStack(void) {

    ssize_t i = 0;
    ssize_t result = 0;
    ssize_t count;
    backtrace_frame_t mStack[MAX_DEPTH];
    backtrace_symbol_t symbols[MAX_DEPTH];

    unwindFn unwind_backtrace = NULL;
    unwindSymbFn get_backtrace_symbols = NULL;
    unwindSymbFreeFn free_backtrace_symbols = NULL;


    if (gHandle == NULL) {
        gHandle = dlopen(PATH_uu, RTLD_NOW);
    }

    // open the so.
    if (gHandle == NULL) {
        gHandle = dlopen(PATH, RTLD_NOW);
    }

    // get the interface for unwind and symbol analyse
    if (gHandle != NULL) {
        unwind_backtrace = (unwindFn) dlsym(gHandle, "unwind_backtrace");
    } else{
        LOGD("tian>>>dleerr55>%s", dlerror());
    }

    if (gHandle != NULL) {
        get_backtrace_symbols = (unwindSymbFn) dlsym(gHandle, "get_backtrace_symbols");
    } else{
        LOGD("tian>>>dleerr444>%s", dlerror());
    }

    if (gHandle != NULL) {
        free_backtrace_symbols = (unwindSymbFreeFn) dlsym(gHandle,
                                                          "free_backtrace_symbols");
    } else{
        LOGD("tian>>>dlee6666rr>%s", dlerror());
    }

    if (!gHandle || !unwind_backtrace || !get_backtrace_symbols ||
        !free_backtrace_symbols) {
        LOGD("tian Error! cannot get unwind info: handle:%p %p %p %p",
             gHandle, unwind_backtrace, get_backtrace_symbols, free_backtrace_symbols);
        return result;
    }

    count = unwind_backtrace(mStack, 1, MAX_DEPTH);
    LOGD("tian>>>dleecount>%d", count);
    get_backtrace_symbols(mStack, count, symbols);

    for (i = 0; i < count; i++) {
        char line[MAX_BACKTRACE_LINE_LENGTH];

        const char *mapName = symbols[i].map_name ? symbols[i].map_name : "<unknown>";
        const char *symbolName = symbols[i].demangled_name ? symbols[i].demangled_name :
                                 symbols[i].symbol_name;
        size_t
        fieldWidth = (MAX_BACKTRACE_LINE_LENGTH - 80) / 2;

        if (symbolName) {
            uint32_t pc_offset = symbols[i].relative_pc - symbols[i].relative_symbol_addr;

            if (pc_offset) {
                snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d  pc %08x  %.*s (%.*s+%u)",
                         i, symbols[i].relative_pc, fieldWidth, mapName,
                         fieldWidth, symbolName, pc_offset);
            } else {
                snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d  pc %08x  %.*s (%.*s)",
                         i, symbols[i].relative_pc, fieldWidth, mapName,
                         fieldWidth, symbolName);
            }
        } else {
            snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d  pc %08x  %.*s",
                     i, symbols[i].relative_pc, fieldWidth, mapName);
        }
        LOGD("tian>>>555>%s", line);
    }
    free_backtrace_symbols(symbols, count);

    return result;
}

 

5.1 手机使用方法。 

 

typedef int (*Unw_BackTrace_Func)(void**, int);
void print_backtrace() {
    static Unw_BackTrace_Func unw_backtrace = NULL;
    if (!unw_backtrace) {
        void *hanle = dlopen("libunwind.so", RTLD_NOW);
        unw_backtrace = (Unw_BackTrace_Func)dlsym(hanle, "unw_backtrace");
    }

    void *buffer[32] = { 0 };
    int n = unw_backtrace((void**)&buffer, 32);
    for(int i = 1; i < n; i++) {
        const char *file = "\t\t\t\t";
        const char *symbol = "\t\t\t\t";
        Dl_info info;
        if (dladdr(buffer[i], &info)) {
            if (info.dli_sname) {
                symbol = info.dli_sname;
            }
            if (info.dli_fname) {
                file = info.dli_fname;
            }
        }

        LOGI("#%02d: %p \t %s \t %s", i, buffer[i], symbol, file);
    }
}
<think>好的,我需要帮助用户在Android Native层使用CallStack打印ALOGD函数的调用堆栈。根据用户提供的引用和之前的回答,我需要综合这些信息来给出解决方案。 首先,用户提到他们想打印ALOGD函数的调用堆栈。ALOGD是Android的日志宏,通常用于调试输出。要追踪ALOGD的调用堆栈,意味着用户希望每当ALOGD被调用时,记录下当时的调用路径。这可能涉及到hook或重写ALOGD宏,或者在调用ALOGD的地方插入堆栈打印代码。 根据之前的回答,使用CallStack类是一种方法。但用户现在想具体针对ALOGD。我需要考虑如何将CallStack集成到ALOGD的调用中。可能的做法是自定义一个宏,替代ALOGD,该宏在记录日志的同时打印堆栈。 参考用户提供的引用[1],其中展示了在Java层通过Frida修改方法实现来打印堆栈。类似地,在Native层,可能需要拦截ALOGD的调用。不过,ALOGD是一个宏,展开后调用__android_log_print函数。因此,拦截__android_log_print可能更直接。但用户希望使用CallStack,而不是动态注入的方式。 另一个方法是,在代码中所有使用ALOGD的地方手动添加CallStack的调用。但这需要修改所有ALOGD出现的代码,可能不现实。因此,更好的方法可能是通过宏重定义来在调用ALOGD时自动捕获堆栈。 例如,可以定义一个宏,比如ALOGD_STACK,当使用该宏时,既记录日志又打印调用堆栈。或者重定义ALOGD宏本身,但这可能会影响其他地方的日志调用,需要谨慎处理。 另外,需要考虑如何确保CallStack的正确使用,包括头文件引入、依赖库的链接,以及兼容不同Android版本的问题。用户之前的问题提到了Android 11及以上需要使用unwindstack库,这可能也需要在解决方案中提及。 总结步骤可能包括: 1. 包含必要的头文件,如utils/CallStack.h。 2. 重定义ALOGD宏,或在调用ALOGD的地方插入CallStack代码。 3. 处理兼容性问题,尤其是Android版本差异。 4. 配置构建文件(Android.mk或Android.bp)以包含必要的库。 5. 处理可能的符号解析问题,确保堆栈信息正确显示。 需要注意,直接拦截ALOGD可能涉及修改系统日志库,这在没有源码的情况下可能困难。因此,更可行的方法是在代码中显式添加CallStack调用,或者通过宏扩展来包裹ALOGD,添加堆栈打印功能。 需要验证这些步骤的正确性,确保在调用ALOGD时确实能触发堆栈打印,并且不会引起性能问题或兼容性问题。同时,提供示例代码和配置说明,帮助用户实际实现。</think>### **在Android Native层捕获ALOGD调用堆栈的完整方案** --- #### **一、核心实现原理** **目标**:在每次调用`ALOGD()`时自动捕获并输出调用堆栈 **实现思路**:通过宏重定义将CallStack功能注入`ALOGD`宏,实现日志与堆栈的同步输出 --- #### **二、具体实现步骤** 1. **头文件定义(关键步骤)** 在需要使用的代码文件顶部添加以下宏定义: ```cpp #include <utils/CallStack.h> using namespace android; // 重定义ALOGD宏(需置于所有头文件引用之后) #undef ALOGD #define ALOGD(fmt, ...) \ do { \ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__); \ CallStack __debug_stack(LOG_TAG); \ __debug_stack.update(1, 8); /* 跳过当前帧,最多捕获8层 */ \ __debug_stack.log("ALOGD_STACK", ANDROID_LOG_DEBUG); \ } while(0) ``` 2. **代码植入位置要求** - 必须确保宏定义在所有`#include <log/log.h>`语句**之后** - 推荐在模块入口文件(如`.cpp`文件)或公共头文件中定义 3. **Android.bp配置示例** ```makefile cc_library_shared { name: "your_module", srcs: ["your_file.cpp"], shared_libs: [ "liblog", "libutils" // CallStack依赖的核心库[^1] ], cflags: [ "-DLOG_TAG=\"YOUR_TAG\"", // 定义日志TAG "-fno-inline", // 禁止内联优化 ], } ``` --- #### **三、兼容性增强方案** | **Android版本** | **适配策略** | |------------------|-----------------------------------------------------------------------------| | ≥ 11 (Android R) | 使用`unwindstack`替代CallStack,避免系统库变更导致的崩溃 | | ≤ 10 | 标准CallStack实现 | **Android 11+ 适配示例**: ```cpp #include <unwindstack/LocalUnwinder.h> #define ALOGD(fmt, ...) \ do { \ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__); \ unwindstack::LocalUnwinder unwinder; \ if (unwinder.Unwind(/*max_depth=*/12)) { \ for (const auto& frame : unwinder.frames()) { \ __android_log_print(ANDROID_LOG_DEBUG, "UNWIND_STACK", \ "#%02d pc %016" PRIx64 " %s", \ frame.num, frame.pc, frame.function_name.c_str()); \ } \ } \ } while(0) ``` --- #### **四、生产环境优化建议** 1. **条件编译控制** 通过编译开关控制堆栈输出: ```cpp #ifdef ENABLE_ALOGD_STACK #define ALOGD(fmt, ...) \ /* 完整实现 */ #else #define ALOGD(fmt, ...) \ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__) #endif ``` 2. **性能敏感场景优化** ```cpp static std::atomic<bool> g_stack_debug_enabled{false}; #define ALOGD(fmt, ...) \ do { \ __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__); \ if (g_stack_debug_enabled.load()) { \ CallStack __debug_stack(LOG_TAG); \ __debug_stack.update(1, 8); \ __debug_stack.log(); \ } \ } while(0) ``` --- #### **五、常见问题排查** 1. **符号显示为`<unknown>`** - **修复方案**: 在编译选项中添加调试符号: ```makefile android: { compileSdkVersion: 30, ndk: { cFlags += "-g -funwind-tables" // 保留调试信息 } } ``` 2. **堆栈深度不足** - 调整`update()`参数: ```cpp __debug_stack.update(1, 15); // 捕获15层堆栈 ``` 3. **系统库冲突** - 若出现`libutils`链接错误,尝试: ```makefile shared_libs: ["libutils:32"], // 指定32位版本 ``` --- ### **相关问题** 1. 如何过滤特定线程的ALOGD堆栈输出? 2. 怎样将CallStack信息与logcat日志自动关联? 3. 在Android NDK中如何实现堆栈信息的符号化解析? --- **引用说明** [^1]: Android官方CallStack实现文档 [^2]: Android Framework实战视频中的Native层堆栈打印技术
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值