Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断

系列目录第一篇:异常机制全景图 | [第二篇:Kernel Panic 与系统重启](#第二篇:Kernel Panic 与系统重启) | [第三篇:Tombstone 机制](#第三篇:Tombstone 机制) | [第四篇:System Server Watchdog](#第四篇:System Server Watchdog) | [第五篇:System Server 崩溃](#第五篇:System Server 崩溃) | [第六篇:ANR 机制](#第六篇:ANR 机制) | [第七篇:Java 层崩溃](#第七篇:Java 层崩溃) | [第八篇:Trace 机制](#第八篇:Trace 机制) | 第九篇:日志系统 | 第十篇:实战方法论


一、为什么需要 Trace 机制?

你可能遇到过这些场景:

  • 用户反馈 App 滑动卡顿,但 logcat 里没有任何异常日志
  • 系统启动慢,但不知道卡在哪个阶段
  • 掉帧严重,但无法定位是渲染慢还是 CPU 调度问题
  • 系统服务调用响应慢,但不知道延迟发生在哪一端

前几篇文章介绍的 tombstone、ANR、Watchdog 都是事后分析------异常已经发生,我们只能从日志中还原现场。但很多问题(如系统卡顿、掉帧、响应慢)并不会触发明确的异常,却严重影响用户体验。Trace 机制正是为了解决这类"没有硬错误,但有严重问题"的场景。

Trace 能回答的问题

问题类型 Trace 能提供的信息
UI 卡顿 每个帧的渲染耗时、哪些线程在竞争 CPU
启动慢 启动过程中各阶段耗时、Binder 调用延迟
掉帧 SurfaceFlinger 合成时间线、VSYNC 信号
系统服务慢 Binder 调用耗时、锁竞争、线程调度
功耗异常 CPU 频率分布、各进程的 CPU 时间片
IO 阻塞 磁盘读写耗时、I/O 调度延迟

二、Trace 体系全景

在 AOSP 7 中,trace 体系分为三层:

复制代码
┌──────────────────────────────────────────────┐
│  用户态工具层                                 │
│  ├─ systrace (systrace.py)                   │
│  ├─ atrace 命令行工具                         │
│  └─ perfetto(AOSP 7 后引入,暂不展开)        │
├──────────────────────────────────────────────┤
│  Framework 打点层                            │
│  ├─ Trace.java (android.os.Trace)            │
│  ├─ ATrace (native 层)                       │
│  └─ 各系统服务的 trace tag                   │
├──────────────────────────────────────────────┤
│  内核追踪层                                   │
│  ├─ ftrace (tracefs)                         │
│  ├─ tracepoint / kprobe / uprobe            │
│  └─ trace_marker (用户态写入内核 trace buffer) │
└──────────────────────────────────────────────┘

三、ftrace ------ 内核追踪基础

3.1 ftrace 是什么

ftrace 是 Linux 内核内置的追踪框架,通过 tracefs 文件系统暴露接口。它是 Android 所有 trace 工具的底层基础。

3.2 挂载与结构

bash 复制代码
# Android 中 ftrace 挂载在
mount -t tracefs none /sys/kernel/tracing
# 或旧版本
mount -t debugfs none /sys/kernel/debug

# 关键目录结构
/sys/kernel/tracing/
├─ available_tracers    # 可用的追踪器
├─ current_tracer       # 当前使用的追踪器
├─ tracing_on           # 1=开启 0=暂停
├─ trace                # 追踪结果输出
├─ trace_marker         # 用户态写入标记
├─ events/              # tracepoint 事件
│   ├─ sched/           # 调度事件
│   ├─ binder/          # Binder 事件
│   ├─ power/           # 电源管理事件
│   └─ ...
└─ options/             # 追踪选项

3.3 trace_marker 用户态打点

这是连接用户态和内核态的重要桥梁------用户态通过向 trace_marker 文件写入特定格式的数据,将标记注入内核 trace buffer:

c 复制代码
// 用户态写入 trace 标记
// frameworks/native/libs/utils/Trace.cpp
static void traceInit() {
    // 打开 trace_marker 文件
    atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY);
}

void ATrace_begin(const char* name) {
    // 写入 'B|<pid>|<name>' 到 trace_marker
    char buf[1024];
    snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
    write(atrace_marker_fd, buf, strlen(buf));
}

void ATrace_end() {
    // 写入 'E' 到 trace_marker
    write(atrace_marker_fd, "E", 1);
}

关键设计 :通过简单的文件写入操作(write()),用户态代码就能将打点信息注入内核 trace buffer,实现跨进程的精确时间同步。格式 B|<pid>|<name> 中的 B 表示 begin,E 表示 end。

3.4 关键 tracepoint

类别 tracepoint 用途
调度 sched/sched_switch 线程切换,找出 CPU 竞争
调度 sched/sched_wakeup 线程被唤醒,追踪唤醒延迟
频率 power/cpu_frequency CPU 频率变化,分析性能瓶颈
Binder binder/binder_transaction Binder 调用耗时
Binder binder/binder_transaction_received Binder 接收时间
图形 mdss/mdp_vsync_* 显示 VSYNC 信号
IO block/block_rq_issue IO 请求发起
内存 kmem/kmalloc 内存分配追踪

