利用系统提供的崩溃日志解Native层Bug
2019独角兽企业重金招聘Python工程师标准>>>
对Android开发者来讲,尤其是使用NDK编写Native层代码的开发者,在编码过程中通常会碰到各种各样的问题。追踪问题的方式有很多,除了在代码中添加日志,来观察程序运行过程中产生的异常外,对崩溃后产生的日志进行分析也是一种重要的定位问题的方式。
Android系统自带一个非常实用的Native层代码崩溃监测进程debuggerd。该进程可以监听到应用程序的崩溃,并将崩溃后的信息输出到文件中,供开发人员调试分析。在开发过程中,我们可以通过logcat来查看debuggerd为我们生成的应用程序崩溃日志。
接下来,就跟大家来探讨一下如何利用debuggerd为我们生成的应用程序崩溃日志来定位并解决程序中存在的问题。
首先通过这个例子来介绍如何使用系统提供的崩溃日志。代码如图1所示。
图1 代码示例
可以看到,JNI_OnLoad函数中除了做一些常规性的操作以外,有这样两行特殊的代码:
[代码]java代码:
?
12 |
int *p = 0 ; *p = 1 ;
|
p”是个int*类型的空指针,而后面的这个赋值操作是向这个空指针的位置写入1。毫无疑问,这个典型的空指针赋值操作会造成应用程序直接崩溃。
将以上代码编译成SO文件,并运行APP。发现程序产生了崩溃,通过logcat就可以看到如图2所示的崩溃日志:
图2 系统生成的崩溃日志
从这一段崩溃日志中,我们可以看到:
(1)崩溃手机的Build fingerprint是:
'Huawei/H30-T00/hwH30-T00:4.4.2/HuaweiH30-T00/C00B246SP02:user/ota-rel-keys,release-keys'。
(2)崩溃发生的进程进程号pid为14077,线程号tid为14077,进程名称为com.example.nativecrash。
(3)signal告诉我们:崩溃信号为SIGSEGV,当进程中执行了一个无效的内存引用时会触发这个类型的崩溃信号。fault addr代表发生崩溃的地址为0,这与代码中的空指针赋值相吻合。
(4)signal下面的4行信息其实就是进程崩溃时所有寄存器的一个快照。
(5)最后是backtrace,这部分对于定位产生崩溃的原因是非常重要的,它反应了进程崩溃时的函数调用栈,通过它,就可以知道在哪里触发了代码崩溃,稍后会详细分析一下这个部分。
简单地解析了崩溃日志中的信息之后,我们就来看看怎么定位崩溃产生的位置?从上面的分析可以知道backtrace是接下来的分析重点。
图3 backtrace中函数调用过程
如图3所示,backtrace其实就是一个函数调用栈。最上面一行是崩溃发生的地方。从第一行可以看出:崩溃发生在自己so中的JNI_OnLoad函数里面,崩溃点是在so的偏移为0x47d2的地方(需要注意的是这个偏移是指二进制文件中的偏移,并非源码中的偏移)。
那么JNI_OnLoad函数是由谁调用的呢?从第二行就可以知道是系统的libdvm.so中的dvmLoadNativeCode这个函数调用了该so中的JNI_OnLoad函数。综上可以看出,backtrace中的函数调用关系其实是由下而上,如图3中的箭头所示。
虽然已经定位崩溃发生在so的JNI_OnLoad函数中,但是依然不知道具体发生在源码文件的哪一行。为了能够定位到崩溃发生在源码文件的哪一行,故需要利用Android提供的工具:addr2line。利用该工具可以将backtrace中显示的崩溃点与源码联系起来,这样就能还原崩溃在源码中的位置。具体的还原步骤如下:
(1)取obj目录下的libtest.so
我们知道,在利用ndk-build编译so的时候会在jni的同级目录下产生libs、obj两个目录,如图4所示:
图4 libs、obj目录
这两个目录下面各有一个so,但是为什么要取obj目录的,而不取libs目录里面的呢?这是因为ndk-build在生成so的时候,会生成两份,一份是包含调试信息的so,放在obj目录下面;一份是不包含调试信息的so,放在libs目录下面。为了利用addr2line工具还原崩溃点在源码中的位置,我们必须使用包含调试信息的so,所以需要obj目录中的so。
(2)在cmd中运行下面的命令
[代码]java代码:
?
1 |
addr2line -f -e libtest.so 0x47d2
|
其中,0x47d2就是我们之前所看到的崩溃点。
运行完可以看到如图5所示的结果:
图5 addr2line运行结果
从以上结果中可以看到:崩溃发生在JNI_OnLoad函数中,崩溃点对应的源码位置在test.cpp的第17行。那么结果真的是在17行吗,我们可以验证一下:
图6 C源码中的崩溃点
如图6所示,空指针赋值的操作正好就是在第17行,正是这个操作导致了崩溃。
至此,我们就基本知道了如何利用系统提供的崩溃日志来定位和解决Native层代码的崩溃了。但是实际的情况比我们想象要复杂得多。以上方式只适用于开发者在调试或测试阶段来获取崩溃日志,但当开发者将APP分发出去之后,APP运行在用户手机上时发生了崩溃,开发者是看不到日志的。所以通常开发者通过自己搭建崩溃日志服务平台,或集成第三方SDK的形式获取应用崩溃信息。每种方式各有利弊,今天介绍一个免SDK集成的工具——360加固保
360加固保推出了“崩溃日志”功能主要特点有:
(1)免SDK集成
目前有不少第三方Crash Report SDK,开发者需要在自己的apk源码中集成SDK,那么作为一个开发者,他的开发成本就相应的增加了很多:
首先,开发者必须学会如何使用SDK,阅读SDK文档,了解其中的API接口,之后才能调用SDK中的API。除此之外,开发者必须得在Android Studio或者eclipse等IDE中配置好SDK的一些使用参数等,但是,这有时并不是一件简单的事,需要一定开发成本。
再次,由于SDK本身也会存在一些问题(比如Bug,需要优化等),所以必然需要升级更新,那么开发者为了能够使用更优质的服务,就必须紧跟SDK的版本升级,不断在自己的源码中更改SDK,这其实是比较麻烦的事。
为了免去开发者的开发成本,360加固保推出免SDK应用崩溃日志分析服务。无需任何开发过程,只需上传APP进行应用加固,2分钟左右,即可掌握最全面的应用崩溃信息。同时,APP具备了防反编译、防破解的能力,轻松提升APP安全性。
(2)提供Native层崩溃日志收集功能,且兼容性强
由于Native层崩溃日志收集的功能涉及Android系统较低层的一些系统机制,其内容较复杂,实现难度较大。所以目前市场上能够提供针对Native层的崩溃收集功能的厂商并不多。即使能够提供Native层的崩溃日志收集,在兼容性方面也存在各种问题。
360加固保自己实现了一套收集崩溃日志的接口,并不依赖系统本身提供的崩溃日志相关接口。这样,即便某款手机上的Android系统是基于手机厂商特殊定制的,不提供崩溃日志相关接口,360加固保也能正常收集到此款手机上面的崩溃信息。
(3)Native层收集的崩溃数据更加全面
与其他第三方应用崩溃信息厂商相比,360加固保收集的Native层崩溃日志数据更全面,更方便开发者根据这些崩溃信息进行Bug追踪和修复。360加固保收集的崩溃日志信息见图7至图11。
图8 加固保收集的应用崩溃信息
图8 加固保收集的应用崩溃信息
图9 加固保收集的应用崩溃信息
图10 加固保收集的应用崩溃信息
图11 360加固保收集的应用崩溃信息
(4)Native层崩溃日志模拟Android系统收集的崩溃日志,开发者阅读时更方便
Android系统本身自带崩溃日志收集功能,图12至图14是Android系统为崩溃进程收集的崩溃日志详情:
图12 Android系统收集的崩溃日志详情
图13 Android系统收集的崩溃日志详情
图14 Android系统收集的崩溃日志详情
通过与360加固保收集到的崩溃信息对比,可以发现,360加固保提供的崩溃日志详情和Android系统收集的基本没有区别,甚至比某些手机系统上面收集的崩溃日志还要详细。由于开发者在平时调试Native层代码时,习惯看到的是Android系统打出来的Log,所以如果与Android系统收集上来崩溃日志详情的相似,可以说开发者看上去会非常亲切,免去开发者重新学习如何查看的烦恼。
(5)所需要获取的apk权限最少
因为收集到的应用崩溃日志需要上传至服务器,所以APP必须有一些网络相关权限。图15是360加固保所需获取的apk权限,只有三个。更少的权限对用户来讲,就意味着少了很多的安全风险;对于开发者来讲,也不必因为收集崩溃信息而添加过多的apk本身并不使用的权限。
图14 360加固保所需获取的apk权限
(6)Native层支持多进程的崩溃日志收集
360加固保提供的崩溃日志分析服务,无论是Android中动态链接库so中fork出来的进程,还是Service组件的进程,凡是Native层出现了崩溃,都会被Native层崩溃信息收集功能察觉,并生成相应的崩溃信息。
原文链接:http://www.apkbus.com/blog-705730-62583.html
作者:白衣染霜花
链接:http://www.imooc.com/article/247064
来源:慕课网
转载于:https://my.oschina.net/u/4000302/blog/3027721
利用系统提供的崩溃日志解Native层Bug相关推荐
- UIView封装动画--iOS利用系统提供方法来做关键帧动画
iOS利用系统提供方法来做关键帧动画 ios7以后才有用. /*关键帧动画options:UIViewKeyframeAnimationOptions类型*/[UIView animateKeyfra ...
- Linux系统提供的time详解
Linux系统提供了很多关于time的处理API,这些API各自的功能和使用场景都有所不同.对于初学者有时会混淆它们,对于API的具体含义理解不到位.本文总结各类time相关的API的使用方式. 分类 ...
- Android7 WIFI系统 PNO机制流程详解和隐藏BUG修改
WIFI启动过程,WifiStateMachine加载驱动固件,连接上wpa_s的socket并检查好配置文件后,进入到DisconnectedState状态.在DisconnectedState状态 ...
- [转载]Android7 WIFI系统 PNO机制流程详解和隐藏BUG修改
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/zhangdaxia2/article/ ...
- iOS应用崩溃日志分析 iOS应用崩溃日志揭秘
转自:http://www.raywenderlich.com/zh-hans/30818/ios%E5%BA%94%E7%94%A8%E5%B4%A9%E6%BA%83%E6%97%A5%E5%BF ...
- iOS崩溃日志符号化 UUID获取
1. 符号表是什么? 符号表就是指在Xcode项目编译后,在编译生成的二进制文件.app的同级目录下生成的同名的.dSYM文件. .dSYM文件其实是一个目录,在子目录中包含了一个16进制的保存函数地 ...
- iOS 崩溃日志在线符号化实践
1. 什么是符号化? 在日常开发中,应用难免会发生崩溃.通常,我们直接从用户导出来的崩溃日志都是未符号化或者部分符号化的,都是一堆十六进制内存地址的集合,可读性较差.未符号化或者部分符号化的崩溃日志对 ...
- 理解崩溃和崩溃日志(WWDC 2018 session 414)
WWDC 2018 session 414: Understanding Crashes and Crash Logs 每个人在写代码的时候,或多或少都会犯错.有的错误就会导致程序崩溃,这非常影响用户 ...
- 如何利用ffmpeg提供的API函数进行多媒体文件的解封装
多媒体已经无处不在,程序员必须知道的一些多媒体封装知识 如何利用ffmpeg提供的API函数进行多媒体文件的解封装. 上一篇文章我们搭好了环境并编译出所需的ffmpeg库,本篇我们讨论如何利用ffmp ...
最新文章
- 微软的平板电脑_关于微软轻便版平板电脑Surface Go,你想知道的一切细节都在这了...
- lua 的正则表达式之坑
- C++ cin cout
- MongoDB 3.X 用户权限控制
- ssl1104-USACO 2.1城堡(foodfill)【图论,广搜】
- C语言关键字必备练习题
- python实现pdf到excel的自动批量转换(附 完整代码)
- 使用mybatis生成UUID
- armv6 armv7 armv7s架构的区别
- java中的移位操作
- FSM有限状态机(三段式)-Verilog实现
- android源码分析!程序员怎样优雅度过35岁中年危机?送大厂面经一份!
- NTC热敏电阻-阻值温度计算
- 瀚高数据库debug问题
- Atom配置markdown
- execute immediate 用法详解
- Spark 和 Python.sklearn:使用随机森林计算 feature_importance 特征重要性
- 写学习心得,赢华为P30、漫步者音响!
- vue VNode如何使用,是什么东西?
- “天才少年”!华中科技大学这一研究生,刚毕业年薪201万!
热门文章
- 全球与中国机器人即服务市场领航调研与投资战略规划分析报告2022-2028年
- 测试思想-好东西与大家分享-1
- sizeof和strlen的区别
- 多生产者多消费者问题
- Office HPDeskjetD2468 打印机电源灯闪烁不停,打印机不工作怎么办
- Oracle database server 安装tips
- Win8.1 查看 “Windows 体验指数“
- 匿名内部类的使用总结
- 付费会员制,如何赋能产品?
- PMCAFF微课堂「已结束」 | 阿里资深运营揭秘电商运营与纯互联网运营的区别与互通