前言
上一篇文章《Usap64 进程莫名死锁问题分析》中使用到分析技术 art-parser 工具是我未开源项目中的一个最重要组成部分。可能因为文章的选题有趣,也因此我的掘金账号粉丝数突涨,让我意想不到的是登上了周热榜一。
作者榜.jpg | 文章热榜.jpg |
---|
其中也有注意到这项工具,并对此感兴趣。这里也浅谈下关于 art-parser 这个工具的由来背景,以及它未来会不会开源等。
前世今生
毕业于 2016 年,当时最新的 AOSP 版号为 7.0,直接跳过了 Dalvik 虚拟机时代,诸如预编译 OAT 文件等也都在这个时期发生,正是如此第一次接触到 Android 应用程序发生 Native Crash 问题,堆栈显示最后 #0 帧处为 boot.oat 文件上,面对这类问题,当时简直是灾难一般。
1 | 早期的NativeCrash复制代码#00 pc 00561a48 /data/dalvik-cache/arm/system@framework@boot.art |
查阅 AOSP 源代码了解到它是一个 ELF 格式的文件,要解决这样的问题,需要了解许多其它相关知识,如 Java Code 与 ART 预编译生成 Machine Code 的关系,ART Java 对象模型,Tombstone 机制以及 Coredump 文件等。随着版本更新,Tombstone 显示的堆栈也更加的详细以及通过 GDB 来解析 Coredump 文件也能够显示更多信息。
1 | 现在的OAT文件复制代码(gdb) bt |
尽管现在有更多信息可以输出,但也无法直接展开 0x13a80000 这个对象的数据结构,在过去,我都是基于 GDB 的脚本功能来编写相关的解析功能,方便是挺方便的,但是这样的脚本无法满足我的需求,更多是局限于分析 Native Crash 问题上。
1 | ini复制代码art-parser> p 0x13a80000 |
而在安卓上发生纯粹的 Native Crash 时,分析 Coredump 转储文件,现有的工具有 GDB、LLDB 等,基本能够满足绝大部分开发者。但是将更多的问题转变为分析 Ramdump、Coredump 时需面临的难点,但优点在于它如同现场一般,我们几乎能够查询到所有在内存上的数据。
原问题类型 | 核心数据 | 离线分析难点 |
---|---|---|
Java OOM | 需要对应的程序的 Java Hprof 文件 | 从 Coredump 转储文件中统计各个 Java 对象的数量占用内存大小。 |
Java 常见异常 | AndroidRuntime 的 Fatal 日志 | 从 Coredump 转储文件中定位 Java 的调用堆栈,检索具体 Java 对象数据,如某对象成员空指针等信息。 |
ANR 、Watchdog | anr_trace.txt 文件,如耗时问题需记录到具体调用栈 | |
冻屏 | 范围广,表现多,如问题表现在用户态时,大部分需要 Dumpsys 数据 | 从 Ramdump 取得 system_server 的 Coredump 转储,从 Coredump 中得到具体的 Dumpsys 数据,计算量大,人工手动计算基本不可能。 |
… |
在互联网技术中,也有类似的如 jmap + gcore 来完成现场保存以及离线提取 hprof 文件。这么棒的解析技术,为什么 Android 的技术栈上会没有呢?于是我最初的设想解决转储问题、提取 hprof 文件以及展开 Java 对象数据结构。
项目结构
随着项目的进行,发现最初的设想无法满足我自身需求,如 Java 堆栈解析,ART 虚拟机寄存器解析,检索对象,统计对象数量等。然而功能实现上虽然并不难,但是 Core 文件的内容也并不是每一个都那么的全,要考虑到许多缺页的情况。比如该 Core 采用的是原生默认参数抓取,那么会缺失的文件页表。部分抓 Core 的机制会缺失重要的 bss 段内存,如 MTK 的项目常常会发生。从 Ramdump 文件中转储 Coredump 文件,可以使用 crash-utility 项目的 gcore 插件来完成,早一些的项目这个过程是没有任何问题的,只是现在的手机项目 zram 的大小占用越来越大,以致于现今要从 Ramdump 中解析用户态的数据较为依赖 zram 的解析。然而 crash-utility 项目中 zram 长久以来都无人维护,也因此 gcore 的转储方案不适用于我们现在的手机项目。
由于这一套解决方案中采用了 crash-utility 来补充内核态解析,彻底打通这个桥梁于是我也给该项目维护更新适配最新的内核。
标题 | |
---|---|
Fix “rd” command to display data on zram on Linux 5.17 and later | github.com/crash-utili… |
arm64: Fix “vtop” command to display swap information on Linux 5.19 and later | github.com/crash-utili… |
Core 从何而来
既然是离线内存分析,那么首当其冲的能力是解决 Core 从何而来的问题。
名称 | 用途 |
---|---|
ram2core | 应用于 crash-tools 环境下,从 ramdump 中提取目标进程的 coredump。配合 zram、shmem 来解析特殊内存。 |
proc2core | 应用于真机 Root 环境下,抓取目标进程 coredump,并且不会破坏现场。 |
OpenCoreSDK | 应用于安卓应用程序非 Root 环境下,需开发者集成的开源方案介绍:《Android 应用程序如何抓取 Coredump》应用:《结合 OpenCore 分析安卓应用程序 Crash 问题》 |
其它方案 | 如配置原生参数《如何理解Native Crash问题》该文在我还没创建掘金前在鸿洋的公众号上投稿过。 |
Core 文件页修复
从 Ramdump 中转储得到某个进程的 Coredump,往往文件页相关的内存都存在大量的丢失,如果是纯粹的 Native 进程,一般也只有 .so 等动态库文件的需求,而 .so 可在符号表文件里找到,因此都可在 LLDB、GDB 上通过外部映射。
但是非 Native 进程,并且需要解析 Java 数据,那么需求就不一样了,它需要依赖 DexCache 内存,也就是对应的原始 .jar,.apk,.dex 等文件内容,此时要拿到这些文件,要么在真机里刷入对应版本的 ROM,或者是通过解压刷机包的 super.img 获得。
1 | 完整的修复过程复制代码# crash SYS_COREDUMP/SYS_COREDUMP vmlinux |
1 | ini复制代码art-parser --core-file=core.2506 |
此时我们需要填补缺少的文件页,类似的像 GDB 、LLDB 那样通过原始文件映射到运行环境下。
1 | ini复制代码将 app_process64 映射到 art-parser 环境下 |
例如该问题是一个内存泄露导致系统死机问题,我们可以在 Ramdump 中解析出该 App 的所有对象,以及统计出占用的内存大小,以及提取成 hprof 文件在 AS 上分析。
1 | ini复制代码art-parser --core-file=/tmp/restore.2506.core --art-path=libart.so |
技术体系
该项目属于一项技术兜底,使用有一定门槛,对大部分开发者而言难以理解,也很难会想到可以运用上,包括我在内部也不容易推广。因为遇上需要用到这项技术来分析的问题,那么想必是穷途末路的时候,但是一旦能用上,或者把问题化简为繁
时会有奇效。
开源问题
我从设计初就考虑将 art-parser 开放给应用开发者使用的,因此从设计上考虑到开发者没有符号表文件,所以并不依赖符号表文件。在今年的 3 月份时联系过内部提报开源项目,只能说至今未果,因此我也不知道能不能将该项目进行开源。
我还是比较热衷于将此项目开源,类似下边的王者荣耀
发生 Java 空指针问题,但却没有报 JE 空指针,而是报 NE 问题,不过使用 art-parser 可以轻松秒杀,想必能帮助到应用开发者解决一些头疼的 NE 问题,尤其是与 Java 相关的。
1 | ini复制代码art-parser> bt 31033 |
1 | ini复制代码 QF[0x6a370b8160] PC[0x7478c03fc8] at dex-pc 0x6a73c4bd42 com.tencent.smtt.utils.LogFileUtils.writeDataToStorage //AM[0x744bdc3808] |
1 | arduino复制代码art-parser> disassemble 0x744bdc3808 -i 0x6a73c4bd42 |
1 | arduino复制代码从字节码可得知调用 v1.mkdirs(),而当前 v1 = 0x0,因此空指针异常。 |
1 | ini复制代码art-parser> p 0x136b2340 -b |
最后能不能开源,我也不知道,只能说尽量争取,毕竟现今在公司里会使用该项目解决一些问题,也仅有几位同事,在这里也只是简单的介绍下,只言片语不易传达,没有真正的使用体会,不知大家对该项目是否感兴趣。
本文转载自: 掘金