Android 侦探事务所:bugreport 证据分析实战

89 阅读11分钟

一、侦探的证据清单:19 类关键线索

承接上回侦探事务所的调查流程,现在侦探们拿到了 bugreport 这份厚厚的证据报告,需要从中找出系统问题的蛛丝马迹。这份报告被精心整理成 19 类证据,每类都以 ------ 证据类别 ------ 作为标题,就像侦探手册里的不同章节。

1. 案件基本档案(系统 build 信息)

侦探首先翻开案件封面,看到系统的 "身份证明":

plaintext

------ dumpstate: 2025-06-12 14:30:00 ------
------ Build: Android 6.0.1 ------
------ Build fingerprint: google/nexus5/hammerhead:6.0.1/MMB29K/123456:user/release-keys ------
------ Bootloader: hammerhead-1.2.3 ------
------ Kernel: Linux version 3.4.0 (build@xxx) (gcc version 4.9.3) ------
------ UPTIME (uptime) ------
up time: 01:23:45, idle time: 00:10:30, sleep time: 00:00:15

故事解析

  • 这是系统的 "出生证明" 和 "工作时长记录"
  • build 信息记录了系统版本和编译时间,就像嫌疑人的基本资料
  • uptime 显示系统已经运行了 1 小时 23 分 45 秒,其中 10 分 30 秒处于空闲状态

2. 现场环境勘查(内存 / CPU / 存储)

侦探进入系统 "犯罪现场",检查硬件运行情况:

cpp

运行

// 存储设备性能证据(/sys/block/mmcblk0/stat)
mmcblk0/stat: 51897 32911 5984151 171690 198408 171047 29988513 2001070 0 334260 2172230
// 计算方式:read_speed = 512 * data3 / data4
read: 512 * 5984151 / 171690 ≈ 17845KB/s 
write: 512 * 29988513 / 2001070 ≈ 1233KB/s

// 内存信息证据(/proc/meminfo)
------ MEMORY INFO (/proc/meminfo) ------
MemTotal:        1865472 kB
MemFree:          234560 kB
Buffers:           12345 kB
Cached:           567890 kB

故事解析

  • 存储设备的 stat 信息就像硬盘的 "工作记录",记录了读写次数和速度
  • 内存信息是系统的 "仓库库存",MemTotal 是总容量,MemFree 是剩余空间
  • CPU 信息(top 命令)则像现场的 "劳动力分配表",显示每个进程的 CPU 占用情况

3. 监控录像(kernel log)

侦探调取了系统的 "监控录像":

plaintext

------ KERNEL LOG (dmesg) ------
[    0.000000] Booting Linux on physical CPU 0x0
[    0.543210] CPU: BogoMIPS 1234.56
[    1.234567] Mounting filesystem on /dev/mmcblk0p15
[   10.765432] wlan0: connected to AP with signal strength -65dBm

故事解析

  • dmesg 记录了内核启动到现在的所有操作,像监控摄像头一样记录系统底层活动
  • 这里可以看到系统启动过程、硬件初始化和设备连接情况

4. 现场物品清单(lsof/map/Wait-Channels)

侦探开始清点现场物品和人员位置:

plaintext

// 打开的文件清单(lsof)
------ LIST OF OPEN FILES ------
COMMAND  PID  USER  FD  TYPE  NAME
zygote   286  root  mem       /system/lib/libart.so
system_server 345  system 3u  IPv4 12345  TCP localhost:8080 (LISTEN)

// 进程内存映射(show map)
------ SHOW MAP 1 (/init) ------
00000000-00001000 r-xp 00000000 103:01 12345                            /init
00002000-00003000 r--p 00001000 103:01 12345                            /init
00004000-00005000 rw-p 00002000 103:01 12345                            /init

// 线程等待位置(Wait-Channels)
------ BLOCKED PROCESS WAIT-CHANNELS ------
1    /init     SyS_epoll_wait
123  com.example.app  pthread_cond_wait

