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

[FAQ15114]如何获取进程的native调用栈?
2016-03-21

内容

[DESCRIPTION]
在native层,在代码的某个地方有时需要知道是谁调用过来的,需要获取此时的native的调用栈。
native的stack frame可能没有用到frame pointer,不能简单的通过FP还原调用栈,不过android本身就有相关的接口可以使用的。
下面一一讲解不同场景下如何获取native调用栈。
 
[SOLUTION]
场景1:在libc里获取当前线程调用栈。
libc有debug_stacktrace.cpp做到这件事,但这文件属于libc_malloc_debug_leak.so的。我们要将该文件添加到libc中。方法如下:
(1). 在alps/bionic/libc/Android.mk里添加 蓝色部分代码:
libc_bionic_src_files := \
......  \
bionic/debug_mapinfo.cpp \
bionic/debug_stacktrace.cpp
 
(2). 在alps/bionic/libc/bionic/libc_init_common.cpp里添加:
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "debug_stacktrace.h" /* add this line */
 
 
 
__LIBC_HIDDEN__ pthread_key_t g_debug_calls_disabled; /* add this line */
 
 
 
void __libc_init_common(KernelArgumentBlock &args)
 
{
 
     ......
 
     { /* add this block */
 
         pthread_key_create(&g_debug_calls_disabled, NULL);
 
         backtrace_startup();
 
     }
 
}
 
(3). 在你需要获取native调用栈的位置调用get_backtrace()函数:
[C/C++] hide
1
2
3
4
5
6
7
8
9
{
 
     uintptr_t Bt[32];
 
 
 
     log_backtrace(Bt, get_backtrace(Bt, 32)); /* 这里将调用栈打印到了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里:
[C/C++] hide
1
2
3
4
5
#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++的桥接):
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
#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末尾添加函数:
[C/C++] hide
1
2
3
4
5
6
7
int dump_cur_nbt( void ) /* add this function */
 
{
 
     return do_tkill(0, current->pid, SIGTRAP);
 
}
 
(2). 在你需要获取native调用栈的位置调用dump_cur_nbt()函数:
[C/C++] hide
1
2
3
4
5
6
7
8
9
{ /* add this block */
 
     extern int dump_cur_nbt( void );
 
 
 
     dump_cur_nbt();
 
}
 
(3). 在libc里添加获取调用栈功能,在alps/bionic/libc/Android.mk里添加 蓝色部分代码:
libc_bionic_src_files := \
......  \
bionic/debug_mapinfo.cpp \
bionic/debug_stacktrace.cpp
 
(4). 在libc里注册SIGTRAP获取调用栈并打印的main log里( 所以要到main log里查看调用栈),在alps/bionic/libc/bionic/libc_init_common.cpp里添加
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "debug_stacktrace.h"
 
 
 
__LIBC_HIDDEN__ pthread_key_t g_debug_calls_disabled;
 
static void DumpCurBt( int Sig, siginfo_t *info, void *unused)
 
{
 
     uintptr_t Bt[32];
 
 
 
     ( void )Sig, ( void )info, ( void )unused;
 
     log_backtrace(Bt, get_backtrace(Bt, 32));
 
}
 
 
 
__LIBC_HIDDEN__ void RegDumpCurBtSig( void )
 
{
 
     struct sigaction action;
 
 
 
     pthread_key_create(&g_debug_calls_disabled, NULL);
 
     backtrace_startup();
 
     memset (&action, 0, sizeof (action));
 
     sigemptyset(&action.sa_mask);
 
     action.sa_sigaction = DumpCurBt;
 
     action.sa_flags = SA_RESTART|SA_SIGINFO;
 
     action.sa_flags |= SA_ONSTACK;
 
     sigaction(SIGTRAP, &action, NULL);
 
}
 
(5). 在alps/bionic/libc/bionic/libc_init_dynamic.cpp和libc_init_static.cpp共2只文件 都需要添加调用RegDumpCurBtSig()的代码:
[C/C++] hide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__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 */
 
         __LIBC_HIDDEN__ void RegDumpCurBtSig( void );
 
 
 
         RegDumpCurBtSig();
 
     }
 
     exit (slingshot(args.argc, args.argv, args.envp));
 
}
 
场景4:在非libc里获取其他线程调用栈。
需要libbacktrace库或libunwind库(L及之后的版本才有该库)支持才行。
libcacktrace库用以下2行代码即可获取调用栈,打印请参考alps/system/core/debuggerd/tombstone.cpp里的代码:
[C/C++] hide
1
2
3
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?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值