四、atrace ------ Android 用户态追踪

4.1 atrace 是什么

atrace 是 Android 提供的命令行工具,封装了 ftrace 的配置和数据采集。

bash 复制代码
# 查看支持的 trace category
adb shell atrace --list_categories

# 典型输出:
 gfx - Graphics
 input - Input
 view - View System
 webview - WebView
 wm - Window Manager
 am - Activity Manager
 sm - Sync Manager
 audio - Audio
 video - Video
 camera - Camera
 hal - Hardware Modules
 app - Application
 res - Resource Loading
 dalvik - Dalvik VM
 rs - RenderScript
 bionic - Bionic C Library
 power - Power Management
 pm - Package Manager
 sched - CPU Scheduling
 freq - CPU Frequency
 idle - CPU Idle
 load - CPU Load
 disk - Disk I/O
 mmc - eMMC commands
 ...

4.2 常用 atrace 命令

bash 复制代码
# 基础用法:追踪 10 秒,抓取 sched 和 gfx 类别
adb shell atrace -t 10 sched gfx > trace.out

# 异步模式:后台启动追踪
adb shell atrace --async_start sched freq gfx
# ... 执行测试操作 ...
adb shell atrace --async_stop > trace.out

# 追加 App 自定义 trace(需要 App 代码中使用 Trace API)
adb shell atrace -t 10 -a com.example.app sched gfx

# 输出压缩格式(减少传输大小)
adb shell atrace -t 10 -z sched gfx > trace.out

五、systrace ------ 可视化分析

5.1 systrace 是什么

systrace 是 Android SDK 提供的 Python 脚本,能够:

  1. 调用 atrace 收集原始数据
  2. 将数据转换为 HTML 可视化报告
  3. 提供交互式分析界面
bash 复制代码
# 使用 systrace.py(位于 sdk/platform-tools/systrace/)
python systrace.py -t 10 -o trace.html sched freq gfx input view

5.2 Systrace 报告解读

报告分为多个 Section:

CPU 调度区域

复制代码
每个 CPU 核心一行,显示在该核心上运行的线程:
CPU 0: [surfaceflinger] [app] [idle] [surfaceflinger] ...
CPU 1: [Binder:1234] [idle] [app] [Binder:1234] ...

解读:
- 线程名可以看出谁在占用 CPU
- 颜色块表示不同的线程
- 空白 = idle(CPU 空闲)

进程/线程区域

复制代码
每个进程一行,显示该进程各线程的状态:
com.example.app (12345)
  └─ main (12345):  [RUNNING][SLEEPING][RUNNING][BLOCKED]...
  └─ RenderThread (12346): [RUNNING][RUNNING]...
  └─ Binder_1 (12347): [RUNNING]...

线程状态颜色:
- 绿色 = Running
- 蓝色 = Runnable(等待 CPU)
- 白色 = Sleeping
- 橙色 = Uninterruptible Sleep(IO 等待)
- 紫色 = Blocked(等待锁)

VSYNC 与渲染管线

复制代码
VSYNC:  |   |   |   |   |   |   |
SurfaceFlinger:  ====  ====  ====
App Render:      ======  ======

每个竖线表示 VSYNC 信号,每个横条表示渲染操作所占的时间。理想情况下,App 的渲染操作应在 16.7ms(60fps)内完成。

Binder 调用

复制代码
App → Binder Transaction → System Server
|---- request ----| |---- processing ----| |---- reply ----|

Binder 区域显示了跨进程调用的完整时间线,可以直观看出哪个方向的延迟最大。

5.3 关键快捷键

快捷键 功能
W/S 放大/缩小
A/D 左右平移
M 标记当前选择的时间范围
F 聚焦当前选择
1-4 切换不同细节级别
? 显示所有快捷键

六、API 打点:在代码中埋点

6.1 Java 层

源码路径frameworks/base/core/java/android/os/Trace.java

java 复制代码
public final class Trace {
    // ...
    public static final long TRACE_TAG_APP = 1L << 12;
    private static final int MAX_SECTION_NAME_LEN = 127;

    // 通过 native 方法写入 trace_marker
    private static native void nativeTraceBegin(long tag, String name);
    private static native void nativeTraceEnd(long tag);

    // App 开发者使用的便捷 API
    public static void beginSection(String sectionName) {
        if (isTagEnabled(TRACE_TAG_APP)) {
            if (sectionName.length() > MAX_SECTION_NAME_LEN) {
                throw new IllegalArgumentException("sectionName is too long");
            }
            nativeTraceBegin(TRACE_TAG_APP, sectionName);
        }
    }

    public static void endSection() {
        if (isTagEnabled(TRACE_TAG_APP)) {
            nativeTraceEnd(TRACE_TAG_APP);
        }
    }
}

关键设计beginSection/endSection 使用 TRACE_TAG_APP tag,需要 atrace 命令中加 -a <package> 参数才能采集到。MAX_SECTION_NAME_LEN 限制名称最长 127 字符。

使用示例:

java 复制代码
import android.os.Trace;

