AI 技术博客
Android全栈9 分钟阅读4550

【全栈第24课】实战:异常重启看 ramdump

Android 全栈工程师进阶教程 第24课。实战:异常重启看 ramdump。基于公开 AOSP 知识整理,含原理、可编译示例、常见问题定位。

> 本文是《Android 全栈工程师进阶教程》系列第 24 课。完整 26 课见 GitHub 仓库

【全栈第24课】实战:异常重启看 ramdump


0. 这节课你将搞懂什么

设备无征兆重启/反复重启,怎么先定性(是 Java 崩、native 崩、还是 kernel panic),再从死机现场(ramdump/pstore)找到 kernel panic 的根因。 学完你能回答:设备自己重启了,我怎么知道是哪一层崩的?kernel panic 日志怎么读?Call trace 栈顶就是凶手吗?我前面学的驱动 bug(中断里睡眠、use-after-free)怎么从 panic 栈反推?

1. 背景:重启是最难查的问题之一

"重启"难在:崩的那一刻设备就没了,抓不到实时日志。而且重启有很多种(Java crash 触发的软重启、native crash、kernel panic 硬重启、watchdog 复位、掉电),根因和定位手段完全不同。关键是先定性是哪种重启,再用对应手段

对应 示例项目 高频的"异常重启"类 ISS 单(如之前 (内部单号) 桌面异常重启)。

2. 全局图:重启分类(先定性)

设备重启
├─ Java crash 重启     → SystemServer 关键服务抛异常 → framework 重启(软重启)
│      查:logcat / dropbox(EventLog)
├─ Native crash        → 关键 native 进程崩
│      查:tombstone(/data/tombstones)
├─ Kernel panic        → 内核挂 → 硬重启(本课重点)
│      查:ramdump / pstore(/sys/fs/pstore)
├─ HW watchdog 复位     → CPU 卡死无响应,看门狗强制复位
│      查:watchdog 日志 / ramdump
└─ 掉电                 → 电池/PMIC 问题
       查:硬件

3. kernel panic 的死机现场:pstore / ramdump

内核 panic 时,会把当时的内核日志/内存关键区写进一块保留内存(ramoops/pstore),这块内存重启不清,所以重启后能读到上一次崩溃的现场:

# pstore(轻量,最常用):
adb shell ls /sys/fs/pstore/
adb shell cat /sys/fs/pstore/console-ramoops-0   # 上次重启的 kernel 日志(含 panic 栈)
adb shell cat /sys/fs/pstore/dmesg-ramoops-0
# 老设备:
adb shell cat /proc/last_kmsg

ramdump 是完整的内存转储(整个 RAM 镜像),用高通 QPST / crashscope 等工具配合符号深度分析,用于 pstore 信息不够时。

4. 前置准备

  • 依赖 L17(中断/UAF)、L22(武器库)、L21(开机/重启)。
  • 本课是实战方法,产出 panic 分析 SOP。

5. 动手:读懂一个 kernel panic 日志(核心技能)

典型 panic 日志结构:

Kernel panic - not syncing: <原因,如 "Attempted to kill init!" 或具体 BUG>
CPU: 3 PID: 1234 Comm: hello-irq ...
...
Call trace:                          ← ★最关键:崩溃时的调用栈(从栈顶往下)
 hello_irq_handler+0x40/0x80         ← 栈顶 = 直接崩溃点(往往是你的驱动)
 __handle_irq_event_percpu+0x...
 handle_irq_event+0x...
 ...
PC is at <函数+偏移>                 ← 出事的指令地址
LR is at <返回地址>

怎么读:

  1. Kernel panic - not syncing: 后面是 panic 原因。
  2. Call trace 栈顶就是直接崩溃点——从上往下第一个是出事的函数。如果栈顶是 hello_irq_handler,说明崩在你的中断处理函数。
  3. 符号化:如果栈是地址(没符号),用 addr2line -e vmlinux <地址> 或对照 System.map 转成 源文件:行号(L22)。

6. 看底层:从 panic 栈反推你学过的驱动 bug

前面课里写的几类驱动 bug,在 panic 栈里长这样:

panic 类型日志特征根因(对应课)
NULL pointer dereferenceUnable to handle kernel NULL pointer dereference at 0000...解引用空指针(probe 没判 NULL,L15/L16)
use-after-freeKASAN 报 use-after-free,栈在 work 回调work 没 cancel_work_sync 就释放(L17)
sleeping in atomicBUG: sleeping function called from invalid context中断上半部里 sleep/做 I2C/拿 mutex(L17)
scheduling while atomicBUG: scheduling while atomicspinlock 里 schedule
I2C transfer in irq栈在中断里调 i2c_transfer中断里直接 I2C(应丢 workqueue,L16/L17)
所以 panic 栈顶 + 上面这张表,能快速定位是哪类驱动 bug。 这就是 L17 那些踩坑的"案发现场"。

