Linux 性能实战 | 第 20 篇:trace-cmd 与 kernelshark 可视化分析 [特殊字符]

📋 本章摘要

第十九章中,我们掌握了 ftrace 的核心用法,通过直接操作 /sys/kernel/debug/tracing 接口完成了 kworker 高负载和软中断风暴的排查。但手工操作 ftrace 存在明显的痛点:

  • 操作繁琐:需要手动 echo 到多个文件,容易出错
  • 数据管理难:trace 数据无法持久化保存和分享
  • 分析低效:纯文本输出难以发现时间维度的关联性
  • 多核分析难:8 核 CPU 的并发事件难以可视化对比

本章将引入两个强大的工具:

  • trace-cmd:ftrace 的命令行封装,一条命令完成记录、分析、导出
  • kernelshark:图形化时间线分析工具,可视化多核、多事件的关联关系

我们将通过两个实战案例(网络中断风暴导致 CPU 调度冲突跨核心延迟传播分析),演示如何使用 trace-cmd + kernelshark 快速定位复杂的多核性能问题。


🚀 trace-cmd:ftrace 的瑞士军刀

1. 为什么需要 trace-cmd

手工 ftrace 的痛点

bash 复制代码
# 手工操作需要 10+ 步
cd /sys/kernel/debug/tracing
echo 0 > tracing_on
echo nop > current_tracer
echo > trace
echo function_graph > current_tracer
echo do_sys_open > set_graph_function
echo 1234 > set_ftrace_pid
echo 1 > events/sched/sched_switch/enable
echo 1 > tracing_on
sleep 10
echo 0 > tracing_on
cat trace > /tmp/output.txt

trace-cmd 一行搞定

bash 复制代码
trace-cmd record -p function_graph -g do_sys_open -P 1234 -e sched:sched_switch sleep 10
trace-cmd report > /tmp/output.txt

2. trace-cmd vs 手工 ftrace

特性 手工 ftrace trace-cmd
命令复杂度 10+ 步骤 1 条命令
数据持久化 手动重定向 自动保存 trace.dat
数据分享 难以移植 二进制格式,可跨机器分析
多核分析 手动区分 CPU 列 自动标记 CPU 信息
图形化分析 不支持 集成 kernelshark
插件扩展 不支持 支持 Python 插件
学习成本 高(需理解 ftrace 接口) 低(类似 perf 命令)

3. 安装 trace-cmd

bash 复制代码
# Ubuntu/Debian
sudo apt install trace-cmd kernelshark

# RHEL/CentOS
sudo yum install trace-cmd kernelshark

# 验证安装
trace-cmd --version
# 输出:trace-cmd version 3.1.2

kernelshark --version
# 输出:KernelShark version 2.2.0

⚙️ trace-cmd 基础使用

1. 核心命令

bash 复制代码
trace-cmd record    # 记录 trace 数据(生成 trace.dat)
trace-cmd report    # 解析 trace.dat 并输出文本
trace-cmd show      # 查看当前 ftrace 状态
trace-cmd reset     # 重置 ftrace 配置
trace-cmd list      # 列出可用的 tracers/events
trace-cmd stat      # 显示 ftrace 统计信息

2. record 命令详解

bash 复制代码
# 基本语法
trace-cmd record [选项] [命令]

# 常用选项
-p <tracer>          # 指定 tracer(function/function_graph/nop)
-e <event>           # 启用 event(支持通配符)
-P <pid>             # 过滤 PID
-g <function>        # function_graph 的函数过滤
-l <function>        # function tracer 的函数过滤
-F                   # 过滤当前命令的进程(自动设置 PID)
-O <option>          # 设置 trace_options
-b <size>            # 缓冲区大小(KB)
-o <file>            # 输出文件名(默认 trace.dat)
-d                   # 实时输出(不保存文件)
-s <usecs>           # 采样间隔(微秒)

3. 实战示例

示例 1:追踪进程的系统调用

bash 复制代码
# 追踪 ls 命令的所有系统调用
trace-cmd record -e 'syscalls:*' ls /tmp

