首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >写了个 AS 插件:SmartNDKStack,快速定位 Android Native 开发 Crash

写了个 AS 插件:SmartNDKStack,快速定位 Android Native 开发 Crash

作者头像
字节流动
发布2021-11-26 11:05:57
发布2021-11-26 11:05:57
2.5K0
举报
文章被收录于专栏:字节流动字节流动

对于Android Native开发的人员而言,可能经常会在开发过程及线上环境中遇到Native Crash的问题,对于这类native crash,我们一般都会直接addr2line,或使用ndk中附带的ndk-stack脚本分析。

但是ndk-stack是不会对build id不匹配的库进行分析的。

对于上述问题,即使build id不同,我们也是可以尝试性地进行分析的,大致流程如下:

基于此分析流程,我编写了一个AS插件:SmartNDKStack,功能如下:

  • 支持上传日志或选中日志分析,解析结果超链接显示
  • 在buildId不匹配时根据函数偏移解析
  • 展开inline调用
  • Project视图中选中elf文件解析build id
  • 指定NDK路径
  • 指定symbol库目录

大家若有需要,可以在插件市场搜索安装。现在可能会存在一些不足,如果出现问题,请把截图和现象描述发我:wangshengyang96@gmail.com

下面是原理描述。

1. 调用栈帧内容说明

写个demo触发crash

代码语言:javascript
复制
__attribute__((always_inline)) void realThrowFatal(){
    raise(SIGABRT);
}
__attribute__((always_inline)) void throwFatal(){
    realThrowFatal();
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkdemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    throwFatal();
    return env->NewStringUTF(hello.c_str());
}
代码语言:javascript
复制

运行看调用栈

代码语言:javascript
复制
2021-11-14 16:04:45.573 14705-14705/? A/DEBUG:       #00 pc 00000000000c5008  /apex/com.android.runtime/lib64/bionic/libc.so (tgkill+8) (BuildId: e81bf516b888e895d4e757da439c8117)
2021-11-14 16:04:45.574 14705-14705/? A/DEBUG:       #01 pc 000000000000f0c8  /data/app/~~NiGDTVWkvUzCYf_UpH7RdQ==/com.example.ndkdemo-dfk3WrVVoUG5N0jUHQiyyw==/lib/arm64/libndkdemo.so (Java_com_example_ndkdemo_MainActivity_stringFromJNI+52) (BuildId: 8c26841b3c32a89935d095d8e916180628bded7b)
2021-11-14 16:04:45.574 14705-14705/? A/DEBUG:       #02 pc 000000000013ced4  /apex/com.android.art/lib64/libart.so (art_quick_generic_jni_trampoline+148) (BuildId: 9f1408149c58982ae91ee6377c202d2b)
...
代码语言:javascript
复制

图片版:

对于如上调用栈而言, #01是开发者最关注的地方,挂在了自己的库里。

这一行的关键信息解读结果如下:

  • crash所在库在本机的绝对路径是/data/app/~~NiGDTVWkvUzCYf_UpH7RdQ==/com.example.ndkdemo-dfk3WrVVoUG5N0jUHQiyyw==/lib/arm64/libndkdemo.so
  • crash地址相对于库的偏移是0xf0c8
  • crash地址在函数Java_com_example_ndkdemo_MainActivity_stringFromJNI中,相对于函数地址的偏移是52(十进制)
  • crash所在库的BuildId是8c26841b3c32a89935d095d8e916180628bded7b
2. 如何解析
  • 常规操作 addr2line -Cfie libndkdemo.so 0xf0c8 网上的大部分教程都是直接进行如上的addr2line操作,对于自己开发过程中直接运行出现了crash的确可以快速定位,但如果代码已发生变更,库不匹配了,定位效率就会大幅下降,比如测试报了crash,但是本地代码已发生变更,库不匹配,如果想拿到build id相同的库,就要回退到当时的代码,重新编一个一样的库再做addr2line。
  • 进阶操作 按照上述流程图所示,我们可以先确认函数在本地库中的地址,得到地址后,与函数偏移相加得到新的地址用作解析。
    • 获取函数地址
    • 解析 函数的地址是0xf094,加上十进制的52,即0xf094 + 0x34刚好是0xf0c8,匹配。 对于本地库文件发生变更的情况,我们依然可以使用这种方案解析(但是要确保变更和当前函数无关)。
    • 本地库发生变更 修改其他函数,使crash所在的函数地址变更,变更后函数地址是0xf0a0,0xf0a0 + 0x34 = 0xf0d4使用新的地址addr2line,可以发现,此时buildId虽然发生变更(修改前是:8c26841b3c32a89935d095d8e916180628bded7b),但是该方案依然能够解析。

作者:省油的灯 链接:https://juejin.cn/post/7030340152325046303

-- END --

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 字节流动 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 调用栈帧内容说明
  • 2. 如何解析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档