
性能瓶颈找不到ftraceperf组合拳5分钟定位内核问题嵌入式Linux最让人头秃的场景系统卡了但你不知道卡在哪。CPU跑满了吗谁在吃CPU是内核态还是用户态中断太多了还是调度出问题了还是DMA卡住了不用猜ftrace和perf这两个Linux自带的工具连安装都不用能回答你所有问题。先说perf——谁在吃CPUperf是基于硬件性能计数器的采样工具开销极低适合不知道问题在哪的盲搜阶段。1. 看CPU使用率# 看CPU在忙什么perftop按比例显示各函数占用的CPU。看到irq_work排第一那八成是中断风暴。看到do_sys_open排第一有人在疯狂打开关闭文件。2. 热点函数定位# 采样10秒存数据perf record-g-a--sleep10# 分析热点perf report-g记录调用链-a所有CPU。看完就知道最热的函数是谁调起来的。3. 上下文切换分析怀疑系统在频繁切换进程perf sched record --sleep5perf sched latency输出每个进程的调度延迟。看到某个进程平均延迟几十ms那它可能被优先级更高的中断或实时线程饿死了。真实案例一次监控摄像头画面卡顿。perf top一看copy_page占CPU 40%——哪个缺德的在大量拷贝内存perf report看调用链定位到v4l2的vb2 buffer queue里有个不合理的memcpy。加上dma_buf直接映射后CPU直接降到5%。再说ftrace——内核内部的printk但不用重编ftrace是内核自带的trace框架几乎不耗资源就可以trace内核函数的调用。新版内核用tracefs挂在/sys/kernel/tracing老的在debugfs。1. 打开function graphcd/sys/kernel/tracing# 设置要trace的函数echo__alloc_pages_nodemaskset_graph_functionechofunction_graphcurrent_tracerecho1tracing_onsleep3echo0tracing_on# 看结果cattrace|head-50输出类似kworker/1:0-98 [001] ... 1234.5678: __alloc_pages_nodemask() { kworker/1:0-98 [001] ... 1234.5679: get_page_from_freelist() { kworker/1:0-98 [001] ... 1234.5680: zone_watermark_ok() { kworker/1:0-98 [001] ... 1234.5680: } ... kworker/1:0-98 [001] ... 1234.5695: } kworker/1:0-98 [001] ... 1234.5696: }每一层缩进代表函数调用深度秒级看出谁慢。2. 中断/调度跟踪echo0tracing_onechotrace# 清空缓冲区echoirq:irq_handler_entryset_eventechosched:sched_switchset_eventecho1tracing_onsleep3echo0tracing_oncattrace可以看到每个中断的发生频率、处理时间以及每次调度切换的原进程→目标进程。3. 动态事件最狠的不想trace内核函数想trace你自己的驱动不用改代码echop:myprobe my_driver_write arg1$arg1 arg2$arg2kprobe_eventsecho1events/kprobes/myprobe/enable甚至可以在函数返回时看返回值echor:myret my_driver_read $retvalkprobe_events不用重新编译内核不用加printk不用重新部署驱动。这就是ftrace最狠的地方。CPU繁忙调度延迟IO等待内核函数中断/调度自定义驱动系统卡顿perf top 初步扫描perf record/report 定位热点函数perf sched latencyiostat perf需要更细的trace?ftrace function_graphftrace event tracekprobe 动态插桩找到瓶颈perf ftrace 组合拳实战光讲单个工具没意思来一个真实场景CPU idle时功耗居高不下。第一步perf top扫一下Events: 1K cycles 43.21% [kernel] [k] __do_softirq好软中断太频繁。第二步perf record看谁发的中断perf record-eirq:irq_handler_entry-a--sleep5perf script看到/proc/interrupts里eth0的TX中断每秒触发了2000次。但这设备明明没多少数据在发。第三步ftrace看中断处理过程echofunction_graphcurrent_tracerechoeth_irq_handlerset_graph_functionecho1tracing_on;sleep2;echo0tracing_oncattrace发现中断处理函数里调了一个wake_up_process唤醒了一个worker线程但那worker大部分时间无事可做又schedule回去了——白忙一场。第四步解决方案原来网卡驱动在NAPI poll模式下每次中断都调用了napi_schedule。但没判断实际是否有数据。加上空包检测后中断次数从2000次/秒降到300次/秒CPU idle时的功耗直接降了0.5W。这个案例里没有perf和ftrace你只能在代码里一行行看、加printk重编、部署、复现——一个下午就没了。用工具10分钟。速查表场景工具命令谁在吃CPUperf topperf top热点函数调用链perf record/reportperf record -g -a sleep 10调度延迟perf schedperf sched latencyCache Missperf statperf stat -e cache-misses ./app内核函数调用图ftraceecho function_graph current_tracer中断频率ftrace eventecho irq:irq_handler_entry set_event函数参数/返回值kprobeecho p:... kprobe_events谁调了特定函数ftrace filterecho func set_ftrace_filter一句话总结perf告诉你哪慢ftrace告诉你为什么慢。两个加起来嵌入式Linux性能问题99%都能在10分钟内定位。别再只会topprintk了那是上一个十年的玩法。