# 查看结果
trace-cmd report | less

示例 2:追踪特定函数的调用链

bash 复制代码
# 追踪 vfs_read 的调用链
trace-cmd record -p function_graph -g vfs_read cat /etc/hosts

# 查看结果
trace-cmd report

示例 3:追踪运行中进程的调度行为

bash 复制代码
# 追踪 PID 1234 的调度事件 10 秒
trace-cmd record -e sched:sched_switch -e sched:sched_wakeup -P 1234 sleep 10

# 查看结果
trace-cmd report | grep -E "perception|sched_switch"

示例 4:自定义输出文件

bash 复制代码
# 保存到指定文件
trace-cmd record -o network_trace.dat -e net:* -e irq:* sleep 5

# 分析
trace-cmd report -i network_trace.dat

🖥️ kernelshark:时间线可视化分析

1. kernelshark 界面概览

复制代码
┌─────────────────────────────────────────────────────────────────┐
│ 文件  编辑  过滤  工具  帮助                                      │
├─────────────────────────────────────────────────────────────────┤
│ ⏵ CPU 0  ████▓▓░░████▓▓░░████▓▓░░  sched_switch, irq_handler   │
│ ⏵ CPU 1  ░░████▓▓░░████▓▓░░████▓  sched_switch, kworker       │
│ ⏵ CPU 2  ▓▓░░████▓▓░░████▓▓░░██  sched_wakeup, net_rx        │
│ ⏵ CPU 3  ████░░▓▓████░░▓▓████░░  softirq_entry, tcp_v4_rcv   │
├─────────────────────────────────────────────────────────────────┤
│ 时间轴:  |-------|-------|-------|-------|-------|          │
│          1234.0   1234.5   1235.0   1235.5   1236.0          │
├─────────────────────────────────────────────────────────────────┤
│ Event 详情:                                                     │
│ CPU: 2  Time: 1234.567890                                       │
│ Event: net:netif_receive_skb                                    │
│ dev=eth0 len=1400 protocol=IP                                   │
└─────────────────────────────────────────────────────────────────┘

2. 核心功能

功能 说明 快捷键
缩放 放大/缩小时间轴 滚轮 / +/-
平移 左右移动时间轴 拖拽 / ←→
过滤 显示/隐藏特定 event Ctrl+F
搜索 查找特定事件 Ctrl+S
标记 标记感兴趣的时间点 M
测量 测量两点间的时间差 右键选择
CPU 过滤 只显示特定 CPU 点击 CPU 行
导出 导出选中区域 Ctrl+E

3. 启动 kernelshark

bash 复制代码
# 分析 trace.dat
kernelshark trace.dat

# 或者直接从 trace-cmd 启动
trace-cmd record -e 'sched:*' -e 'irq:*' sleep 5
kernelshark trace.dat

🧪 实战案例 1:网络中断风暴导致 CPU 调度冲突

1. 问题背景

自动驾驶感知系统在处理多路摄像头数据时,出现间歇性延迟峰值(正常 20ms,峰值 150ms)。通过 mpstat 观察到 CPU 3 的软中断占用突然飙升,怀疑网络中断影响了感知进程的调度。

mpstat -P ALL 1 输出

复制代码
CPU    %usr   %sys   %irq   %soft   %idle
  0    25.3    4.2    0.5     2.1    68.0
  1    23.8    5.1    0.4     1.8    69.0
  2    78.5    8.2    0.3     1.5    11.5   ← perception 进程
  3    12.3    3.5    2.8    65.4    16.0   ← 软中断异常!

2. 使用 trace-cmd 记录

bash 复制代码
# 记录 10 秒的调度、中断、网络事件
trace-cmd record \
    -e sched:sched_switch \
    -e sched:sched_wakeup \
    -e irq:irq_handler_entry \
    -e irq:irq_handler_exit \
    -e irq:softirq_entry \
    -e irq:softirq_exit \
    -e net:netif_receive_skb \
    -b 20480 \
    sleep 10

# 生成 trace.dat(约 50MB)
ls -lh trace.dat
# 输出:-rw-r--r-- 1 user user 52M Feb 13 14:23 trace.dat

