Linux ftrace 的 ring buffer size 与 trace_pipe_raw 的低开销采集方式

3次阅读

trace_pipe_raw 更适合高吞吐采集,因其跳过内核格式化与字符串拼接,直接输出二进制事件流,避免 sprintf 级开销,cpu 占用低一个数量级,但需按事件边界原子读取并配合用户态解析工具。

Linux ftrace 的 ring buffer size 与 trace_pipe_raw 的低开销采集方式

trace_pipe_raw 为什么比 trace_pipe 更适合高吞吐采集

因为 trace_pipe_raw 跳过了内核的格式化和字符串拼接,直接输出二进制事件流,避免了每条 trace 都触发 sprintf 级别的开销。在高频事件(如 sched_switchirq_handler_entry)场景下,trace_pipe 容易成为瓶颈,而 trace_pipe_raw 的 CPU 占用通常低一个数量级。

常见错误现象:cat /sys/kernel/tracing/trace_pipe 在压测时卡住或丢事件,但 cat /sys/kernel/tracing/trace_pipe_raw 仍能稳定读出数据;这是因为前者依赖 trace_Event_printk,后者只做 memcpy + ring buffer copy

  • 必须配合用户态解析工具(如 perf script -F 或自研 parser),不能直接 human-read
  • trace_pipe_raw 输出的是 per-CPU 二进制流,头部含 Struct trace_entry 和 event-specific payload,无换行、无时间戳文本
  • 若用 ddhead -c 截断,可能切在事件中间,导致后续解析错位 —— 必须按完整事件边界读取

ring buffer size 设置不当会直接导致 trace_pipe_raw 丢事件

环形缓冲区大小不是“越大越好”,而是要匹配采集速率与消费速率的差值。默认的 buffer_size_kb(通常 1408 KB)在开启高频事件时几秒就满,一旦 buffer 满且 reader 没及时消费,新事件就会覆盖旧事件 —— trace_pipe_raw 不报错、不阻塞,只静默丢弃。

使用场景:监控 1000+ QPS 的系统调用入口(sys_enter),实测 buffer 需设为 8192 KB 以上才能撑过 30 秒采集窗口。

  • 设置方式:echo 8192 > /sys/kernel/tracing/buffer_size_kb(注意:单位是 KB,不是字节)
  • 该值对每个启用的 tracer(如 function_graph、event)独立生效,但 trace_pipe_raw 读取的是所有启用事件共用的主 buffer
  • 过大的 buffer 会占用不可交换的内核内存(kmalloc-backed),在内存紧张机器上可能触发 OOM killer
  • 可通过 cat /sys/kernel/tracing/buffer_total_size_kb 查看当前总占用(含 per-CPU 备份)

如何安全读取 trace_pipe_raw 并避免解析错位

直接 catdd bs=1 会破坏事件结构,必须按事件长度原子读取。linux 内核在 trace_pipe_raw 中每个事件前写入 struct trace_entry(固定 12 字节),其中 size 字段标明整条事件长度(含 header)。

示例关键逻辑(C 伪代码):

while (read(fd, &entry, sizeof(entry)) == sizeof(entry)) {     int len = entry.size;     uint8_t *buf = malloc(len);     if (read(fd, buf, len) != len) break; // 必须一次读完     parse_event(buf); // 自定义解析     free(buf); }
  • 不能用 stdio 缓冲(如 fread),必须用 read() 系统调用直通 fd
  • 务必检查每次 read() 返回值是否等于预期长度,否则说明 buffer 已被覆盖或 reader 落后太多
  • trace_pipe_raw 是阻塞式 fd,但 buffer 满时新事件会覆盖旧事件 —— 所以“阻塞”不等于“安全”,仍需监控丢弃计数
  • 丢弃计数路径:/sys/kernel/tracing/trace_buffer_overrun(全局)或 per-CPU 的 /sys/kernel/tracing/per_cpu/cpu*/trace_buffer_overrun

function graph tracer 开启后对 trace_pipe_raw 的影响

启用 function_graph 会显著增加 ring buffer 压力:每个函数进出各记一条事件,且 payload 更大(含 call depth、parent ip 等)。此时即使 buffer_size_kb 相同,trace_pipe_raw 的丢事件概率也会飙升。

性能影响:在 4GHz CPU 上,function_graph 可使单核额外增加 5–15% 的开销,远高于普通 event tracer;其二进制格式也更复杂(含 struct ftrace_graph_ent / struct ftrace_graph_ret 两种 header)。

  • 开启方式:echo function_graph > /sys/kernel/tracing/current_tracer,之后再 echo event 名到 set_event
  • 务必先调大 buffer(建议 ≥16384 KB),并优先关闭非必要函数(echo ':mod:ext4' > /sys/kernel/tracing/set_ftrace_filter
  • trace_pipe_raw 中的 function graph 事件无法用 perf script 直接解析,需用 kernel source 中的 scripts/ftrace/ftrace-to-dot.pl 或自研 parser
  • 如果只要函数调用关系图,不如用 perf record -e 'ftrace:function',它底层也走类似路径但封装了 buffer 管理

真正难的是平衡:buffer 太小,丢事件;太大,吃内存又拖慢系统;不用 trace_pipe_raw,开销压不住;用了,又得自己啃二进制格式和边界对齐。没人替你扛这些细节。

text=ZqhQzanResources