public void doHeavyWork() {
    Trace.beginSection("doHeavyWork");
    try {
        processData();
        Trace.beginSection("processData_sub");
        // 子步骤
        Trace.endSection();
    } finally {
        Trace.endSection();
    }
}

6.2 Native 层

cpp 复制代码
#include <utils/Trace.h>

void doHeavyWork() {
    ATRACE_BEGIN("doHeavyWork");
    // 耗时操作
    ATRACE_END();
}

七、结合异常定位的实战案例

案例 1:UI 卡顿分析

复制代码
问题:用户反馈 App 滑动卡顿

分析步骤:
1. 抓取 systrace:systrace.py -t 5 -o trace.html sched freq gfx input view
2. 打开 trace.html,找到 App 的 main 线程
3. 观察 VSYNC 信号和渲染完成时间
4. 发现:某个帧的渲染耗时 80ms(远超 16.7ms)
5. 放大该帧时间范围,发现 RenderThread 在等待 CPU
6. 查看 CPU 调度区域,发现同时有后台线程在密集计算
7. 结论:后台任务与渲染线程抢 CPU,导致掉帧
8. 修复:将后台任务移到 cpuset 后台组,降低优先级

案例 2:ANR 前兆分析

复制代码
问题:App 偶尔 ANR,但 traces.txt 中主线程没有明显阻塞

分析步骤:
1. 抓取 systrace + CPU 调度
2. 发现 ANR 发生前,所有 CPU 核心 100% 占用
3. 主线程处于 Runnable 状态(等待 CPU),而非 Blocked
4. 结论:系统整体 CPU 过载,主线程分不到 CPU 时间片
5. 修复:优化后台任务,降低 CPU 使用率

案例 3:System Server 慢

复制代码
问题:系统服务调用响应慢

分析步骤:
1. systrace 中开启 binder 类别
2. 观察 Binder 调用链路
3. 发现 AMS 的某个方法耗时 500ms
4. 进一步查看 AMS 线程的 trace
5. 发现 AMS 在等待 PackageManager 的锁
6. 结论:PackageManager 持有锁时间过长,导致连锁反应

八、使用技巧与最佳实践

8.1 选择合适的 trace 类别

bash 复制代码
# 性能分析(最小集)
sched freq

# UI 卡顿
sched freq gfx input view

# Binder 问题
sched freq binder

# 启动速度
sched freq am wm

# 内存问题
sched freq memreclaim

# 全盘分析(注意数据量)
sched freq gfx input view wm am binder disk

8.2 减小 trace 开销

复制代码
- 限制追踪时间:-t 5(5 秒通常足够)
- 只追踪必要的 App:-a com.example.app
- 使用压缩格式:-z
- 减少不相关的 category
- 在非关键路径上减少 Trace.beginSection/endSection 调用

8.3 脚本化收集

bash 复制代码
#!/bin/bash
# 一键收集异常现场的 trace 数据
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

# 1. 抓取 atrace
adb shell atrace -t 10 -z sched freq gfx input view binder \
    > trace_${TIMESTAMP}.trace

# 2. 导出 logcat
adb logcat -d > logcat_${TIMESTAMP}.txt

# 3. 导出 dmesg
adb shell dmesg > dmesg_${TIMESTAMP}.txt

# 4. 导出 ANR traces
adb pull /data/anr/traces.txt traces_${TIMESTAMP}.txt

echo "All logs collected with timestamp: ${TIMESTAMP}"

九、总结

  1. Trace 是"事前监控"能力:不等异常发生,而是持续观察系统运行状态,发现性能瓶颈。

  2. 三层架构:ftrace(内核)→ atrace(Android 封装)→ systrace(可视化分析)。

  3. trace_marker 是用户态与内核态的桥梁 :通过 ATrace_begin/endTrace.beginSection/endSection 将应用级打点注入内核 trace buffer。

  4. systrace HTML 报告是核心分析工具:按时间轴展示 CPU 调度、线程状态、Binder 调用、渲染管线。

  5. 结合异常定位:systrace 可以补充 ANR/Watchdog 分析中缺失的"时间线上下文",帮助判断是锁阻塞还是 CPU 竞争。

下一篇将介绍 Android 日志系统全景------从 logcat 到 bugreport。注意:新一代 trace 工具 perfetto 在 Android 10+ 中成为默认,AOSP 7 以 atrace/systrace 为主。


本文基于 AOSP 7(Android Nougat)源码编写

相关推荐
极客猴子7 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker7 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone7 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常
雾屿_Mistisle7 小时前
文件包含漏洞(File Inclusion)
android
小孔龙8 小时前
Android 图形系统全景
android·计算机图形学
2501_915921438 小时前
详细解析,iOS 应用上架 App Store 的完整流程与指南
android·ios·小程序·https·uni-app·iphone·webview
qq_425516188 小时前
录音转文字工具免费下载:免费额度与功能限制对比
android·人工智能·智能手机·powerpoint
plainGeekDev9 小时前
Robolectric → 分层测试:测试策略重构
android·java·kotlin
plainGeekDev9 小时前
Instrumentation → Compose Testing
android·java·kotlin