Skip to content

第一百九十一章:跟踪之力

大乘期

涉及内核源码:

林小源踏入一片幽暗的峡谷,两侧岩壁上刻满了密密麻麻的函数名————每一个名字都在微微发光,像是某种古老铭文。

"别乱碰。"一个低沉的声音从峡谷深处传来。

林小源循声望去,只见一道半透明的人影悬浮在峡谷中央,身披一件由光丝编织的斗篷,每一根光丝都连接着岩壁上的一处铭文。那人影面容模糊,唯有一双眼睛锐利如鹰,正盯着一条从身边掠过的光流。

"你是……ftrace?"林小源问。

"我是这峡谷的守望者。"那人影没有回头,"每一个从这里经过的函数调用,我都看在眼里。"

林小源注意到那些光丝正在微微颤动,每当岩壁上某个铭文亮起,对应的光丝就会闪一下。他伸手碰了碰最近的一根光丝,指尖传来一阵细微的震动——那是一次函数调用的脉搏。

"你能跟踪所有函数?"林小源惊叹道。

"当然。"ftrace 终于转过头来,嘴角带着一丝傲然,"function 跟踪器记录每一次调用,function_graph 跟踪器绘制调用图,irqsoff 跟踪器测量中断关闭时间,preemptoff 跟踪器测量抢占关闭时间。我有四种跟踪器,各司其职。"

"那怎么启用你?"

ftrace 指了指峡谷入口处的一块石碑:"echo function > current_tracer,然后 echo 1 > tracing_on。之后 cat trace 就能看到所有记录。"他顿了顿,补充道,"如果你只关心某个子系统,可以用 过滤。比如 echo ext4* > set_ftrace_filter,我就只跟踪 ext4 相关的函数。"

c
/*
 * ftrace 框架:
 *
 * 功能:
 *   函数跟踪 — 跟踪函数调用
 *   函数耗时 — 测量函数执行时间
 *   事件跟踪 — 跟踪内核事件
 *   图形化 — 函数调用图
 *
 * 跟踪器:
 *   function — 函数跟踪
 *   function_graph — 函数调用图
 *   irqsoff — 中断关闭时间
 *   preemptoff — 抢占关闭时间
 *
 * 接口:
 *   /sys/kernel/debug/tracing/
 *   trace — 跟踪输出
 *   current_tracer — 当前跟踪器
 *   set_ftrace_filter — 设置过滤
 *
 * 使用:
 *   echo function > current_tracer
 *   echo 1 > tracing_on
 *   cat trace
 *
 * 工具:
 *   trace-cmd — 命令行工具
 *   perf — 性能分析
 *   火焰图 — 可视化
 */

/* 模拟 ftrace 输出 */
struct ftrace_entry {
    char comm[16];
    int pid;
    char func[64];
    unsigned long long time;
};

void print_ftrace_entry(struct ftrace_entry *e) {
    printf("  %s-%d  %llu  %s\n",
           e->comm, e->pid, e->time, e->func);
}

printf("=== 跟踪之力 — 分析性能的利器 ===\n\n");

printf("ftrace 是内核的函数跟踪框架:\n\n");

/* 模拟 ftrace 输出 */
printf("--- 函数跟踪输出 ---\n");
struct ftrace_entry entries[] = {
    {"cat", 1234, "do_sys_open", 1000},
    {"cat", 1234, "path_openat", 1200},
    {"cat", 1234, "link_path_walk", 1500},
    {"cat", 1234, "vfs_read", 2000},
    {"cat", 1234, "ext4_file_read", 2500},
};

int n = sizeof(entries) / sizeof(entries[0]);
for (int i = 0; i < n; i++) {
    print_ftrace_entry(&entries[i]);
}

printf("\n--- 跟踪器类型 ---\n");
printf("function:\n");
printf("  函数跟踪\n");
printf("  记录函数调用\n\n");
printf("function_graph:\n");
printf("  函数调用图\n");
printf("  显示调用关系\n\n");
printf("irqsoff:\n");
printf("  中断关闭时间\n");
printf("  找出中断延迟\n\n");
printf("preemptoff:\n");
printf("  抢占关闭时间\n");
printf("  找出抢占延迟\n\n");

printf("--- 使用方法 ---\n");
printf("1. 函数跟踪:\n");
printf("   echo function > current_tracer\n");
printf("   echo 1 > tracing_on\n");
printf("   cat trace\n\n");
printf("2. 过滤函数:\n");
printf("   echo ext4* > set_ftrace_filter\n\n");
printf("3. 函数调用图:\n");
printf("   echo function_graph > current_tracer\n\n");

printf("--- 工具 ---\n");
printf("trace-cmd:\n");
printf("   trace-cmd record -e ext4\n");
printf("   trace-cmd report\n\n");
printf("perf:\n");
printf("   perf record -g\n");
printf("   perf report\n\n");
printf("火焰图:\n");
printf("   perf script | stackcollapse.pl | flamegraph.pl\n");

林小源跟着 ftrace 往峡谷深处走去。越往里走,岩壁上的铭文越密集,光丝交织成一张巨大的网。

"切换到 function_graph 模式。"ftrace 说。

刹那间,那些原本独立发光的铭文开始用光线连接起来,形成一棵倒悬的树。根节点在最上方————从它分出 ,再分出 ,每一条分支都标注着耗时。

"这就是调用图。"ftrace 的声音变得柔和了一些,"你一眼就能看出哪个分支耗时最长。"

林小源盯着那棵光树,果然——ext4_file_read 那条分支比其他分支粗了整整一倍,光芒也更刺眼。

"瓶颈在这里。"林小源喃喃道。

"对。"ftrace 点头,"function_graph 跟踪器的价值就在这里。它不只记录调用,还展示关系和耗时。你不需要逐行分析日志,一眼就能看出问题所在。"

林小源伸手触碰那条粗壮的分支,指尖传来一阵灼热——那是 I/O 等待的温度。

峡谷的尽头是一片开阔的悬崖。悬崖上悬浮着一幅巨大的图——由无数红黄相间的色块堆叠而成,像是燃烧的火焰。

"火焰图。"ftrace 说,语气里难得带了一丝赞叹。

林小源走近那幅图,发现每一个色块都是一个函数名。宽度代表耗时——越宽的函数占用的 CPU 时间越多。高度代表调用深度——越高的位置说明调用链越深。

"一眼就能看出热点。"林小源说。

"没错。"ftrace 说,"perf 采集数据,stackcollapse.pl 折叠调用栈,flamegraph.pl 生成火焰图。三个工具,一条管道。宽的色块就是你需要优化的地方——它们吞噬了最多的 CPU 时间。"

林小源盯着火焰图中最宽的那块红色——ext4_file_read——它几乎占了整幅图的三分之一。他深吸一口气,把这张图刻进了脑海里。


道藏笔记

内核启示

ftrace 是内核的函数跟踪框架。

跟踪器:

  • function — 函数跟踪
  • function_graph — 函数调用图
  • irqsoff — 中断关闭时间
  • preemptoff — 抢占关闭时间

接口:

  • /sys/kernel/debug/tracing/
  • trace — 跟踪输出
  • current_tracer — 当前跟踪器

工具:

  • trace-cmd — 命令行工具
  • perf — 性能分析
  • 火焰图 — 可视化

ftrace 是利器——深入内核,分析性能。


破关试炼

跟踪之力之试

跟踪之力一章中,示例跟踪到的 ext4 文件读取函数名是什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道