7. 编译 & 运行(抓现场)

# 设备重启后,第一时间抓上次现场(别再重启,pstore 会被覆盖):
adb shell cat /sys/fs/pstore/console-ramoops-0 > panic.log
# 找 panic
grep -A30 "Kernel panic" panic.log     # panic 原因 + Call trace
# 符号化(若栈是地址):
addr2line -e out/.../vmlinux 0xffffff...   # 地址转 源文件:行号

> 验证状态(诚实):本课为 panic 分析方法论,无编译产物;命令为标准 ramdump/pstore 调试方式。

8. 踩坑提醒

  1. 重启后立刻抓 pstore:再次重启会覆盖 pstore,现场就没了。先 cat 出来存文件。
  2. 栈顶不一定是逻辑错处:栈顶是崩溃指令处,但根因可能在更上层(传了个空指针进来)。要顺着栈往下看调用链。
  3. 没符号看不懂:panic 栈是地址要符号化(addr2line/vmlinux),否则一堆十六进制没用。
  4. Java crash 当 kernel panic 查:先定性!Java 崩在 logcat/dropbox,不在 pstore。查错地方白费。
  5. watchdog 复位不一定有 panic:CPU 卡死(死锁)时没 panic,是 watchdog 超时强制复位,看 watchdog 日志和卡死时的栈。

9. 常见问题分析与定位(异常重启实战)

9.1 常见问题清单

  1. 无征兆单次重启
  2. 反复重启(boot loop)
  3. 特定操作后重启(可复现)
  4. 重启但 pstore 没东西
  5. 卡死后重启(watchdog)

9.2 分析与定位

通用第一步:定性(哪种重启)

# 先看 Java/native 层有没有崩(软重启)
adb logcat -b crash -d                          # crash buffer
adb shell ls /data/tombstones/                  # native crash
adb shell ls /data/system/dropbox/ | grep crash # Java crash 记录
# 再看 kernel 层(硬重启)
adb shell cat /sys/fs/pstore/console-ramoops-0   # kernel panic
  • dropbox 有 system_server crash → Java 软重启;tombstone 有 → native;pstore 有 panic → kernel 硬重启。

① 无征兆单次重启 — 定性后按对应层查;最可能 kernel panic(pstore)或 native crash(tombstone)。

② boot loop — 每次都崩在同一处,反复重启。pstore 看每次 panic 栈是否一样;若是 SystemServer 反复崩,logcat 看异常。

③ 可复现重启 — 最好查!复现前 adb logcat > log &,复现后看最后的日志/抓 pstore,直接定位触发点。

④ pstore 没东西 — ramoops 没配置/被关,或这次不是 kernel panic(是软重启,查 Java/native);或需要 ramdump 完整转储。

⑤ watchdog 复位 — 没 panic 但重启了。dmesg/pstore 找 watchdog 相关;CPU 死锁要看卡死时各 CPU 的栈(ramdump 里有)。

9.3 定位决策树

异常重启
├─ 第一步定性:
│   logcat -b crash / dropbox 有 system_server crash → Java 软重启 → 看异常栈
│   /data/tombstones 有 → native crash → tombstone+ndk-stack(L22)
│   /sys/fs/pstore 有 panic → kernel 硬重启 → 读 Call trace
│   都没有 → watchdog 复位(卡死)或掉电
├─ kernel panic → Call trace 栈顶 + 第6节表 → 哪类驱动 bug(NULL/UAF/atomic,L15-17)
│   栈是地址 → addr2line 符号化(L22)
├─ 可复现 → 复现前开 logcat,复现后看最后日志(最好查)
├─ pstore 空 → ramoops 没配/不是 panic(查 Java/native)/需 ramdump
└─ watchdog → 找卡死时各 CPU 栈(ramdump)

10. 小结

  1. 重启先定性:Java 软重启(dropbox)/ native crash(tombstone)/ kernel panic(pstore)/ watchdog / 掉电——根因和手段完全不同。
  2. kernel panic 看 pstore:/sys/fs/pstore/console-ramoops-0,重启后第一时间抓(会被覆盖)。
  3. Call trace 栈顶 = 直接崩溃点,符号化后定位到源码行。
  4. 栈顶 + bug 类型表:NULL/use-after-free/sleeping in atomic 对应 L15-L17 的驱动坑。
  5. 排查口诀:先定性(crash buffer/tombstone/pstore);kernel panic 读 Call trace 栈顶+符号化;可复现就开 logcat 复现。

下节预告

L25:实战——功耗/发热定位 —— 待机掉电快/使用发烫,怎么揪出耗电的元凶(wakelock/CPU/regulator)。

评论