ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Perfetto 内核 ftrace 插桩实战:从自定义 tracepoint 到 UI 轨道的完整接入指南

2026/9/17 21:26:51 拓冰建站 浏览量
Perfetto 内核 ftrace 插桩实战:从自定义 tracepoint 到 UI 轨道的完整接入指南 Perfetto 内核 ftrace 插桩实战从自定义 tracepoint 到 UI 轨道的完整接入指南【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto导读本文面向内核与系统开发者完整讲解如何为 Linux 内核添加自定义插桩静态 tracepoint并用 Perfetto 的tracebox录制、用trace_processor解析、最终在 Perfetto UI 中可视化的全流程。你将掌握三条技术路径直接用tracebox录制自定义 tracepoint 事件Part A通过修改 Perfetto 源码为事件生成专用 protobuf 描述并编写解析逻辑将其变为结构化 SQL 表和 UI 轨道Part B以及不修改任何 Perfetto 代码、仅靠字段命名约定就让事件自动呈现为 slice/counter 轨道的 Kernel Track Events 方案Part C。只想在本地内核上快速获得轨道可视化、不想改动 Perfetto 的读者可以直接跳到 Part C。背景ftrace 与 Perfetto 的协作关系ftrace 是内置于 Linux 内核的可配置追踪框架。开发者可以通过 tracepoint 和 probe 对内核进行插桩这些插桩可以在运行时动态启用从而记录事件例如某个 CPU 上从一个线程到另一个线程的上下文切换。用户空间通过tracefs文件系统通常挂载在/sys/kernel/tracing来配置要记录的内容并读取生成的 trace。内核本身已预置了数百个 tracepoint可用于理解调度、内存等各个子系统。Perfetto 可以通过配置FtraceConfig将一组 ftrace 事件记录到 Perfetto trace 中。系统录制实现内置于traced_probes或tracebox负责配置 tracefs并把产生的事件流转换为 Perfetto 的 protobuf trace 格式。Perfetto 的查询引擎和 UI 则对 ftrace 事件具有领域特定的解析能力例如原始的上下文切换sched_switch和唤醒sched_waking事件会被转换为 UI 中按 CPU、按线程组织的调度轨道并映射为可查询的 SQL 表。另外如果只是想可视化内核函数的执行过程而不做任何额外插桩Perfetto 内置了对 ftracefunction_graphtracer 的可视化支持详见 Kernel function graph tracing其配置选项位于 ftrace_config.proto 中的enable_function_graph等字段。Part A插桩内核并录制 tracepoint本部分以最简路径演示完整闭环编写一个名为ticker的内核模块其中包含一个名为ticker_tick的 tracepoint每秒触发一次并携带一个递增计数器作为参数然后用tracebox录制并用 Perfetto UI 查看。创建内核模块源文件新建目录并按照以下目录结构放置三个文件. ├── Makefile ├── ticker.c └── trace └── events └── ticker.h主源码ticker.c定义一个内核定时器每次回调触发一次trace_ticker_tick(tick_count)然后重新武装定时器。注意触发函数的命名规律为trace_event_name它由头文件中的宏展开生成// ticker.c #include linux/kernel.h #include linux/module.h #include linux/timer.h #include linux/version.h #define CREATE_TRACE_POINTS #include trace/events/ticker.h MODULE_LICENSE(GPL); MODULE_AUTHOR(Perfetto); MODULE_DESCRIPTION(Ticker: A kernel module emitting example static tracepoint events.); MODULE_VERSION(0.1); static struct timer_list my_timer; static unsigned int tick_count 0; static unsigned long timer_interval_ms 1000; static void my_timer_callback(struct timer_list *timer) { // Fire the tracepoint, incrementing the tick count every time. // The function name is trace_event_name from the header file. trace_ticker_tick(tick_count); // Re-arm the timer. mod_timer(my_timer, jiffies msecs_to_jiffies(timer_interval_ms)); } static int __init ticker_init(void) { pr_info(Ticker: Initializing...\n); timer_setup(my_timer, my_timer_callback, 0); mod_timer(my_timer, jiffies msecs_to_jiffies(timer_interval_ms)); pr_info(Ticker: Timer started.\n); pr_info(Ticker: View events under /sys/kernel/tracing/events/ticker/\n); return 0; } static void __exit ticker_exit(void) { pr_info(Ticker: Exiting...\n); #if LINUX_VERSION_CODE KERNEL_VERSION(6, 15, 0) timer_delete_sync(my_timer); #else del_timer_sync(my_timer); #endif pr_info(Ticker: Timer stopped and module unloaded.\n); } module_init(ticker_init); module_exit(ticker_exit);tracepoint 头文件trace/events/ticker.h使用内核标准的TRACE_EVENT宏声明事件。这里有一个重要约束头文件必须放在trace/events/子目录下而不能放在目录根下否则内核宏无法正确展开。TP_STRUCT__entry定义事件携带的字段countTP_printk定义事件在 tracefs 文本输出中的打印格式// trace/events/ticker.h #undef TRACE_SYSTEM #define TRACE_SYSTEM ticker #if !defined(_TRACE_TICKER_H_) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_TICKER_H_ #include linux/tracepoint.h TRACE_EVENT(ticker_tick, TP_PROTO(unsigned int count), TP_ARGS(count), TP_STRUCT__entry( __field(unsigned int, count) ), TP_fast_assign( __entry-count count; ), TP_printk(count%u, __entry-count ) ); #endif /* _TRACE_TICKER_H_ */ /* This part must be outside protection */ #include trace/define_trace.hMakefile用于编译内核模块# Makefile obj-m ticker.o ccflags-y -I$(src) KDIR ? /lib/modules/$(shell uname -r)/build PWD : $(shell pwd) all: $(MAKE) -C $(KDIR) M$(PWD) modules clean: $(MAKE) -C $(KDIR) M$(PWD) clean .PHONY: all clean编译并加载模块确保已安装与当前内核版本匹配的内核头文件KDIR 默认指向/lib/modules/$(uname -r)/build然后执行make编译内核模块。编译完成后安装模块sudo insmod ticker.ko需要卸载时使用sudo rmmod ticker.ko可选手动验证 tracepoint加载模块后可以先不经过 Perfetto直接用 tracefs 验证事件是否按预期产生。首先确认事件已注册到 tracefsls /sys/kernel/tracing/events/ticker/ticker_tick启用 ticker 事件和整体追踪echo 1 | sudo tee /sys/kernel/tracing/events/ticker/ticker_tick/enable echo 1 | sudo tee /sys/kernel/tracing/tracing_on然后持续读取事件流sudo cat /sys/kernel/tracing/trace_pipe可以看到 ticker 事件大约每秒产生一次count字段递增。这里的文本表示正是由上面 tracepoint 定义中的TP_printk(...)部分生成的# cat /sys/kernel/tracing/trace_pipe idle-0 [011] ..s1. 850584.176058: ticker_tick: count38 idle-0 [011] ..s1. 850585.200042: ticker_tick: count39 ...-2904431 [015] ..s1. 850586.224031: ticker_tick: count40 puppet-2904431 [015] ..s.. 850587.248080: ticker_tick: count41 idle-0 [011] ..s1. 850588.272137: ticker_tick: count42 idle-0 [011] ..s1. 850589.296040: ticker_tick: count43 idle-0 [011] ..s1. 850590.320049: ticker_tick: count44 idle-0 [011] ..s1. 850591.344048: ticker_tick: count45 idle-0 [011] ..s1. 850592.372038: ticker_tick: count46 idle-0 [011] ..s1. 850593.392033: ticker_tick: count47 idle-0 [003] ..s1. 850594.416049: ticker_tick: count48 idle-0 [011] ..s1. 850595.440054: ticker_tick: count49用 tracebox 录制 Perfetto trace接下来录制包含 ticker 事件的系统 trace。创建录制配置文件文本格式的 textproto只启用linux.ftrace数据源并显式列出ticker/ticker_tick事件# ticker.cfg buffers { size_kb: 20480 fill_policy: DISCARD } # Record our ticker events only. data_sources { config { name: linux.ftrace target_buffer: 0 ftrace_config { ftrace_events: ticker/ticker_tick } } } # 10s trace, but can be stopped prematurely. duration_ms: 10000关于配置中的缓冲区与数据源说明buffers.size_kb定义了 Perfetto 侧的环形缓冲区大小这里是 20 MBfill_policy: DISCARD表示缓冲区写满后丢弃新事件而不是阻塞生产者ftrace_events是需要记录的 ftrace 事件列表写法为子系统/事件名例如sched/sched_switch。Perfetto 侧的FtraceConfig还支持更多可选参数如buffer_size_kb控制内核每个 CPU 的 ftrace 环形缓冲区大小、compact_sched压缩调度事件、symbolize_ksyms记录时解析内核符号等完整字段见 ftrace_config.proto。按照 system tracing 指南 准备好tracebox环境后用命令行录制./tracebox -c ticker.cfg --txt -o ticker.pftracetracebox会自动完成之前手动做过的步骤——启用整体追踪并开启 ticker 事件无需再手工操作 tracefs。命令执行后会将 Perfetto protobuf trace 写入ticker.pftrace。在 Perfetto UI 中查看 trace打开 https://ui.perfetto.dev把ticker.pftrace文件拖入窗口或按CtrlO打开文件对话框。展开Ftrace Events轨道组可以看到按 CPU 组织的原始事件视图选中事件即可查看其字段。另外CtrlShiftP打开命令面板后输入Show ftrace tab可以打开一个近似文本输出的标签页。需要注意的是由于 Perfetto 记录的是事件的二进制表示它并不会按照TP_printk(...)说明符对事件做文本化。原始事件同样可以 SQL 查询例如SELECT * FROM ftrace_event JOIN args USING (arg_set_id)在这个阶段事件以 Perfetto 的通用回退编码generic fallback encoding写入 trace只要 Perfetto 在编译期不认识该事件录制端traced_probes/tracebox就会读取 tracefs 中该事件的 format 文件把已知字段序列化为GenericFtraceEvent。trace_processor侧对应的解析逻辑位于 generic_ftrace_tracker.cc 与 ftrace_parser.cc 的ParseFtraceEvent大 switch 分支中而ParseFtraceEvent会先从事件中读取pid、timestamp等元数据字段见 ftrace_parser.cc 中FtraceEvent::kPidFieldNumber的查找逻辑再按字段 ID 分发到各个专用解析函数。要让事件变成结构化的 SQL 表和专用 UI 轨道需要进入 Part B 的完整接入流程。Part B将新 tracepoint 集成到 Perfetto要让这个新 tracepoint 在 Perfetto 中获得专用解析需要做两件事生成事件的 protobuf 描述让序列化代码traced_probes或tracebox能够把事件写成专用 protobuf 类型而不是上面隐式使用的通用回退编码为trace_processor查询引擎添加解码器解析 protobuf trace 时从新事件中创建期望的轨道。下面以把 ticker 事件解析成一条全局 counter 轨道为例。由于需要修改 Perfetto 源码请先克隆本仓库以下指令默认当前目录为仓库根目录。在 Perfetto 中生成事件的 protobuf 描述首先把 tracefs 中描述该事件的 format 文件复制到 Perfetto 的合成事件数据目录src/traced/probes/ftrace/test/data/synthetic/下存放各事件的 format 文件作为测试与代码生成的数据源DESTsrc/traced/probes/ftrace/test/data/synthetic/events/ticker/ticker_tick; \ mkdir -p $DEST \ cp /sys/kernel/tracing/events/ticker/ticker_tick/format $DEST/format然后把事件追加到事件清单文件 event_list该文件按行列出 Perfetto 支持的所有 ftrace 事件是代码生成器的输入echo ticker/ticker_tick src/tools/ftrace_proto_gen/event_list接着运行生成脚本为序列化与解码两侧创建 protobuf 描述和其他编译期文件tools/run_ftrace_proto_gen tools/gen_all out/YOUR_BUILD_DIRECTORY运行后会创建/修改至少以下文件protos/perfetto/trace/ftrace/ftrace_event.protoFtraceEvent 的 oneof 中加入新字段protos/perfetto/trace/ftrace/ticker.proto新事件的专用消息类型src/traced/probes/ftrace/event_info.cc事件字段元数据供运行时序列化protos/perfetto/trace/perfetto_trace.proto聚合所有消息类型这一步足以让序列化逻辑开始为事件使用专用 protobuf 类型。需要注意录制时 Perfetto 会在运行时读取 tracefs 中该事件的 format 文件并且只序列化 Perfetto 编译期已知的字段——也就是说如果内核侧后来给事件新增了字段需要重新生成代码并重新编译才能识别。修改完成后本地重新编译tracebox并重新录制 trace。关于tools/run_ftrace_proto_gen的底层实现它对应 src/tools/ftrace_proto_gen 下的工具链ftrace_proto_gen.cc、ftrace_descriptor_gen.cc等其职责是从每个事件的 format 文件解析出字段名、类型、偏移与大小生成 protobuf 消息定义以及event_info.cc中的字段描述表。在 trace_processor 中解析事件接下来在 trace_processor 的 ftrace_parser.cc 中添加 protobuf 的解码与解析逻辑。以创建一条聚合所有事件的全局 counter 轨道为例需要在巨大的ParseFtraceEventswitch-case 中为FtraceEvent::kTickerTickFieldNumber添加一个 case该 switch 正是所有 ftrace 事件按字段号分发的入口见 ftrace_parser.cc 中FtraceEvent::kSchedSwitchFieldNumber、kSchedWakingFieldNumber等既有分支添加一个函数intern 一条轨道并把所有带时间戳的值作为计数写入。示例改动省略头文件改动// ftrace_parser.cc static constexpr auto kTickerCountBlueprint tracks::CounterBlueprint( ticker, tracks::UnknownUnitBlueprint(), tracks::DimensionBlueprints(), tracks::StaticNameBlueprint(Ticker)); // ~~ snip ~~ case FtraceEvent::kTickerTickFieldNumber: { ParseTickerEvent(cpu, ts, fld_bytes); break; } // ~~ snip ~~ void FtraceParser::ParseTickerEvent(uint32_t cpu, int64_t timestamp, protozero::ConstBytes data) { protos::pbzero::TickerTickFtraceEvent::Decoder ticker_event(data); PERFETTO_LOG(Parsing ticker event: % PRId64 , % PRIu32 , %d, timestamp, cpu, static_castint(ticker_event.count())); // Push the global counter. TrackId track context_-track_tracker-InternTrack(kTickerCountBlueprint); context_-event_tracker-PushCounter( timestamp, static_castdouble(ticker_event.count()), track); }重新编译trace_processor或整个 UI后数据即可从 counter 表中查询SELECT * FROM counter JOIN counter_track ct ON ct.id counter.track_id WHERE ct.type ticker在 UI 中可视化该轨道要让这条轨道真正显示在 UI 中还需要由 UI 代码Perfetto 中以插件形式组织注册它。最简单的选择是dev.perfetto.TraceProcessorTrack插件。在该插件的 counter 轨道注册表 counter_tracks.ts 中添加一条规则把所有type为ticker的 counter 轨道加入SYSTEM顶级分组// ui/src/plugins/dev.perfetto.TraceProcessorTrack/counter_tracks.ts ~~ snip ~~ { type: ticker, topLevelGroup: SYSTEM, group: undefined, }, ~~ snip ~~这里的注册表结构正是该插件将counter_tracks表counter_track.type字段映射为 UI 轨道分组的地方每个条目声明type对应 SQL 中counter_track.type的值、topLevelGroup顶级轨道组如SYSTEM和可选的group与展示配置。之后重新构建本地 UIui/run-dev-server打开刚才用具备该事件编译期知识的tracebox重新录制的 trace。现在应该能看到一条包含事件数据的专用 UI 轨道。这是把 ftrace 事件接入 Perfetto 全栈最完整的方式。步骤较多且需要把你的改动上游化但它在 trace_processor 代码中提供了任意解析的完整能力并让所有 Perfetto 用户都能使用解析结果。Part C不修改 Perfetto 代码的简单 slice/counter 可视化Kernel Track Events对于只在本地内核上做实验、或编写不会上游到主线内核的模块的开发者还有更简单的方式Perfetto 可以根据字段命名约定自动为事件创建基本的 slice 与 counter 轨道完全不需要修改或重新编译 Perfetto让你把精力集中在插桩代码上。考虑 slice 的典型场景它通常由一对事件推导而来——一个表示 slice 开始另一个表示结束。Perfetto 需要一种约定来知道某个 tracepoint 应被这样解释。做法是Perfetto 会查找具有特定名称和类型的 tracepoint 字段即TP_STRUCT__entry(..)部分。如果 tracepoint 符合该约定trace_processor 与 UI 会自动把事件按轨道分组分组/作用域由进一步的约定控制。下面给出几种常见场景的 tracepoint 模板及其预期可视化效果slice 轨道用于同步代码——操作在启动它的同一线程上结束例如单个函数中循环前后的区间进程级process-scopedslice 轨道适合在进程级别分组可视化的事件CPU 级cpu-scopedcounter 轨道表示每 CPU 计数器的事件。完整的作用域与轨道命名参考规范见 Kernel track events: format and conventions。从源码看这一约定在 trace_processor 侧的落地实现位于 generic_ftrace_tracker.cc它通过内部字符串track_event_type、slice_name、counter_value、scope_tgid、scope_cpu识别事件字段并以scope_前缀识别任意作用域字段。线程级 slice 轨道thread-scoped如果只是想可视化一段可能嵌套的代码区域的持续时间且该区域的开始和结束发生在同一线程上这是最简单的用法。只需在 tracepoint 的布局TP_STRUCT__entry(...)中提供两个具有众所周知名与类型的字段Perfetto 就会把该 tracepoint 解析为线程级 slice 轨道char track_event_type__string slice_namePerfetto 会根据实际事件中track_event_type的值把事件解释为 slice 或 instantB在活动线程上开启一个命名 slice名称取自slice_nameE结束该线程上最近开启的 slice忽略slice_nameI设置一个名为slice_name的 instant零持续时间事件。线程 ID 和时间戳是每个 ftrace 事件天然携带的无需显式指定。示例 tracepoint 声明命名为trk_example/tid_track_example注意内核 v6.10 之前__assign_str需要两个参数见 相关内核补丁。// trace/events/trk_example.h #undef TRACE_SYSTEM #define TRACE_SYSTEM trk_example #if !defined(_TRACE_TRK_EXAMPLE_H_) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_TRK_EXAMPLE_H_ #include linux/tracepoint.h TRACE_EVENT(tid_track_example, TP_PROTO( char track_event_type, const char *slice_name ), TP_ARGS(track_event_type, slice_name), TP_STRUCT__entry( __field(char, track_event_type) __string(slice_name, slice_name) ), TP_fast_assign( __entry-track_event_type track_event_type; /* kernels before v6.10: __assign_str(slice_name, slice_name) */ __assign_str(slice_name); ), TP_printk( type%c slice_name%s, __entry-track_event_type, __get_str(slice_name) ) ); #endif /* This part must be outside protection */ #include trace/define_trace.h注意只有TP_STRUCT__entry中字段的类型和名称是关键的对额外字段、printk 说明符甚至字段顺序都没有任何约束。为了方便使用可以把 tracepoint 调用包装成宏// Convenience macros #define TRACE_EX_BEGIN(name) trace_tid_track_example(B, name) #define TRACE_EX_END() trace_tid_track_example(E, ) #define TRACE_EX_INSTANT(name) trace_tid_track_example(I, name)被插桩代码示例演示嵌套 slice 与 instantTRACE_EX_BEGIN(outer); udelay(500); for (int i0; i 3; i) { TRACE_EX_BEGIN(nested); udelay(1000); TRACE_EX_INSTANT(instant); TRACE_EX_END(); } TRACE_EX_END();用下面的配置录制 tracedenser_generic_event_encoding选项自 Perfetto v53 起为默认值但在旧版本上是必需的——从 ftrace_config.proto 可以看到该字段默认值随版本变化v53 为 true之前为 false// trace.txtpb duration_ms: 10000 buffers: { size_kb: 40960 fill_policy: DISCARD } data_sources: { config: { name: linux.ftrace ftrace_config: { denser_generic_event_encoding: true ftrace_events: trk_example/* } } }录制得到的轨道由 Perfetto trace_processor 与 UI 自动推导。注意每个线程获得各自独立的轨道且轨道会自动嵌套在进程轨道组之下进程级 slice 轨道process-scoped与上一示例类似但 slice 事件按进程级别分组。注意这种方式允许从不同于启动操作的线程上终止 slice但分组后的 slice 必须严格嵌套——所有 slice 都必须在它们的父 slice 之前结束关于异步 slice 的概念详见 converting 指南中的异步 slice 与重叠事件。此类解析在TP_STRUCT__entry中要求的字段char track_event_type__string slice_nameint scope_tgid前两个字段的语义与上一示例相同而scope_tgid需要填充该事件应被分组到的进程 ID即 TGID。进程 ID 必须是真实的不要硬编码任意常量但发出事件的线程不必属于该进程。示例 tracepoint 声明命名为trk_example/tgid_track_example// trace/events/trk_example.h #undef TRACE_SYSTEM #define TRACE_SYSTEM trk_example #if !defined(_TRACE_TRK_EXAMPLE_H_) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_TRK_EXAMPLE_H_ #include linux/tracepoint.h TRACE_EVENT(tgid_track_example, TP_PROTO( char track_event_type, const char *slice_name, int scope_tgid ), TP_ARGS(track_event_type, slice_name, scope_tgid), TP_STRUCT__entry( __field(char, track_event_type) __string(slice_name, slice_name) __field(int, scope_tgid) ), TP_fast_assign( __entry-track_event_type track_event_type; /* kernels before v6.10: __assign_str(slice_name, slice_name) */ __assign_str(slice_name); __entry-scope_tgid scope_tgid; ), TP_printk( type%c slice_name%s tgid%d, __entry-track_event_type, __get_str(slice_name), __entry-scope_tgid ) ); #endif /* This part must be outside protection */ #include trace/define_trace.h示例便捷宏使用当前进程上下文current-tgid作为事件分组依据// Convenience macros #define TRACE_EX_BEGIN(name) trace_tgid_track_example(B, name, current-tgid) #define TRACE_EX_END() trace_tgid_track_example(E, , current-tgid) #define TRACE_EX_INSTANT(name) trace_tgid_track_example(I, name, current-tgid)被插桩代码示例与前面相同TRACE_EX_BEGIN(outer); udelay(500); for (int i0; i 3; i) { TRACE_EX_BEGIN(nested); udelay(1000); TRACE_EX_INSTANT(instant); TRACE_EX_END(); } TRACE_EX_END();使用上一示例的配置录制后的可视化效果所有 slice 栈都在进程级别聚合CPU 级 counter 轨道cpu-scoped与 slice 和 instant 类似也存在让 tracepoint 自动呈现为 Perfetto counter 轨道的约定。计数器同样可以按线程/进程分组但本示例演示按 CPU 分组。此类解析在TP_STRUCT__entry中要求的字段u64 counter_value接受任何整型类型int scope_cpu示例 tracepoint 声明命名为trk_example/cpu_counter_example// trace/events/trk_example.h #undef TRACE_SYSTEM #define TRACE_SYSTEM trk_example #if !defined(_TRACE_TRK_EXAMPLE_H_) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_TRK_EXAMPLE_H_ #include linux/tracepoint.h TRACE_EVENT(cpu_counter_example, TP_PROTO( u64 counter_value, int scope_cpu ), TP_ARGS(counter_value, scope_cpu), TP_STRUCT__entry( __field(u64, counter_value) __field(int, scope_cpu) ), TP_fast_assign( __entry-counter_value counter_value; __entry-scope_cpu scope_cpu; ), TP_printk( counter_value%llu cpu%d, (unsigned long long)__entry-counter_value, __entry-scope_cpu ) ); #endif /* This part must be outside protection */ #include trace/define_trace.h被插桩代码示例static unsigned int n 0 trace_cpu_counter_example(n, smp_processor_id());使用前面示例的配置录制后的可视化效果计数器增量被归因于执行 tracepoint 的 CPU因为使用了smp_processor_id()作为 CPU 索引如果对该 tracepoint 更合理也可以改用静态索引作用域分组规则的细节一个使用单一 tracepoint 录制的 trace 通常会产生 N 条独立轨道其分组规则对 slice 轨道和 counter 轨道相同如下完整参考见 kernel-track-event.md默认仅指定必填字段行为是线程级作用域事件按命中 tracepoint 的线程的线程 ID 分组每个有事件的线程一条轨道E事件终止该线程上最近开启的 slice。如果事件带有scope_前缀的字段事件按该字段的值分组其中一些预定义名称有特殊含义scope_tgid进程级轨道值必须是真实存在的进程调用线程不必属于该进程UI 会把该轨道呈现在进程分组下scope_cpuCPU 级轨道发出代码不必运行在该 CPU 上scope_your_feature_idx自定义轨道 ID 分配默认线程级轨道使用 ftrace 系统本身记录的、命中 tracepoint 的线程 ID。作用域 ID 不必与 OS 层面的概念相关。如果事件包含track_name字段事件会在上述基础上按该名称再增加一个分组维度E事件将终止具有完全一致 track name 的最近开启 slice即使同一线程/进程/CPU 等作用域内存在多条命名轨道。最终效果录制事件按{tracepoint} x {track name} x {scope id}的唯一组合分组后两者分别默认为 tracepoint 名称和线程 ID。slice 轨道上的 slice 必须严格嵌套——所有 slice 都必须在父 slice 之前终止见异步 slice 概念。需要通过轨道命名或作用域来保证该不变式。tracepoint 名称和子系统可以是任意的头部可以声明任意数量的符合这些模板的 tracepoint每个 tracepoint 独立处理对额外字段、字段顺序或TRACE_EVENT()声明的其他部分没有任何约束包括 printk 说明符甚至不需要打印 Perfetto 特定字段。该约定并不显式绑定静态 tracepoint一个创建了带相关字段的 tracefs 条目的动态 probe例如 kprobe同样可以工作。结语三条路径如何选择路径是否需要修改 Perfetto能力适用场景Part Atracebox直接录制否记录任意 ftrace 事件UI/SQL 中查看原始事件快速验证自定义 tracepoint、本地实验Part B完整接入是protobuf 生成 trace_processor 解析 UI 插件注册任意结构化解析转为专用 SQL 表与 UI 轨道需要为 tracepoint 提供通用/专用解析且改动会上游化Part CKernel Track Events 约定否自动生成 slice/instant/counter 轨道线程/进程/CPU 级作用域本地内核或不会上游化的模块只需简单可视化Part C 目前仍是一套演进中的约定其参考文档 kernel-track-event.md 明确说明设计仍可能调整对于把精力放在内核插桩本身、又希望获得第一类轨道可视化体验的开发者是最省力的方案。【免费下载链接】perfettoProduction-grade client-side tracing, profiling, and analysis for complex software systems.项目地址: https://gitcode.com/GitHub_Trending/pe/perfetto创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考