2. 嵌入式系统调试基础

性能剖析,说白了就是给嵌入式系统做“体检”。

我见过太多工程师,代码功能跑通了就急着交付。结果呢?现场一跑,CPU飙到100%,或者内存泄漏三天后死机。嗯,这时候再回头找问题,成本可就高了。

这一章,咱们就聊聊性能瓶颈长什么样,手头有哪些趁手的工具,以及两种核心的剖析思路——采样和插桩,到底该怎么选。

2.1 性能瓶颈的常见表现

先说说我这些年踩过的坑。性能问题不会直接告诉你“我慢了”,它通常会伪装成下面几种样子:

  • CPU 占用率居高不下:一个任务占用了90%以上的CPU,其他任务饿死。我在一个工业控制器项目里遇到过,一个轮询串口的死循环把整机拖垮了。
  • 响应时间抖动(Jitter):大部分时候响应很快,偶尔一次慢得像死机。这通常是中断处理太长,或者关中断时间太久。
  • 内存持续增长:内存占用只升不降,最终OOM。我调试过一个网络协议栈,每次收包都malloc,但只有特定路径才free——典型的“内存泄漏”。
  • 任务调度延迟:高优先级任务没有及时得到执行。原因往往是低优先级任务占了锁不放,或者中断风暴。
  • Cache Miss 过高:CPU流水线频繁停顿。代码和数据布局不合理,导致大量Cache Miss,实际执行效率远低于理论值。

核心观点:性能瓶颈的根源,要么是“计算太多”,要么是“等待太多”。前者看CPU占用,后者看I/O、锁、调度延迟。

2.2 Profiling 工具链介绍

工具不在多,在于会用。嵌入式领域,我常用的三把刀是 perf、gprof 和 ftrace。它们各有各的脾气。

2.2.1 perf —— 内核级性能剖析利器

perf 是 Linux 内核自带的性能分析工具。它利用硬件性能计数器(PMU)和内核跟踪点,几乎零开销地采集数据。

我个人习惯用它来定位“CPU到底在忙什么”。

常用命令速查:

命令 作用
perf top 实时显示当前最耗CPU的函数
perf record -g ./app 采样记录程序运行时的调用栈
perf report 分析采样结果,生成热点报告
perf stat ./app 统计程序运行时的硬件事件(如周期数、Cache Miss)

我的经验:在嵌入式板子上,先用 perf list 看看硬件支持哪些事件。有些低端ARM芯片的PMU事件不全,这时候就得靠软件采样了。

2.2.2 gprof —— 函数级调用图分析

gprof 是 GNU 工具链的一部分。它通过编译时插桩(-pg 选项),在函数入口和出口插入计数代码。

它适合做“函数调用关系”和“执行时间分布”的静态分析。但要注意,gprof 对多线程和中断上下文基本无能为力。

使用步骤:

  1. 编译时加 -pg 选项:gcc -pg -o app app.c
  2. 运行程序,正常退出后会生成 gmon.out 文件
  3. 执行 gprof app gmon.out 查看报告

避坑指南:我曾经在一个长期运行的服务器程序上用 gprof,结果 gmon.out 文件巨大,分析工具直接卡死。gprof 更适合短时间、单线程的测试场景。

2.2.3 ftrace —— 内核行为跟踪器

ftrace 是 Linux 内核内置的跟踪框架。它可以跟踪函数调用、调度事件、中断上下文等。

当你怀疑问题出在内核态(比如驱动、调度、中断)时,ftrace 是首选。

快速上手:

# 挂载 tracefs
mount -t tracefs tracefs /sys/kernel/tracing

# 设置跟踪器为 function_graph
echo function_graph > /sys/kernel/tracing/current_tracer

# 开始跟踪
echo 1 > /sys/kernel/tracing/tracing_on

# 运行你的测试程序...

# 停止跟踪
echo 0 > /sys/kernel/tracing/tracing_on

# 查看结果
cat /sys/kernel/tracing/trace

我的习惯:先用 function_graph 看整体调用链,找到可疑函数后,再用 function 跟踪器配合 set_ftrace_filter 精确跟踪特定函数。

2.3 采样与插桩的区别

这是性能剖析里最基础、也最容易混淆的概念。说白了,就是两种不同的“偷看”方式。

2.3.1 采样(Sampling)

采样就像每隔一段时间拍一张快照。它不修改你的代码,只是定期检查程序计数器(PC)或调用栈。

  • 优点:开销极小,适合生产环境;可以分析任意代码,包括库和内核。
  • 缺点:统计结果有误差,低频事件可能被漏掉;无法精确测量单次执行时间。
  • 代表工具:perf(默认模式)、OProfile。

2.3.2 插桩(Instrumentation)

插桩是在代码中插入额外的指令,比如在函数入口记录时间戳,在出口计算耗时。

  • 优点:精度高,可以精确测量每个函数的执行次数和时间;能捕获所有事件。
  • 缺点:开销大,会改变程序行为(Heisenberg效应);需要重新编译或修改代码。
  • 代表工具:gprof、ftrace(function_graph模式)、LTTng。

对比表格:

维度 采样 插桩
开销 低(通常 < 5%) 高(可能 > 50%)
精度 统计近似 精确计数
代码修改 无需 需要
适用场景 生产环境、热点定位 开发环境、详细分析
典型工具 perf, VTune gprof, ftrace

我的建议:先用采样工具(perf top)快速定位热点函数。如果热点函数内部逻辑复杂,再用插桩工具(gprof 或 ftrace)深入分析。别一上来就插桩,否则可能被海量数据淹没。

2.4 知识体系结构图

下面这张图,把本章的核心逻辑串起来了。你可以把它当作一个“性能剖析决策树”。

性能剖析知识体系结构图 性能剖析知识体系 性能瓶颈 CPU 占用高 响应时间抖动 内存持续增长 调度延迟 perf(内核级) gprof(函数级) ftrace(内核跟踪) 采样(Sampling) 插桩(Instrumentation) 采样:低开销,统计近似,适合热点定位 代表工具:perf, OProfile 插桩:高精度,改变程序行为,适合详细分析 代表工具:gprof, ftrace 从识别瓶颈 → 选择工具 → 确定方法 → 对比取舍 形成完整的性能剖析闭环

2.5 如何选择?

你可能会问:“这么多工具和方法,我到底该用哪个?”

我的经验是:先看问题现象,再选工具。

  • 如果系统整体响应慢,先用 perf top 看看哪个函数在吃CPU。
  • 如果偶尔卡顿,用 perf record -g 采样,然后分析 perf report 里的调用链。
  • 如果怀疑内核态有问题(比如中断处理太长),上 ftrace。
  • 如果只是开发阶段想优化某个模块,gprof 够用了。

注意:别在客户现场直接跑 gprof 或 ftrace 的 function_graph 模式。开销太大,可能把系统拖得更慢。生产环境首选 perf 采样。