3. 使用 kernelshark 可视化分析

步骤 1:打开 trace.dat

bash 复制代码
kernelshark trace.dat

步骤 2:观察 CPU 时间线

在 kernelshark 中可以清晰看到:

  • CPU 2(perception 进程):持续运行,偶尔被中断
  • CPU 3 :大量连续的 softirq_entry(NET_RX) 事件
  • 关键发现:每当 CPU 3 的软中断密集时,CPU 2 的 perception 进程就被调度出去

时间线分析

复制代码
时间: 1234.500s - 1234.550s(50ms 窗口)

CPU 2: [perception]────────[idle]──[perception]──[idle]──[perception]
          ^20ms            ^中断    ^5ms       ^中断    ^8ms
          
CPU 3: [ksoftirqd]████████████████████████████████████
       softirq    softirq  softirq  softirq  (连续 NET_RX)

步骤 3:测量延迟

  1. 在 kernelshark 中标记 perception 进程被抢占的时间点(1234.520s)
  2. 标记 perception 进程恢复运行的时间点(1234.545s)
  3. 时间差:25ms(正好对应延迟峰值!)

4. report 分析确认

bash 复制代码
# 统计 CPU 3 的软中断频率
trace-cmd report | grep 'CPU 3.*softirq_entry' | wc -l
# 输出:145672  (10 秒内 145,672 次 = 14,567 次/秒)

# 查看网络包接收频率
trace-cmd report | grep 'netif_receive_skb' | grep 'CPU 3' | wc -l
# 输出:145672  (与软中断一致)

# 查看包大小
trace-cmd report | grep netif_receive_skb | head -5

输出

复制代码
ksoftirqd/3-25   [003] 1234.567: netif_receive_skb: dev=eth1 len=66
ksoftirqd/3-25   [003] 1234.568: netif_receive_skb: dev=eth1 len=66
ksoftirqd/3-25   [003] 1234.569: netif_receive_skb: dev=eth1 len=66

根因确认

  • 网卡 eth1(某个摄像头)以 ~15k 包/秒的速率发送 66 字节小包
  • 中断全部分配到 CPU 3,导致软中断占用 65%
  • CPU 3 调度器无法及时调度其他进程,影响整体系统响应

5. 解决方案

bash 复制代码
# 方案 1:分散网卡中断到多个 CPU
cat /proc/interrupts | grep eth1
# 输出:128:  12345678  ...  eth1-TxRx-0

# 将 eth1 中断分散到 CPU 0-2
echo 07 > /proc/irq/128/smp_affinity  # 二进制 0111

# 方案 2:启用 RPS(Receive Packet Steering)
echo f > /sys/class/net/eth1/queues/rx-0/rps_cpus  # 使用 CPU 0-3

效果验证

bash 复制代码
# 再次记录 10 秒
trace-cmd record -e 'sched:*' -e 'irq:*' sleep 10

# 用 kernelshark 对比
kernelshark trace.dat

优化后时间线

复制代码
CPU 0: [ksoftirqd]██░░██░░  (25% softirq)
CPU 1: [ksoftirqd]██░░██░░  (25% softirq)
CPU 2: [perception]████████  (不再被中断)
CPU 3: [ksoftirqd]██░░██░░  (25% softirq)

性能对比

指标 优化前 优化后 改善
CPU 3 软中断 65% 16% ↓ 75%
perception P99 延迟 150ms 25ms ↓ 83%
调度等待时间 25ms 1.2ms ↓ 95%

🔄 实战案例 2:跨核心延迟传播分析

1. 问题场景

数据融合模块(data_fusion)在 CPU 0 处理完数据后,唤醒 CPU 2 上的决策模块(planning),但 planning 的唤醒延迟从预期的 1ms 飙升到 15ms。

2. 记录唤醒路径

bash 复制代码
# 记录调度和 IPI(核间中断)事件
trace-cmd record \
    -e sched:sched_switch \
    -e sched:sched_wakeup \
    -e sched:sched_waking \
    -e irq:irq_handler_entry \
    -e irq:irq_handler_exit \
    -O stacktrace \
    sleep 5

