Android JNI堆栈分析工具简介

导语 :从事Android开发的同事如果在碰到JNI的bug一般都是比较头疼的,因为JNI出错的日志信息比较少,不像Java层的堆栈那样,可以直接看到出错的信息(异常信息)以及出错的类和行数。最近有在分析项目中一个JNI crash,查了一些JNI堆栈分析的方法,涉及到ndk的几个工具的使用,跟大家分享一下。

一、JNI堆栈

为了查看JNI的异常堆栈,我这里模拟了一个出错的代码:

这段代码在testException的13行会有空指针的问题,我们实际运行的时候会碰到这样的堆栈异常:

1. 06-23 15:02:26.772: W/(299): stopped -- fatal signal, send SIGSTOP to process, request.pid:14173
2. 06-23 15:02:26.772: I/DEBUG(299): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
3. 06-23 15:02:26.772: I/DEBUG(299): Build fingerprint: 'OPPO/A33m/A33:5.1.1/LMY47V/1390465867:user/release-keys'
4. 06-23 15:02:26.772: I/DEBUG(299): Revision: '0'
5. 06-23 15:02:26.772: I/DEBUG(299): ABI: 'arm'
6. 06-23 15:02:26.772: I/DEBUG(299): pid: 14173, tid: 14173, name: xample.hellojni  >>> com.example.hellojni <<<
7. 06-23 15:02:26.772: I/DEBUG(299): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
8. 06-23 15:02:26.782: I/DEBUG(299):     r0 ab0173d0  r1 fff7690c  r2 00000001  r3 00000000
9. 06-23 15:02:26.782: I/DEBUG(299):     r4 ab0173d0  r5 73f18f38  r6 12c1a190  r7 12c79000
10. 06-23 15:02:26.782: I/DEBUG(299):     r8 00000000  r9 ab016e00  sl 00000000  fp 12c5b1c0
11. 06-23 15:02:26.782: I/DEBUG(299):     ip 66127bf9  sp fff768f8  lr 66127c01  pc 66127bf4  cpsr 200b0030
12. 06-23 15:02:26.782: I/DEBUG(299): backtrace:
13. 06-23 15:02:26.782: I/DEBUG(299):     #00 pc 00000bf4  /data/app/com.example.hellojni-1/lib/arm/libhello-jni.so (testException+3)
14. 06-23 15:02:26.782: I/DEBUG(299):     #01 pc 00000bfd  /data/app/com.example.hellojni-1/lib/arm/libhello-jni.so (Java_com_example_hellojni_HelloJni_stringFromJNI+4)
15. 06-23 15:02:26.782: I/DEBUG(299):     #02 pc 000001df  /data/dalvik-cache/arm/data@app@com.example.hellojni-1@base.apk@classes.dex

这样一堆东西我们是看不出是哪里发生了错误,注意到这里:

#00 pc 00000bf4  /data/app/com.example.hellojni-1/lib/arm/libhello-jni.so (testException+3)
#01 pc 00000bfd  /data/app/com.example.hellojni-1/lib/arm/libhello-jni.so (Java_com_example_hellojni_HelloJni_stringFromJNI+4)
#02 pc 000001df  /data/dalvik-cache/arm/data@app@com.example.hellojni-1@base.apk@classes.dex

从上面的片段中我们能看到arm汇编代码调用命令的地址,头两行是我们自己的so文件相关的(libhello- jni.so),分别是0bfd->0bf4,出错的地方是0bf4,能通过这些调用信息找到对应的代码行数吗?答案肯定是可以的,当然前提是我们有Native的源码,以下的工具都是我们有源码的前提。

从JNI堆栈分析代码对应的调用栈

NDK提供了一个工具帮助我们定位汇编命令对应的代码文件以及行数:arm-linux-androideabi-addr2line,工具的位置如下:

输入如下命令:

arm-linux-androideabi-addr2line -e F:\hello-jni\obj\local\armeabi\libhello-jni.so 0bf4 0bfd

-e是jni编译过程中obj目录下的中间so文件,这里要注意下,不能用libs目录下的so文件。 后面跟上地址信息,这里需要知道两个地址对应的行数,所以就有两个,如果堆栈比较深,可以跟多个地址信息;

结果如下:

从结果我们可以看到,出错的地方是hello-jni.cpp的第13行,确实就是我们出现空指针的地方;

二、获取汇编代码

上面的例子中,我们在日志中看到了出错的汇编代码位置,但是我们是不知道对应的汇编代码,以及函数的,下面的一个工具能够帮助我们反编译so获取汇编代码:

arm-linux-androideabi-objdump -S -D F:\hello-jni\libs\armeabi\libhello-jni.so > C:\Users\stevcao\Desktop\jni2.txt