故事解析

  • lsof 列出所有进程打开的文件,就像现场物品清单,zygote 进程正在使用 libart.so 库
  • show map 是进程的 "内存布局图",显示每个程序在内存中的位置
  • Wait-Channels 记录线程的 "停留位置",比如 init 进程正在等待 epoll 事件

5. 通话记录(logcat 日志)

侦探查看系统的 "通话记录":

plaintext

------ SYSTEM LOG (logcat) ------
06-12 14:30:22.345 I/ActivityManager: Starting activity com.example.app/.MainActivity
06-12 14:30:23.678 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.app, PID: 1234
    java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.app.Data.load()' on a null object reference
    at com.example.app.MainActivity.onCreate(MainActivity.java:45)
    
------ EVENT LOG (logcat -b events) ------
06-12 14:30:20.123 I/Event: user 0 started app com.example.app at time 123456789

故事解析

  • system log 是系统的 "日常对话",记录应用启动和崩溃等事件
  • event log 是 "重要事件记录",记录用户操作和应用启动等关键事件
  • radio log 则是 "网络通话记录",记录移动网络和 WiFi 的通信情况

6. 嫌疑人笔录(VM Traces)

侦探获取了应用的 "嫌疑人笔录":

plaintext

------ VM TRACES JUST NOW (/data/anr/traces.txt.bugreport) ------
"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 obj=0x12345678 self=0x87654321
  | sysTid=1234 nice=0 cgrp=default sched=0/0 handle=0xabcdef01
  | state=S schedstat=( 123456789 12345678 1234 ) utm=10 stm=20
  at com.example.app.Data.load(Data.java:30)
  at com.example.app.MainActivity.onCreate(MainActivity.java:45)
  ...

------ TOMBSTONE (/data/tombstones/tombstone_01) ------
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/nexus5/hammerhead:6.0.1/MMB29K/123456:user/release-keys'
pid: 1234, tid: 1235, name: com.example.app  >>> com.example.app <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
    r0 00000000  r1 00000000  r2 00000000  r3 00000000
    ...
    backtrace:
        #00 pc 00001234  /system/lib/libc.so (malloc+123)
        #01 pc 00005678  com.example.app (com.example.app.NativeLib.crashNative+45)

故事解析

  • VM Traces JUST NOW 是当前的 "嫌疑人讯问记录",显示主线程在 Data.load () 处阻塞
  • LAST ANR 是上次应用无响应的 "历史笔录",记录 ANR 发生时的堆栈
  • tombstone 是 native 层崩溃的 "死亡报告",显示在 NativeLib.crashNative 处发生段错误

7. 网络通信记录(network)

侦探调查系统的 "网络通信记录":

plaintext

------ NETWORK DEV INFO (/proc/net/dev) ------
wlan0:  rx:123456789 bytes (123.4 MB)  tx:987654321 bytes (987.6 MB)
eth0:   rx:0 bytes (0.0 MB)  tx:0 bytes (0.0 MB)

------ WIFI NETWORKS (wpa_cli list_networks) ------
Selected interface 'wlan0'
network id / ssid / bssid / flags
0       HomeWiFi  12:34:56:78:90:ab [CURRENT]
1       OfficeWiFi        23:45:67:89:0a:bc

故事解析

  • 网络设备信息记录了各个网络接口的收发数据量,像网络的 "流量统计表"
  • WiFi 网络列表显示连接过的 WiFi 网络,当前连接的是 HomeWiFi

8. 历史档案(last log)

侦探查阅系统的 "历史档案":

plaintext

------ LAST KMSG (/proc/last_kmsg) ------
[  123.456789] kernel: OOM killer: killing process com.example.app (pid:1234, total-vm:123456KB)
[  124.567890] kernel: Memory freed: 12345KB

------ LAST LOGCAT (logcat -L) ------
06-11 22:30:45.678 W/ActivityManager: Activity idle timeout for ActivityRecord{123456 u0 com.example.app/.MainActivity}

