21、系统调试与性能优化:logcat、systrace、perfetto、内存泄漏检测

做嵌入式Android开发,说白了就是跟资源较劲。CPU就那么点频率,内存就那么几百兆,屏幕刷新率还卡着上限。系统跑着跑着卡顿了,或者莫名其妙内存爆了,这时候你怎么办?

我刚开始做嵌入式系统时,遇到问题第一反应就是加日志。加了一堆Log.d,跑起来一看,全是垃圾信息。后来才明白——调试不是靠蛮力,是靠工具。今天我就把几个压箱底的调试工具掰开揉碎讲给你听。

核心观点:嵌入式调试的本质是「定位瓶颈」。logcat帮你找逻辑错误,systrace和perfetto帮你找性能瓶颈,内存检测帮你找资源泄漏。缺一不可。

21.1 logcat:最基础的日志工具

logcat是Android日志系统的核心。它把系统日志分成了几个等级:V(Verbose)、D(Debug)、I(Info)、W(Warning)、E(Error)。我个人的习惯是,开发阶段用D,发布前全改成W以上,不然日志量太大。

常用的过滤方式有几种:

  • 按标签过滤:adb logcat -s MyAppTag,只显示你关心的日志
  • 按优先级过滤:adb logcat *:W,只看警告和错误
  • 按进程过滤:adb logcat --pid=1234,只看某个进程的日志

我在项目中遇到过一个问题:某个传感器数据偶尔丢失,但日志里全是系统其他模块的垃圾信息。后来我用adb logcat -b main -s SensorHub只抓传感器相关的日志,问题立刻暴露了——是某个中断处理函数里忘了释放锁。

小技巧:logcat支持多级缓冲。用-b main抓主日志,-b events抓事件日志,-b crash抓崩溃日志。崩溃日志单独抓,不会被其他日志冲掉。

21.2 systrace:系统级性能追踪

systrace是Google早期推出的性能分析工具。它通过ftrace机制,把CPU调度、进程状态、内核事件全部记录下来。说白了,就是给系统拍一张「心电图」。

使用方式很简单:

# 抓取5秒的trace
python systrace.py -t 5 -o mytrace.html

# 只抓特定模块
python systrace.py -t 5 -o mytrace.html gfx input view webview

生成的HTML文件可以在浏览器里打开。你会看到一条时间轴,上面有各个进程的运行状态。绿色表示运行中,蓝色表示可运行,白色表示休眠。如果某个进程长时间处于蓝色状态,说明它在等CPU——嗯,这就是瓶颈。

我记得有一次,系统UI滑动卡顿。用systrace一看,发现SurfaceFlinger进程每隔几百毫秒就出现一个红色标记,点开一看,是GPU渲染超时。后来把某个动画的帧率从60fps降到30fps,问题解决了。

注意:systrace在Android 10之后已经被Perfetto取代。但很多老设备还在用,所以这个工具你还是要会。另外,systrace抓取时间不宜过长,5-10秒就够了,否则文件太大打不开。

21.3 Perfetto:新一代性能追踪工具

Perfetto是systrace的升级版。它支持更细粒度的追踪,比如内存分配、文件I/O、网络请求。而且它的数据格式是protobuf,解析效率更高。

Perfetto有两种使用方式:

  • 命令行模式:adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace -t 10s sched freq gfx
  • UI模式:在浏览器打开ui.perfetto.dev,直接拖拽trace文件分析

我个人更喜欢用Perfetto的SQL分析功能。它把trace数据存成了SQLite表,你可以写SQL查询。比如:

-- 查询所有运行时间超过10ms的slice
SELECT name, dur FROM slice WHERE dur > 10000000 ORDER BY dur DESC

这个功能太强了。你想想看,以前用systrace只能肉眼盯着时间轴找异常,现在一条SQL就能把最耗时的操作列出来。我在做低功耗优化时,就用这个查到了某个定时器回调里做了大量字符串拼接,导致CPU频繁唤醒。

对比总结:systrace适合快速看整体情况,Perfetto适合深入分析具体问题。如果你还在用systrace,我建议尽快切换到Perfetto。

21.4 内存泄漏检测

内存泄漏是嵌入式开发的噩梦。系统内存本来就小,泄漏一点就崩。我见过一个项目,跑了两天之后系统自动重启,查了半个月才发现是某个Service里持有了Activity的引用。

常用的检测工具有:

工具 原理 适用场景
LeakCanary 基于WeakReference和ReferenceQueue 应用层内存泄漏
MAT (Memory Analyzer Tool) 分析heap dump文件 深入分析对象引用链
Android Profiler 实时监控内存使用 快速定位内存波动

LeakCanary是我最常用的。它会在检测到泄漏时自动生成一个heap dump,并给出泄漏路径。比如:

// 典型的泄漏路径
com.example.MyActivity
  └─ com.example.MySingleton (持有Activity的Context)
    └─ android.content.Context

看到这个路径,你就知道是单例持有了Activity的Context。解决办法很简单:把Context换成ApplicationContext。

避坑指南:我曾经在某个项目里发现内存泄漏,但LeakCanary死活检测不出来。后来手动dump了heap,用MAT分析,发现是JNI层分配的内存没有释放。记住,LeakCanary只检测Java堆,不检测Native堆。Native内存泄漏要用malloc debugAddressSanitizer

21.5 实战:综合调试流程

说了这么多工具,到底怎么用?我总结了一套流程:

  1. 先看logcat:有没有明显的错误日志?比如NullPointerException、OutOfMemoryError
  2. 再用Perfetto:抓一段trace,看CPU、GPU、内存的使用情况。有没有某个进程长时间占用CPU?
  3. 然后查内存:用Android Profiler看内存曲线。如果持续增长,大概率有泄漏
  4. 最后用LeakCanary:定位具体的泄漏点和引用链

这套流程我用了好几年,基本能解决90%的性能问题。剩下的10%,嗯,那就得看硬件了——比如某个外设驱动有bug,或者DMA传输有冲突,那就不是软件工具能解决的了。

核心原则:调试不是一次性的,而是迭代的。改一个问题,再抓一次trace,看看有没有引入新问题。尤其是嵌入式系统,牵一发而动全身。

系统调试与性能优化知识体系 调试与优化 logcat 日志系统 systrace 性能追踪 Perfetto 新一代追踪 内存泄漏检测 按标签/优先级过滤 多级缓冲 -b main/events CPU调度/进程状态 时间轴可视化分析 SQL查询分析 内存/文件I/O追踪 LeakCanary/Android Profiler MAT heap dump分析 综合调试流程:logcat → Perfetto → 内存分析 → 定位修复 迭代验证,避免引入新问题

公众号:蓝海资料掘金营,微信deep3321