在嵌入式Linux系统中,perf 是一款轻量且强大的性能分析工具,基于内核性能计数器(PMU,Performance Monitoring Unit),可用于分析 CPU 热点、函数调用耗时、调度行为、中断开销等关键性能指标。相比其他工具(如 gprof),perf 对系统侵入性小,支持用户态和内核态分析,非常适合资源受限的嵌入式环境。

一、嵌入式环境下使用 perf 的前提条件

在使用 perf 前,需确保嵌入式系统满足以下条件:

1. 内核配置支持

需开启内核配置项(通过 make menuconfig 配置):

CONFIG_PERF_EVENTS=y                # 启用 perf 核心功能
CONFIG_PERF_USE_VMALLOC=y           # 允许 perf 使用 vmalloc 分配内存(嵌入式常用)
CONFIG_DEBUG_INFO=y                 # 生成内核调试信息(用于解析内核符号)
CONFIG_KALLSYMS=y                   # 启用内核符号表(显示内核函数名)
CONFIG_FRAME_POINTER=y              # 启用帧指针(提升调用栈解析准确性)

根据嵌入式芯片架构(如 ARM、MIPS),可能还需要开启架构相关的 PMU 支持(如 CONFIG_ARM_PMU=yCONFIG_ARM64_PMU=y)。

2. 交叉编译 perf 工具

perf 工具源码位于 Linux 内核源码的 tools/perf 目录,需针对嵌入式目标架构(如 ARM)进行交叉编译:

# 进入 perf 源码目录
cd linux-src/tools/perf

# 交叉编译(以 ARM 架构为例,交叉编译器为 arm-linux-gnueabihf-gcc)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- \
     NO_LIBELF=0 NO_LIBUNWIND=1 NO_DWARF=1
  • 编译选项说明:
    • NO_LIBELF=0:启用 ELF 解析(必要,用于符号解析);
    • NO_LIBUNWIND=1/NO_DWARF=1:禁用 unwind/dwarf(嵌入式通常不支持,减少依赖);
    • 若目标系统无 libelf,可编译静态版本:LDFLAGS=-static

编译完成后,会生成 perf 可执行文件,将其拷贝到嵌入式设备的 /usr/bin/bin 目录(需确保可执行权限:chmod +x /usr/bin/perf)。

二、perf 核心用法(嵌入式场景常用)

perf 功能丰富,以下是嵌入式性能分析中最常用的场景:

1. 分析 CPU 热点函数(定位“谁在占用 CPU”)

通过采样 CPU 指令执行,识别最耗时的函数(用户态/内核态均可),是性能优化的起点。

步骤:

  • 采样目标进程/系统

    # 跟踪单个进程(<pid> 为进程 ID),记录调用栈(-g),采样 10 秒后退出
    perf record -g -p <pid> -- sleep 10
    
    # 跟踪整个系统的所有进程(适合找不到具体进程的场景)
    perf record -g -a -- sleep 10
    
    • -g:记录函数调用栈(关键,用于分析函数调用关系);
    • -F 1000:可选,指定采样频率(默认 1000Hz,嵌入式可降低至 100Hz 减少开销);
    • 采样会生成 perf.data 文件(存储在当前目录)。
  • 分析采样结果

    perf report
    

    输出结果按 CPU 占用率排序,显示每个函数的采样次数(占比),例如:

    90.12%  app_name  app_name      [.] loop_function
     5.34%  app_name  libc.so       [.] memcpy
     2.15%  kernel    [kernel]      [k] __schedule
    

    可直观看到 loop_function 占用了 90% 的 CPU,需优先优化。

2. 统计关键性能事件(量化性能指标)

perf stat 用于统计指定事件(如 CPU 周期、指令数、缓存命中/失效、分支预测失败等)的发生次数,适合量化性能瓶颈。

示例:

# 统计进程 <pid> 在 5 秒内的关键事件
perf stat -p <pid> -- sleep 5

# 统计执行某个命令的事件(如运行 ./test_app)
perf stat ./test_app

