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 空闲的问题。

相关推荐
用户83562907805144 分钟前
Python 实现 PDF 文件加密与解密方法
后端·python
用户8356290780511 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
花椒技术4 小时前
直播间常驻子应用加载优化实践:从 1550ms 到 890ms
性能优化·直播·前端工程化
你好潘先生9 小时前
别再记命令了,用 yeero do 说句人话就能跑脚本,而且不烧 token
服务器·python·命令行
Agent_大师9 小时前
WebSocket 行情重连成功,K线缺口不会自动消失
python
荣码9 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
copyer_xyf9 小时前
FastAPI 如何连接 MySQL
后端·python
orion5720 小时前
Missing Semester Class1:course overview and introduction of shell
linux
apocelipes1 天前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
用户8356290780511 天前
使用 Python 在 PDF 中创建与管理书签
后端·python