故事解析

  • LAST KMSG 是内核的 "历史错误记录",显示 123 秒时因内存不足杀死了 com.example.app
  • LAST LOGCAT 是系统日志的 "历史备份",记录了前一天的 Activity 超时事件

9. 网络安全记录(ip/iptables)

侦探检查系统的 "网络安全记录":

plaintext

------ IP RULES (ip rule show) ------
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

------ IPTABLES (iptables -L) ------
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
DROP       all  --  anywhere             anywhere             !ctstate RELATED,ESTABLISHED

故事解析

  • IP 规则是网络的 "交通规则",决定数据包的路由方式
  • IPTABLES 是网络的 "安全检查点",设置了允许和拒绝的网络连接规则

10. 系统配置档案(property)

侦探查看系统的 "配置档案":

plaintext

------ SYSTEM PROPERTIES ------
[ro.build.version.sdk]: [23]
[ro.product.model]: [Nexus 5]
[net.bt.name]: [Android]
[dalvik.vm.heapsize]: [192m]

------ FILESYSTEMS & FREE SPACE (df) ------
Filesystem                Size  Used Avail Use% Mounted on
/dev/block/mmcblk0p11     2GB  1.5GB  500MB  75% /system
/dev/block/mmcblk0p28    10GB  8GB    2GB   80% /data

故事解析

  • 系统属性是系统的 "个人资料",记录了 SDK 版本、设备型号等信息
  • 磁盘空间信息是系统的 "储物空间清单",显示 /system 和 /data 分区的使用情况

11. 进程通信记录(Binder)

侦探调查进程间的 "通信记录":

plaintext

------ BINDER STATS (/sys/kernel/debug/binder/stats) ------
threads: 128
processes: 64
transaction_count: 123456
failed_transaction_count: 123

------ BINDER TRANSACTIONS (/sys/kernel/debug/binder/transactions) ------
transaction 12345: from pid 123 (com.example.app) to pid 456 (system_server)
  code: 123, flags: 0x0
  data_size: 4096, reply_size: 1024

故事解析

  • Binder 是 Android 进程间通信的 "邮局系统",stats 记录了通信的基本情况
  • transactions 记录了具体的 "信件往来",比如 com.example.app 向 system_server 发送了一个 4KB 的请求

12. 系统服务调查报告(dumpsys)

侦探对系统服务进行 "全面调查":

plaintext

------ DUMPSYS (dumpsys) ------
DUMP OF SERVICE activity:
  Activities:
    Running activities (top 5):
      TaskRecord{12345 #123 A=com.example.app U=0 sz=1}
        Run #0: ActivityRecord{67890 u0 com.example.app/.MainActivity}
  Services:
    Running services:
      ServiceRecord{abcdef u0 com.example.app/.DataService}

------ CHECKIN BATTERYSTATS (dumpsys batterystats -c) ------
Battery history:
  AC powered: true
  USB powered: false
  charge counter: 12345 mAh
  capacity: 80%

故事解析

  • dumpsys 是系统服务的 "调查报告",记录了 Activity、Service 等的运行状态
  • checkin 数据是系统的 "定期体检报告",包含电池、内存、网络等统计信息

13. 应用现场勘查(dumpsys app)

侦探对应用进行 "现场勘查":

plaintext

------ APP ACTIVITIES (dumpsys activity all) ------
Activity stack:
  Task id=123
    ActivityRecord{12345 u0 com.example.app/.MainActivity}
      Intent: act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]
      Process: com.example.app (pid=1234)
      State: RESUMED
      Last saved instance: null

------ APP SERVICES (dumpsys activity service all) ------
Services:
  ServiceRecord{67890 u0 com.example.app/.DataService}
    Process: com.example.app (pid=1234)
    State: RUNNING
    Start time: 06-12 14:20:30

