第一百九十一章:跟踪之力
大乘期涉及内核源码:
一
林小源踏入一片幽暗的峡谷,两侧岩壁上刻满了密密麻麻的函数名——、、——每一个名字都在微微发光,像是某种古老铭文。
"别乱碰。"一个低沉的声音从峡谷深处传来。
林小源循声望去,只见一道半透明的人影悬浮在峡谷中央,身披一件由光丝编织的斗篷,每一根光丝都连接着岩壁上的一处铭文。那人影面容模糊,唯有一双眼睛锐利如鹰,正盯着一条从身边掠过的光流。
"你是……ftrace?"林小源问。
"我是这峡谷的守望者。"那人影没有回头,"每一个从这里经过的函数调用,我都看在眼里。"
林小源注意到那些光丝正在微微颤动,每当岩壁上某个铭文亮起,对应的光丝就会闪一下。他伸手碰了碰最近的一根光丝,指尖传来一阵细微的震动——那是一次函数调用的脉搏。
"你能跟踪所有函数?"林小源惊叹道。
"当然。"ftrace 终于转过头来,嘴角带着一丝傲然,"function 跟踪器记录每一次调用,function_graph 跟踪器绘制调用图,irqsoff 跟踪器测量中断关闭时间,preemptoff 跟踪器测量抢占关闭时间。我有四种跟踪器,各司其职。"
"那怎么启用你?"
ftrace 指了指峡谷入口处的一块石碑:"echo function > current_tracer,然后 echo 1 > tracing_on。之后 cat trace 就能看到所有记录。"他顿了顿,补充道,"如果你只关心某个子系统,可以用 过滤。比如 echo ext4* > set_ftrace_filter,我就只跟踪 ext4 相关的函数。"
/*
* 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");#include <stdio.h>
#include <string.h>
/*
* 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);
}
int main() {
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");
return 0;
}二
林小源跟着 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 文件读取函数名是什么?