这里的so文件可以libs目录下的,也可以是obj目录下的;生成的反编译文件会有所不一样,obj目录的信息会详细点,包括源文件的代码对应的汇编代码以及注释都会有;libs目录下的so文件会只有汇编代码。下面分别贴出:

obj目录下so dump结果

libs目录下的so dump结果

三、ndk-stack工具

工具位置:

ndk-stack可以直接从日志中分析出堆栈的错误信息,能够直接帮助我们定位到错误的位置,一步到位;

我们可以直接把logcat中的错误信息输入给ndk-stack,也可以使用ndk-stack来分析crash的日志(比如平台上报的crash数据);

ndk-stack -sym F:\hello-jni\obj\local\armeabi\

或者:

ndk-stack -sym F:\hello-jni\obj\local\armeabi\ -dump crash.log

用ndk-stack对本文中出现的日志分析,输入如下信息,和用addr2line工具得到的结果是一样的:

********** Crash dump: **********
Build fingerprint: 'OPPO/A33m/A33:5.1.1/LMY47V/1390465867:user/release-keys'
pid: 14173, tid: 14173, name: xample.hellojni  >>> com.example.hellojni <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Stack frame 06-23 15:02:26.782: I/DEBUG(299):     #00 pc 00000bf4  /data/app/com.example.hellojni-1/lib/arm/libhello-jni.so (testException+3): Routine testException at F:/hello-jni//jni/hello-jni.cpp:13
Stack frame 06-23 15:02:26.782: I/DEBUG(299):     #01 pc 00000bfd  /data/app/com.example.hellojni-1/lib/arm/libhello-jni.so (Java_com_example_hellojni_HelloJni_stringFromJNI+4): Routine Java_com_example_hellojni_HelloJni_stringFromJNI at F:/hello-jni//jni/hello-jni.cpp:20
Stack frame 06-23 15:02:26.782: I/DEBUG(299):     #02 pc 000001df  /data/dalvik-cache/arm/data@app@com.example.hellojni-1@base.apk@classes.dex

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏XAI

【定制化图像开放平台】入门实例之手写数字模型训练

本帖主要用手写数字为例进行一个简单入门实例总结(非官方) 平台网站:http://ai.baidu.com/customize/app/model/ 定制化图像...

38616
来自专栏coding for love

在线商城项目11-商品列表页的排序实现

请求后台接口会带上三种排序参数default,priceDown和priceUp。另外,如果不带参数,我们默认排序也是default。 这里,我们做一个简单的...

362
来自专栏游戏开发那些事

【随笔】关于算法竞赛中使用文件输入输出和文件名的规定等问题

算法竞赛对文件名有着严格的规定,包括程序名和输入输出文件名,不要使用绝对路径或者相对路径。

963
来自专栏Python爬虫与算法进阶

一个通用爬虫思路(Python3)

我们是谁? 萌新! 我们要什么? 学习爬虫! 什么时候要? 天天要! 其实一个爬虫无非就三步:下载数据、解析数据、保存数 ? 本文提供一个代码示例,分别展示这三...

2574
来自专栏人工智能LeadAI

TensorFlow的Debugger调试实例

之前有翻译整理过关于TensofFlow的Debugger的简单教程,具体内容见这里(https://www.jianshu.com/p/9fd237c7fda...

3619
来自专栏difcareer的技术笔记

ODEX格式及生成过程

Apk在安装(installer)时,就会进行验证和优化,目的是为了校验代码合法性及优化代码执行速度,参见Dalvik Optimization and Ver...

492
来自专栏人工智能LeadAI

TensorFlow的Debugger调试实例

之前有翻译整理过关于TensofFlow的Debugger的简单教程,具体内容见这里。这次用自己实际的例子,来简要的做个使用介绍。 首先是代码遇到了问题,训练过...

3276
来自专栏地方网络工作室的专栏

Shell 命令行,实现一个获取任意位数的随机密码的脚本

Shell 命令行,实现一个获取任意位数的随机密码的脚本 每次我们想要获得一个密码的时候都很头疼,于是我之前自己用nodejs写了一个 Shell 脚本。这两天...

1836
来自专栏Seebug漏洞平台

CVE-2015-1641 Word 利用样本分析

00 引 子 本文我们将通过一个恶意文档的分析来理解漏洞 CVE-2015-1641(MS15-033)的具体利用过程,以此还原它在现实攻击中的应用。就目前来...

3028
来自专栏我和PYTHON有个约会

爬虫0060:scrapy快速入门爬虫高级操作:Scrapy framework

官方网站:https://scrapy.org/,打开官方网站,可以看到一段关于scrapy的描述

751

扫码关注云+社区