日志系统与抓取技巧:logcat、dmesg、kernel log,如何高效定位问题

做Android稳定性分析,说白了就是跟日志打交道。你想想看,系统崩了、卡了、重启了,它自己又不会说话,唯一能告诉你的就是日志。我见过太多工程师,遇到问题第一反应就是“抓个logcat”,结果抓回来的东西要么不全,要么根本不对症。

今天我就把压箱底的日志抓取技巧掰开揉碎了讲。嗯,这里要注意,不是所有日志都叫logcat,也不是所有问题都能靠logcat解决。

一、logcat:你最熟悉的陌生人

logcat大家都会用,但你真的用对了吗?我个人习惯把logcat分成三类场景来看:

1. 应用层崩溃(Java Exception)

这类问题最直观。App闪退了,logcat里会有一大堆堆栈信息。但有个坑——logcat缓冲区是环形的,如果你抓晚了,关键信息就被冲掉了。

避坑指南: 我曾经遇到过一个问题,用户反馈App闪退,但开发说logcat里啥都没有。后来发现是测试人员等到闪退发生后才开始抓log,关键信息早就没了。正确的做法是:先开logcat再复现问题

抓取命令很简单:

# 实时抓取,过滤包名
adb logcat -s com.example.app

# 保存到文件,带时间戳
adb logcat -v threadtime > crash.log

# 清空缓冲区后再抓
adb logcat -c && adb logcat -v threadtime > fresh.log

2. Native层崩溃(Tombstone)

这类问题更棘手。比如系统服务挂了、SurfaceFlinger崩了,logcat里只会看到一行:

F/libc    ( 1234): Fatal signal 11 (SIGSEGV), code 1, fault addr 0xdeadbeef

这时候光看logcat是不够的。你需要配合tombstone文件来看。tombstone文件保存在 /data/tombstones/ 目录下,里面包含了完整的寄存器信息、堆栈回溯。

我的习惯: 遇到Native崩溃,我一般会同时抓三样东西:logcat、tombstone、还有dmesg。为什么?因为dmesg里可能藏着内核态的内存访问异常,能帮你快速定位是空指针还是野指针。

二、dmesg:内核的“悄悄话”

dmesg很多人不重视,觉得那是内核工程师才看的。其实不然。很多系统稳定性问题,根因就在内核态。

举个例子。手机突然重启了,logcat里只有“Kernel panic - not syncing”几个字,然后就没然后了。这时候dmesg就是救命稻草。

# 抓取dmesg
adb shell dmesg > dmesg.log

# 实时监控内核日志
adb shell dmesg -w

dmesg里常见的关键信息:

  • Watchdog timeout:某个内核线程卡住了,触发了看门狗
  • Out of memory:内存不足,LMK(Low Memory Killer)开始杀进程
  • IRQ storm:中断风暴,某个外设疯狂触发中断
  • Page allocation failure:内存碎片化严重,分配连续物理内存失败

实战案例: 我记得有一次,某款手机在玩游戏时频繁重启。logcat里只有“Kernel panic”,看不出原因。我抓了dmesg一看,发现每次重启前都有“watchdog: BUG: soft lockup - CPU#0 stuck for 22s!”的日志。顺着这个线索,最终定位到是GPU驱动的一个死循环问题。

三、kernel log:比dmesg更细的“显微镜”

dmesg其实只是kernel log的一部分。完整的kernel log还包括:

  • printk:内核代码里的调试打印
  • trace event:通过ftrace框架记录的跟踪事件
  • pstore:系统崩溃后保留的“遗言”

抓取方式:

# 查看内核日志缓冲区大小
adb shell cat /proc/sys/kernel/printk

# 调整日志级别(0-7,数字越小越紧急)
adb shell "echo 8 > /proc/sys/kernel/printk"

# 抓取完整的kernel log(需要root)
adb shell cat /proc/kmsg > kernel.log
注意: /proc/kmsg 只能被root用户读取。非root设备可以用 dmesg 替代,但信息量会少一些。另外,kmsg 是阻塞读取的,读完后其他进程就收不到日志了,所以别长时间挂着。

四、高效定位问题的“三板斧”

光知道怎么抓日志还不够,关键是怎么用。我总结了一套自己的方法,叫“三板斧”:

第一板斧:看时间线

把logcat、dmesg、tombstone的时间戳对齐。比如系统在10:00:00重启了,那就在10:00:00前后找线索。dmesg里可能10秒前就有“Out of memory”的警告,logcat里可能30秒前就有某个服务反复重启的日志。

第二板斧:找异常模式

不要只看一次崩溃的日志。我习惯把同一类问题的日志收集3-5份,对比看有没有共同点。比如每次崩溃前都有“SurfaceFlinger: waitForVsync timeout”的日志,那大概率是VSYNC相关的问题。

第三板斧:用工具辅助

手动翻日志太累了。我常用的工具有:

工具 用途 命令示例
grep 过滤关键字 grep -i "error\|fatal\|panic" logcat.log
awk 提取特定字段 awk '{print $1, $2, $5}' dmesg.log
logcat -b 查看不同缓冲区 logcat -b events -b system -b main
bugreport 一键收集所有日志 adb bugreport bugreport.zip

我的小技巧: 遇到疑难杂症,我一般先跑一个 adb bugreport。这个命令会把logcat、dmesg、dumpstate、anr traces等所有信息打包成一个zip文件。虽然文件很大(有时候上百MB),但信息最全。然后我用 grep 在里面搜关键字,效率比一个一个文件翻高多了。

五、实战:一个完整的定位流程

说了这么多,咱们来走一遍完整的流程。假设用户反馈手机频繁重启:

  1. 第一步:抓bugreport
    adb bugreport bugreport_restart.zip
  2. 第二步:看dmesg

    解压后找到 dmesg.txt,搜“panic”或“watchdog”。如果看到“Kernel panic - not syncing: Fatal exception”,说明是内核崩溃。

  3. 第三步:看tombstone

    FS/data/tombstones/ 目录下找最新的tombstone文件。里面会记录崩溃时的调用栈。比如看到“#00 pc 0001a2b4 /system/lib/libc.so (abort+4)”,说明是libc的abort触发的。

  4. 第四步:交叉验证

    在logcat里搜“abort”或“SIGABRT”,看是哪个进程调用了abort。如果发现是 system_server 调用的,那问题就锁定在系统服务层了。

总结一下: 日志系统就像侦探手里的放大镜。logcat看应用层,dmesg看内核层,tombstone看Native层。三样东西结合起来,大部分稳定性问题都能找到根因。嗯,说白了就是“多维度、交叉验证”这六个字。

最后提醒一句:日志抓取要趁早。别等问题复现了才想起来抓,那时候黄花菜都凉了。我习惯在设备上一开机就开logcat和dmesg的循环抓取,这样问题一出现,日志就已经在手里了。