3. kernelshark 时间线分析

关键观察

  1. 1234.500s :CPU 0 的 data_fusion 调用 sched_waking(唤醒 planning)
  2. 1234.502s :CPU 0 发送 IPI 到 CPU 2(irq_handler_entry: reschedule
  3. 1234.515s :CPU 2 的 sched_switch 才切换到 planning
  4. 延迟 = 15ms(预期应该 < 2ms)

问题定位

  • CPU 2 在 1234.502s - 1234.515s 期间运行什么?
  • kernelshark 显示:CPU 2 正在运行低优先级的 background_worker(13 秒连续运行)
  • planning 是高优先级进程(nice=-10),但仍等待了 13ms

根因background_worker 关闭了抢占(可能持有自旋锁)

4. 验证抢占关闭

bash 复制代码
# 使用 function_graph 追踪 CPU 2
trace-cmd record \
    -p function_graph \
    -P $(pgrep background_worker) \
    -g '*lock*' \
    sleep 5

trace-cmd report | grep -A 10 "spin_lock"

发现

复制代码
background_worker-5678 [002] 1234.502: spin_lock() {
  _raw_spin_lock();
  critical_section() {
    expensive_computation() {
      ... (13ms)
    }
  }
  _raw_spin_unlock();
}

5. 解决方案

c 复制代码
// 原代码(持锁时间过长)
spin_lock(&data_lock);
expensive_computation();  // 13ms!
spin_unlock(&data_lock);

// 优化后
local_data = copy_data_under_lock();  // 0.1ms
expensive_computation(local_data);    // 无锁执行
update_result_under_lock();           // 0.1ms

效果 :唤醒延迟从 15ms 降至 1.2ms


📊 高级功能:多事件关联分析

1. 使用 trace-cmd 的过滤器

bash 复制代码
# 只显示特定进程的事件
trace-cmd report -F 'common_pid == 1234'

# 只显示特定 CPU
trace-cmd report -F 'common_cpu == 2'

# 组合条件
trace-cmd report -F 'common_pid == 1234 && common_cpu == 2'

# 只显示特定时间范围
trace-cmd report -S 1234.5 -E 1235.0

2. kernelshark 的高级过滤

过滤器语法

复制代码
# 显示所有调度事件
sched:*

# 显示 CPU 2 的所有事件
cpu == 2

# 显示特定进程
comm ~ "perception"

# 组合条件
(comm ~ "perception" || comm ~ "planning") && cpu == 2

3. 导出分析结果

bash 复制代码
# 导出为 CSV(用于 Python 分析)
trace-cmd report -F 'cpu == 3' -O csv > cpu3_events.csv

# Python 分析脚本
import pandas as pd

df = pd.read_csv('cpu3_events.csv')
print(df.groupby('event')['duration'].agg(['count', 'mean', 'max']))

✅ 最佳实践

1. trace-cmd 使用建议

bash 复制代码
# (1) 控制缓冲区大小(避免内存不足)
trace-cmd record -b 10240 ...  # 10MB per CPU

# (2) 使用事件过滤减少数据量
trace-cmd record -e 'sched:sched_switch' -F 'prev_pid == 1234 || next_pid == 1234' ...

# (3) 限制追踪时间
trace-cmd record ... sleep 10  # 最多 10 秒

# (4) 实时查看(不保存文件)
trace-cmd record -d -e 'net:*' | head -100

# (5) 压缩输出文件
trace-cmd record -o trace.dat ... && gzip trace.dat

2. kernelshark 分析技巧

任务 操作
找到延迟峰值 搜索 sched_switch,查看 prev_state == D
测量中断处理时间 标记 irq_handler_entry 和 irq_handler_exit,查看时间差
对比优化效果 加载两个 trace.dat,使用 "Compare" 功能
导出关键区域 选中时间范围,右键 "Export Selection"
查看调用栈 记录时加 -O stacktrace,双击事件查看

3. 性能开销

配置 trace.dat 大小(10秒) CPU 开销
-e sched:* ~5 MB < 1%
-e 'sched:*' -e 'irq:*' ~20 MB 2-3%
-e 'sched:*' -e 'irq:*' -e 'net:*' ~50 MB 5-8%
-p function -l 'vfs_*' ~100 MB 10-15%
-p function_graph > 500 MB 30-50%

🎯 小结

核心要点

  • trace-cmd 大幅简化 ftrace 操作,一条命令完成记录和分析
  • kernelshark 提供时间线可视化,快速发现多核、多事件的关联性
  • 多核分析 是 kernelshark 的杀手级功能,瞬间定位跨核心问题
  • 数据持久化 trace.dat 格式便于分享和离线分析
  • 结合使用 trace-cmd + kernelshark + 手工 ftrace,形成完整工具链

典型分析流程



发现性能问题
trace-cmd 记录

相关 events
kernelshark

可视化分析
定位根因?
调整 events 范围

重新记录
验证假设
实施优化
trace-cmd 对比

优化效果

工具选择

场景 推荐工具 原因
单核热点分析 perf 采样开销低
多核调度分析 trace-cmd + kernelshark 可视化时间线
中断延迟分析 trace-cmd + kernelshark 清晰显示中断处理流程
函数调用链分析 trace-cmd function_graph 完整调用树
实时监控 ftrace(手工) 灵活性高
生产环境 eBPF + bpftrace 开销最低

常用命令速查

bash 复制代码
# === 记录常见场景 ===
# 调度分析
trace-cmd record -e 'sched:*' sleep 10

# 中断分析
trace-cmd record -e 'irq:*' -e 'softirq:*' sleep 10

# 网络分析
trace-cmd record -e 'net:*' -e 'irq:*' sleep 10

# 块 IO 分析
trace-cmd record -e 'block:*' sleep 10

# 特定进程
trace-cmd record -e 'sched:*' -P $(pgrep perception) sleep 10

# 函数调用链
trace-cmd record -p function_graph -g vfs_read cat /etc/hosts

# === 分析与查看 ===
trace-cmd report                      # 文本输出
trace-cmd report | less               # 分页查看
trace-cmd report -F 'cpu == 2'        # 过滤 CPU
kernelshark trace.dat                 # 图形化分析

# === 管理 ===
trace-cmd reset                       # 重置 ftrace
trace-cmd show                        # 查看当前配置
trace-cmd list -e                     # 列出所有 events

📖 系列封面

Dive Deep into System Optimization - 从基础观测到高级优化,从 CPU 到存储,从内核到用户态,深入 Linux 性能分析的每一个角落。


下一章预告第 21 篇:系统调用阻塞分析与高并发优化

我们将深入分析阻塞 IO、非阻塞 IO 和异步 IO 的性能差异,学习 select、poll、epoll 的原理与优化技巧,并通过实战案例解决高并发场景下的 load 高但 CPU 空闲的问题。

相关推荐
2401_873587821 小时前
Linux——传输层协议TCP
linux·网络·tcp/ip
嵌入小生0072 小时前
进程(2)---相关函数接口、消亡、exec函数族 | 嵌入式(Linux)
linux·c语言·嵌入式·进程·函数接口·exec函数族·进程的消亡
程序员一点2 小时前
第9章:软件包管理(DNF 与 RPM)
linux·运维·openeuler
@syh.2 小时前
【linux】进程间通信
linux
wdfk_prog2 小时前
EWMA、加权平均与一次低通滤波的对比与选型
linux·笔记·学习·游戏·ssh
FoldWinCard2 小时前
Python 第三次作业
java·服务器·python
国科安芯2 小时前
航空级电机控制系统的抗辐照MCU功能安全设计与电磁兼容验证方法
单片机·嵌入式硬件·安全·性能优化·架构·安全性测试
longxibo2 小时前
【Ubuntu datasophon1.2.1 二开之六:解决CLICKHOUSE安装问题】
大数据·linux·clickhouse·ubuntu
何中应2 小时前
Jenkins如何注册为CentOS7的一个服务
linux·运维·jenkins·开发工具