小工具知识网

当前位置: 首页 > 行业知识 > Android framework C/C++中打印堆栈

行业知识

Android framework C/C++中打印堆栈

2026-11-06 19:41:09 来源:互联网转载

有时候我们需要有这样的需求,需要在某些函数中打印函数的调用路径,经过查询主要有四种方式实现。

1. CallStack堆栈回溯

Android中提供了堆栈打印的类CallStack.cpp,此类在的路径为/system/core/libutils/CallStack.cpp,在不同的Android版本中,此类所在的so库不同,具体如下表所示。

Android版本所在so库依赖方法
Andorid O以上libutilscallstackAndroid.mk: LOCAL_SHARED_LIBRARIES里包含libutilscallstack;Android.bp: shared_libs里包含libutilscallstack
Android OlibutilsAndroid.mk: LOCAL_SHARED_LIBRARIES里包含libutils; Android.bp: shared_libs里包含libutils
Android N 以前版本libutilsAndroid.mk: LOCAL_SHARED_LIBRARIES里包含libutils

具体的使用步骤:

(1)包含头文件

#include "utils/CallStack.h"

(2)引用动态库

根据所在的so库进行依赖使用,如上表所示。

(3)打印堆栈

android::CallStack cs("tagName")

注意:此种打印堆栈的方法,并不通用。比如自己编写的普通APP中无法找到CallStack头文件,还有在系统源代码中有些地方无法依赖libutilscallstack.so 、libutils.so库。

2. backtrace堆栈回溯

此方法是参照CallStack.cpp堆栈打印的实现,查看CallStack.cpp的源代码可以发现,实现打印堆栈的方法是在backtrace/Backtrace.h头文件中,对应的实现文件是在/system/core/libbacktrace目录下,通过查看Android.dp文件可知,最终/system/core/libbacktrace目录下的文件会编译成libbacktrace.so文件。由此可知想要打印堆栈信息,可以借助libbacktrace.so文件。

具体的使用步骤:

(1)包含头文件

#include 

(2)引用动态库

Android.mk: LOCAL_SHARED_LIBRARIES里包含libbacktraceAndroid.bp: shared_libs里包含libbacktrace

(3)打印堆栈

Vector mFrameLines;std::unique_ptr backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, -1));if (!backtrace->Unwind(0)) {ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);}for (size_t i = 0; i NumFrames(); i++) {mFrameLines.push_back(backtrace->FormatFrameData(i).c_str());}

3. _Unwind_Backtrace堆栈回溯

具体的使用见下面的代码

#include #include #include #include #include #include namespace {    struct BacktraceState    {        void** current;        void** end;    };    static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)    {        BacktraceState* state = static_cast(arg);        uintptr_t pc = _Unwind_GetIP(context);        if (pc) {            if (state->current == state->end) {                return _URC_END_OF_STACK;            } else {                *state->current++ = reinterpret_cast(pc);            }        }        return _URC_NO_REASON;    }}size_t captureBacktrace(void** buffer, size_t max){    BacktraceState state = {buffer, buffer + max};    _Unwind_Backtrace(unwindCallback, &state);  //调用_Unwind_Backtrace方法,回调unwindCallback    return state.current - buffer;}void dumpBacktrace(std::ostream& os, void** buffer, size_t count){    for (size_t idx = 0; idx  调用堆栈中,在函数内部实际执行的位置(地址)            if(info.dli_sname) {                symbol = info.dli_sname; //一个指针,指向与指定的address最接近的符号的名称。该符号要么带有相同的地址,要么是带有低位地址的最接近符号。函数名称                fname = info.dli_fname; //一个指针,指向包含address的加载模块的文件名。so文件地址                baseAddr = info.dli_fbase; //,载模块的句柄。该句柄可用作dlsym() 的第一个参数。so在内存中的地址                sAddr = info.dli_saddr; //最接近符号的实际地址。对于代码符号,它包含最接近代码符号的OPD(正式Plabel 描述符)的地址。在内存中函数的相对地址            }else {                fname = info.dli_fname;                baseAddr = info.dli_fbase;                sAddr = info.dli_saddr;            }            //os             os     const size_t max = 100; // 调用的层数    void* buffer[max];    std::ostringstream oss;    dumpBacktrace(oss, buffer, captureBacktrace(buffer, max));    __android_log_print(ANDROID_LOG_INFO, "backtrace", "%s", oss.str().c_str());    return oss.str().c_str();}    lr = *(uint64_t *) (fp + sizeof(char *));    Dl_info info;    if (!dladdr((void *) lr, &info)) {        break;    }    if (!info.dli_fname) {        break;    }    LOG("backtrace pc = %p,module = %s", lr, info.dli_fname);    fp = *((uint64_t *) fp);}

上一篇:怎么连接对方MYSQL服务器?

下一篇:怎么设置软件代理服务器?