输出示例:

 Performance counter stats for './test_app':

       123456789      cycles:u                  # 用户态 CPU 周期
       234567890      instructions:u            # 用户态指令数
        12345678      cache-misses:u            # 用户态缓存失效
            1234      branch-misses:u           # 分支预测失败

       0.567890123 seconds time elapsed
  • 关键指标解读:
    • instructions/cycles(IPC,每周期指令数):值越高,CPU 利用率越高效(理想接近 1);
    • cache-misses 过高:说明代码/数据访问模式不佳,需优化缓存利用(如调整数据结构、循环顺序);
    • branch-misses 过高:条件分支过多,可通过查表、分支预测优化。
3. 跟踪系统调用与内核行为

perf trace 可跟踪进程的系统调用(如 read/write/ioctl)及其耗时,定位用户态与内核态交互的瓶颈(如 IO 延迟)。

示例:

# 跟踪进程 <pid> 的系统调用
perf trace -p <pid>

# 跟踪系统调用并显示耗时(单位 ms)
perf trace -p <pid> --duration 1  # 只显示耗时 >1ms 的调用

输出示例:

     0.000 ms: app_name/1234  read(3, "data...", 1024) = 1024
     5.678 ms: app_name/1234  ioctl(5, 0x1234, 0xaddr) = 0
    10.123 ms: app_name/1234  write(4, "output...", 512) = 512

ioctlwrite 耗时过长,可能是驱动效率低或硬件 IO 性能不足。

4. 分析调度延迟(实时嵌入式系统)

在实时嵌入式场景(如工业控制),调度延迟(任务从就绪到运行的时间)是关键指标,perf 可结合调度事件跟踪:

# 跟踪调度事件(sched_switch 进程切换,sched_wakeup 唤醒)
perf record -e 'sched:sched_switch' -e 'sched:sched_wakeup' -a -- sleep 10

# 分析调度事件
perf script  # 输出原始事件日志,包含进程切换时间、优先级等

通过日志可计算:

  • 进程被唤醒后多久开始运行(wakeupswitch 的时间差);
  • 高优先级进程是否被低优先级进程阻塞(实时性问题)。

三、嵌入式场景的进阶技巧

1. 解决符号解析问题

嵌入式系统通常不会存储完整的调试符号(节省空间),导致 perf report 显示 [unknown] 或地址。解决方法:

  • 主机端 分析:将目标板的 perf.data、可执行文件(带调试符号)、内核镜像拷贝到主机,用交叉编译的 perf 解析:
    # 主机端执行(指定目标板的二进制文件路径)
    arm-linux-gnueabihf-perf report --symfs /path/to/target-rootfs
    
    其中 /path/to/target-rootfs 是目标板根文件系统的主机镜像(包含带符号的 binlib 等)。
2. 减少 perf.data 体积

嵌入式存储有限,perf record 生成的 perf.data 可能过大,可:

  • 降低采样频率:-F 100(默认 1000Hz);
  • 限制采样时间:-- sleep 5(只采样 5 秒);
  • 仅跟踪特定事件:-e cycles(只记录 CPU 周期,不记录其他事件);
  • 存储到内存文件系统:perf record -o /tmp/perf.data ...(避免写 flash)。
3. 结合 ftrace 分析内核细节

perf 可与内核 ftrace 结合,深入分析内核函数(如调度器、中断处理):

# 跟踪内核函数 schedule(进程调度)的调用
perf record -e 'ftrace:function_entry' -e 'ftrace:function_exit' \
            -F schedule -a -- sleep 5

四、常见问题与解决

  1. perf: error: no such tool 'record'
    原因:编译 perf 时缺少依赖(如 libelf),需确保交叉编译时启用 NO_LIBELF=0 并安装交叉编译的 libelf。

  2. 采样结果中内核函数显示为地址
    原因:内核未开启 CONFIG_KALLSYMS 或未加载带符号的内核镜像,需重新配置内核并生成带符号的 vmlinux

  3. Permission denied 错误
    perf 需要 root 权限执行(访问内核性能计数器),需用 sudo perf ... 或切换到 root 用户。

总结

perf 是嵌入式 Linux 性能分析的“瑞士军刀”,核心流程为:

  1. 交叉编译 perf 并部署到目标板;
  2. perf record 采样目标进程/系统;
  3. perf reportperf stat 分析热点函数/性能事件;
  4. 结合符号解析和主机端分析优化结果。

通过 perf 可快速定位 CPU 瓶颈、调度延迟、IO 交互等问题,是嵌入式系统性能优化的必备工具。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