故事解析

  • 应用 Activity 信息是应用的 "现场照片",显示当前运行的 Activity 及其状态
  • 应用 Service 信息是应用的 "后台工作记录",显示正在运行的 Service 及其启动时间

二、侦探助手:ChkBugReport 证据分析工具

面对厚厚的证据报告,侦探拿出了得力助手 ChkBugReport,它能将枯燥的文本证据转化为直观的可视化报告。

工具使用流程

bash

# 1. 获取bugreport证据
adb shell bugreport > bugreport.txt

# 2. 用ChkBugReport分析证据
java -jar chkbugreport.jar bugreport.txt

# 3. 打开可视化报告
浏览器打开 bugreport_out/index.html

故事解析

  • 就像侦探用放大镜和指纹检测工具处理证据,ChkBugReport 能自动分析 bugreport

  • 它会生成 HTML 格式的报告,用图表和分类视图展示关键信息,比如:

    • 进程状态统计
    • 电池使用趋势
    • ANR 和崩溃事件汇总

关键证据分析场景

java

// ChkBugReport分析死锁场景的关键逻辑
void analyzeDeadlocks(List<ThreadInfo> threads) {
    for (ThreadInfo thread : threads) {
        if (thread.state == "MONITOR" && thread.blockedBy != null) {
            // 发现可能的死锁:线程持有锁A并等待锁B
            DeadlockCandidate candidate = new DeadlockCandidate();
            candidate.thread = thread;
            candidate.heldLocks = thread.heldLocks;
            candidate.waitingFor = thread.blockedBy;
            deadlocks.add(candidate);
        }
    }
    // 生成死锁关系图
    generateDeadlockGraph(deadlocks);
}

故事解析

  • ChkBugReport 会特别关注状态为 MONITOR 的线程,这些线程可能参与了死锁
  • 它会分析线程的锁持有情况和等待关系,生成死锁关系图,就像侦探绘制嫌疑人关系图
  • 对于 AIDL 死锁等特定场景,它能识别出跨进程的锁依赖关系

三、侦探手记:实战分析技巧

  1. 快速定位关键证据:通过搜索 ------ 快速跳转到不同类别的证据,比如搜索------ DUMPSYS定位系统服务信息。

  2. ANR 问题排查路径

    • 查找------ VM TRACES AT LAST ANR部分
    • 查看主线程(tid=1)的堆栈,找到阻塞的方法
    • 检查相关 Service 或 BroadcastReceiver 的执行时间
  3. 崩溃问题排查路径

    • 查找------ TOMBSTONE部分
    • 分析崩溃类型(SIGSEGV、SIGBUS 等)和崩溃地址
    • 查看 backtrace 确定崩溃的函数调用栈
  4. 性能问题排查路径

    • 查看------ MEMORY INFO------ PROCRANK分析内存使用
    • 检查------ KERNEL WAKELOCKS找出保持唤醒的模块
    • 分析------ CPU INFO------ PROCESSES找出高 CPU 占用的进程

四、案件总结:证据分析的核心思维

bugreport 就像系统的 "体检报告" 和 "犯罪现场调查报告",每个部分都记录了系统运行的某一方面信息。作为 Android 侦探,需要:

  1. 建立证据地图:清楚每类证据的来源和作用,比如:

    • 系统 build 信息是案件背景
    • logcat 是时间线证据
    • VM Traces 和 tombstones 是直接作案证据
    • dumpsys 是系统服务的证词
  2. 时间线分析:将不同来源的证据按时间顺序排列,还原问题发生的过程。

  3. 关联分析:找出不同证据之间的联系,比如 ANR 发生时的系统负载、内存使用情况和相关日志。

  4. 模式识别:通过 ChkBugReport 或手动分析,识别常见问题模式,如死锁模式、内存泄漏模式等。

通过这种系统化的证据分析方法,侦探们就能从复杂的 bugreport 中找出系统问题的根源,完成案件侦破。