如何获取进程的native调用栈?

文摘 Android Kernel MediaTek 2020-08-5 阅读:8712

[DESCRIPTION]

在native层,在代码的某个地方有时需要知道是谁调用过来的,需要获取此时的native的调用栈。

native的stack frame可能没有用到frame pointer,不能简单的通过FP还原调用栈,不过android本身就有相关的接口可以使用的。

下面一一讲解不同场景下如何获取native调用栈。

[SOLUTION]

场景1:在libc里获取当前线程调用栈。

libc里打开libutils.so,调用里面的打印调用栈的方法。方法如下:

(1). alps/system/core/libutils/CallStack.cpp在最尾巴增加:

extern "C" void DumpNativeBt(void)
{
   android::CallStack stack("dump_bt");
}

(2). 在alps/bionic/libc/bionic/libc_init_common.cpp里添加:

typedef void (*DUMP_NATIVE_BT)(void);
static DUMP_NATIVE_BT pDumpNativeBt;

__LIBC_HIDDEN__ void DumpNativeBt(void)
{
    if (pDumpNativeBt)
        pDumpNativeBt();
}

void __libc_init_common(KernelArgumentBlock &args)
{
    ......
    { /* add this block */
      void *handle = dlopen("libutils.so", RTLD_NOW);

      if (handle) {
          pDumpNativeBt = reinterpret_cast<DUMP_NATIVE_BT>(dlsym(handle, "DumpNativeBt"));
          if (!pDumpNativeBt)
            dlclose(handle);
      }
    }
}

(3). 在你需要获取native调用栈的位置调用DumpNativeBt()函数:

{
    __LIBC_HIDDEN__ void DumpNativeBt(void);

    DumpNativeBt(); /* 这里将调用栈打印到了main log */
}

场景2:在非libc里获取当前线程调用栈。

如果你需要获取native调用栈的位置不在libc里,那就需要另外的方法了。有2个库提供了获取native调用栈的方法。

方法1:用libbacktrace库或libcorkscrew库打印调用栈,也可以用libutils库,该库封装了libbacktrace库或libcorkscrew库。

(1). 在你的模块的Android.mk添加libutils动态库:

LOCAL_SHARED_LIBRARIES :=

    ......

    libutils

(2). 在你需要获取native调用栈的位置定义android::CallStack对象,即可将调用栈输出到main log里:

#include <utils/CallStack.h> /* add this line */

android::CallStack stack("xxxx"); /* add this code at necessary place */

注意:上面的代码只能在C++文件里用,如果是C文件,还需如下步骤:

(3). 添加一只cpp文件用于转接:

LOCAL_SRC_FILE :=

    ......

    xxx.cpp

xxx.cpp文件内容如下(C到C++的桥接):

#include <utils/CallStack.h>

extern "C" void DumpNativeBt(void)
{
    android::CallStack stack("xxxx");
}

然后在C文件里调用这个接口DumpNativeBt()。

方法2:L版本开始提供了libunwind库,因此也可以用该库获取调用栈。

具体使用就不细讲,请参考代码:alps/external/libunwind/tests/Gtest-bt.c

场景3:在kernel里获取当前线程调用栈。

在某个驱动里,有时需要知道是哪个native函数调用它的,需要获取当时的调用栈。方法是在你需要的位置发送信号给当前进程,进程收到信号获取调用栈,如下:

(1). 在alsp/kernel/kernel/signal.c末尾添加函数:

int dump_cur_nbt(void) /* add this function */
{
    return do_tkill(0, current->pid, SIGTRAP);
}

(2). 在你需要获取native调用栈的位置调用dump_cur_nbt()函数:

{ /* add this block */
    extern int dump_cur_nbt(void);

    dump_cur_nbt();
}

(3). alps/system/core/libutils/CallStack.cpp在最尾巴增加:

extern "C" void DumpNativeBt(int Sig, void *info, void *unused)
{
    (void)Sig, (void)info, (void)unused;
   android::CallStack stack("dump_bt");
}

(4). 在libc里注册SIGTRAP获取调用栈并打印的main log里(所以要到main log里查看调用栈),在alps/bionic/libc/bionic/libc_init_dynamic.cpp和libc_init_static.cpp共2只文件需要添加:

__noreturn void __libc_init(void* raw_args, void (*onexit)(void) __unused, int (*slingshot)(int, char**, char**), structors_array_t const * const structors)
{
    ......
    { /* add this block */
        typedef void (*DUMP_NATIVE_BT)(int Sig, siginfo_t *info, void *unused);
      struct sigaction action;
      void *handle = dlopen("libutils.so", RTLD_NOW);

      if (handle) {
          pDumpNativeBt = reinterpret_cast<DUMP_NATIVE_BT>(dlsym(handle, "DumpNativeBt"));
          if (pDumpNativeBt) {
                memset(&action, 0, sizeof(action));
              sigemptyset(&action.sa_mask);
              action.sa_sigaction = pDumpNativeBt;
              action.sa_flags = SA_RESTART|SA_SIGINFO;
              action.sa_flags |= SA_ONSTACK;
              sigaction(SIGTRAP, &action, NULL);
            } else {
            dlclose(handle);
        }
      }
    }
    exit(slingshot(args.argc, args.argv, args.envp));
}

场景4:在非libc里获取其他线程调用栈。

需要libbacktrace库或libunwind库(L及之后的版本才有该库)支持才行。

libcacktrace库用以下2行代码即可获取调用栈,打印请参考alps/system/core/debuggerd/tombstone.cpp里的代码:

std::unique_ptr map(BacktraceMap::Create(pid));
std::unique_ptr backtrace(Backtrace::Create(pid, tid, map.get()));

libunwind库就不细讲,请参考代码:或alps/external/libunwind/tests/Gtest-bt.c

场景5:获取其他进程调用栈。

非当前进程就需要用到ptrace了,还需有相同的uid或root权限才行。有几种方法:

方法1:用libbacktrace库。

方法2:用libunwind库。

方法3:通过debuggerd提供的服务抓取调用栈,libcutils库封装好了,直接调用dump_backtrace_to_file()。

方法4:命令行:debuggerd -b <tid>

方法5:用RTT,请参考:[FAQ03827]如果抓一个正在运行的程序的Native Backtrace?

1条评论

    111

    112好吧

    游客 MacOS 1263 天前回复

© 2024 芯缘异码. Powered by Typecho