尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Linux性能排查利器:从strace到bpftrace,一文讲透trace工具家族

发布时间:2026/9/24 21:11:03

资讯中心
01
ARTICLE

Linux性能排查利器:从strace到bpftrace,一文讲透trace工具家族

Linux性能排查利器:从strace到bpftrace,一文讲透trace工具家族
线上服务P99抖动到心慌CPU、内存、IO看着都正常这时候你会怎么办如果第一反应只是打开top再盯一遍那大概率还会盯着屏幕怀疑人生。我第一次遇到这个场景时盯着监控面板看了一下午最后是靠Linux trace性能分析工具才把问题挖出来——所谓trace就是让系统自己把运行轨迹记录下来精确到每一次系统调用、每一次内核函数调用、每一次线程被唤醒和抢占。它不是告诉你系统是否繁忙而是告诉你系统到底在忙什么、哪些等待不合理。这篇文章写给三类人刚接触Linux性能排查、分不清strace和perf trace区别的入门者已经在用top、vmstat、pidstat但遇到疑难杂症总感觉隔靴搔痒的进阶用户以及想系统了解trace工具家族、知道什么场景该掏出哪把刀的运维和开发同学。我会把几个主力工具的原理、用法、实测开销和踩坑经验一起讲透最后用一个我实际排查过的线上案例串起来。1. 用trace之前先把工具家族谱系认全1.1 “trace到底在trace什么”一个理发店排队的故事很多性能问题卡在“不知道程序在等什么”。常规性能工具看的是结果CPU高不高、内存够不够、磁盘忙不忙。但“高”和“不够”背后是什么原因这类工具给不出答案。我习惯用一个理发店的例子解释trace。想象你观察一家理发店一整天的运营情况top帮你统计出“下午3点客流最旺、店里平均5个人排队”这是profiling采样后算平均值和分布但要搞清楚“为什么下午3点会堵”你需要的是另一个角度的数据每个顾客几点进门、几点坐下、洗完头几点起来、中间有没有因为等吹风机干等、有没有人插队——把每个时刻发生的事件按时间顺序记下来这就是tracing。Linux trace工具家族就是一套“事件记录设备”它钩在用户态和内核态的各个关键位置把程序运行过程中的系统调用、内核函数、信号、调度事件、用户态函数调用等统统记录下来。有了这份轨迹档案你才能回答那些常规监控回答不了的问题为什么一次写入耗时100ms为什么线程明明可运行了却等了300ms才上CPU为什么同一个函数调用有时1us有时10ms1.2 六大主力工具分工一张表认清谁管哪一段Linux下叫trace的工具不少但它们追踪的对象和场景差异很大很多人一上来就被弄晕。我按自己实际使用的频率把它们分成六类工具追踪对象典型场景侵入性上手成本strace用户态程序发起的系统调用排查文件读写、网络、权限相关延迟很高ptrace打断低ltrace动态库函数调用定位用户态库函数级别问题高低ftrace内核函数调用和内核事件内核时延、调度延迟、函数调用链极低中perf trace系统调用、内核事件、调度事件聚合统计事件关联分析低中bpftrace内核/用户态动态探针、tracepoint自定义指标、快速验证假设低中高LTTng内核用户态事件的高精度记录长时间全量追踪、后置分析中低高这个表有个容易忽略的点strace和perf trace追踪的对象部分重叠但机制完全不同这也是为什么很多人觉得“strace慢得没法用perf trace就轻快很多”。后面的章节我会详细拆开讲。另外提醒一句其实“trace”这个词在不同领域意思差很多。前端常说的trace组件、分布式链路追踪说的是跨服务的调用链追踪跟Linux内核的trace工具完全是两码事。这篇文章只聚焦后者——在单机Linux上追踪内核和进程状态的工具。2. strace排查用户态与内核态交互的第一步但别拿它硬扛2.1 一条命令看到程序的“一举一动”strace可能是大多数人和trace工具的第一次相遇因为它上手门槛最低一句话就能把进程所有系统调用打出来。strace -f -e tracefile,network,process -p 12345-f表示跟踪子线程-e tracefile,network,process只过滤文件、网络、进程相关调用-p指定进程号。输出格式大概是这样的[pid 12345] 14:30:01.002345 openat(AT_FDCWD, /etc/app/config.yaml, O_RDONLY) 4 [pid 12345] 14:30:01.002356 read(4, server:\n port: 8080\n, 512) 22 [pid 12345] 14:30:01.002902 connect(3, {sa_familyAF_INET, sin_porthtons(6379), sin_addrinet_addr(10.0.0.5)}, 16) -1 ETIMEDOUT (Connection timed out)每一行都是一个系统调用的完整记录调用名、参数、返回值。 -1 ETIMEDOUT直接告诉你连接超时了 4说明打开文件成功、fd是4。排查权限问题、配置文件找不到、网络连接失败这类问题strace基本是首选。有一点很多人不知道strace不仅能看系统调用还能看信号。比如进程被莫名kill掉strace -e tracesignal -p PID能抓到信号来源虽然是发送方进程不是发起信号的用户级调用栈。2.2 strace为什么会让程序慢一个数量级这里必须说清楚一个反直觉的事实strace对性能的影响非常大尤其是系统调用密集的程序。我实测过一个大量pread的Java进程正常1秒能完成的批处理操作挂上strace直接8秒以上某些场景吞吐掉到十分之一。原因在于strace的底层机制。它依赖ptrace系统调用每次目标进程发起系统调用内核都会把进程停下来通知跟踪者“调用了XX”跟踪者确认处理后放行等系统调用结束返回又得通知一次。进一次出一次两次打断程序在用户态和内核态之间反复横跳开销可想而知。有同事跟我形容过strace跑起来像给运动员脚上绑了两个沙袋每一步都在跟空气搏斗。所以在生产环境用strace要记住三条经验用-c做统计模式而不是逐条记录。strace -c -p PID跑几十秒最终只输出每种系统调用的次数、耗时总和、平均耗时轻量很多。必须逐条跟踪时用timeout 10 strace -o /tmp/trace.log -f -e traceread,write -p PID这样的形式限时执行输出重定向到文件避免糊在终端上把shell卡死。永远不要长时间挂在一个繁忙进程上。strace适合“秒级取证”不适合“长期监控”。2.3 strace常用参数速查与组合技巧参数作用我的惯用玩法-f/-ff跟踪子进程 / 每个子进程单独输出到文件排查多进程服务必加-f-tt显示微秒级时间戳看一次调用的精确耗时-T显示每个系统调用的耗时和-tt配合定位慢调用-e tracefile,network,process,signal按类别过滤只跟踪关心的类别降低干扰-e trace%file只跟踪文件相关调用查“找不到配置文件”最快-s 200显示最多200字节的字符串内容默认32字节经常不够看-y打印fd对应的路径看到read(4, ...)时直接显示fd 4是哪个文件-o file输出到文件必用-p PID附加到运行中进程线上排查主要靠它-c汇总系统调用统计先看统计再决定要不要细看我排查慢IO的固定组合长这样strace -f -tt -T -y -e traceread,write,fsync,fdatasync,openat -p PID -o /tmp/trace.log跑完以后用awk按耗时排序几行就能找出最慢的系统调用awk {print $NF, $0} /tmp/trace.log | sort -rn | head -20-T输出的耗时在每行末尾直接按它排序就能定位“哪个调用慢得离谱”。这个思路比对着几千行输出肉眼翻高效得多。3. ftrace内核自带、零依赖适合查调度和内核函数延迟3.1 先找到ftrace的“总控台”strace管的是用户态到内核态的边界但很多时候问题发生在内核内部一个内核函数调用慢或者线程状态切换的时机不对。这种场景strace就使不上劲了得请出ftrace。ftrace最朴实的特点是不会骗人它就在内核里通过tracefs文件系统暴露控制接口不需要装任何额外软件很多容器镜像里它也能用前提是host没有限制你的权限。老版本路径在/sys/kernel/debug/tracing新内核通常在/sys/kernel/tracing两个路径可以兼容处理mount -t tracefs tracefs /sys/kernel/tracing 2/dev/null || mount -t debugfs debugfs /sys/kernel/debug 2/dev/null cd /sys/kernel/tracing关键文件就这几个记住它们就掌握了ftrace的操作逻辑available_tracers当前内核支持的跟踪器列表常见的有function、function_graph、sched_switch等。current_tracer当前启用哪个跟踪器echo function current_tracer即可切换。available_filter_functions所有可跟踪的内核函数列表大几千个。set_ftrace_filter设置要跟踪的函数支持*通配符。tracing_on总开关写1开启记录写0停止。trace和trace_pipe读取跟踪结果前者读完还在后者边读边清空。3.2 完整操作抓一个内核函数的调用轨迹要说清楚ftrace怎么用我直接给一个我实际抓过多次的流程——跟踪文件打开的内核函数调用cd /sys/kernel/tracing # 先关掉记录避免历史数据干扰 echo 0 tracing_on # 清空已有缓冲 echo trace # 启用function跟踪器 echo function current_tracer # 只跟踪do_sys_openat2这个函数其余不记录 echo do_sys_openat2 set_ftrace_filter # 打开记录开关 echo 1 tracing_on # 触发一次文件操作比如在其他终端执行 cat /etc/hostname # 停掉记录 echo 0 tracing_on # 查看结果 head -50 trace输出大致长这样# tracer: function # TASK-PID CPU# TIMESTAMP FUNCTION # | | | | | cat-12345 [003] .... 20231.204082: do_sys_openat2 - __x64_sys_openat‑前面的函数是被调用的目标后面是调用它的上一级合起来就是一条调用关系。如果需要看更完整的调用链把跟踪器切成function_graphecho function_graph current_tracer echo do_sys_openat2 set_ftrace_filter echo 3 max_graph_depth # 只展开3层防止输出爆炸输出会变成缩进的函数调用树每层函数进出都有时间戳括号里的微秒数就是函数执行耗时1) | do_sys_openat2() { 1) 0.320 us | getname_flags(); 1) 0.130 us | get_unused_fd_flags(); 1) 2.510 us | do_filp_open(); 1) 3.000 us | }这套东西最妙的点在于它不依赖任何用户态调试工具在一个最小化安装的Linux上也能用非常适合做内核层问题的初判。3.3 关键用法ftrace查调度延迟和函数耗时分布ftrace除了function tracer还有一个我常用的wakeup和sched_switch跟踪器。前者记录进程从被唤醒到真正上CPU的最长延迟后者记录每个CPU上的线程切换序列。排查“线程明明ready了却迟迟不运行”这类调度问题它们比perf更直观。举一个实际用法。我想知道某个线程的调度延迟基本盘cd /sys/kernel/tracing echo wakeup current_tracer echo 12345 set_ftrace_pid # 只跟踪这个PID echo 1 tracing_on sleep 5 cat trace输出的wakeup_lat字段直接给出该进程最大唤醒延迟。我在有些共享型云主机上跑过这个数字能到几十毫秒瞬间就知道机器负载高、CPU争抢严重。另一个容易踩的坑是ftrace的function跟踪器会把每个匹配函数的每次调用都记下来如果filter设置得太宽比如echo schedule* set_ftrace_filter记录量能在几秒内把缓冲区塞满然后trace文件里全是截断的数据。我建议先点查几个可疑函数或者配合set_ftrace_pid只跟踪目标进程别一上来就全量捕获。4. perf trace和bpftrace把追踪能力提升到“事件级编程”4.1 perf trace为什么比strace“轻”perf trace和strace看到的东西有一大半重叠它也能列出系统调用、时间戳、返回值但实现机制完全不同。perf trace基于perf_event_open和内核跟踪点tracepoint不会像ptrace那样每次系统调用打断进程两次所以开销低很多适合做略微长一点的采样观察。基本用法很接近perf trace -e openat,read,write,close -p 12345 -- sleep 5它还能直接显示线程切换、signal、页错误等strace不太方便追踪的事件。我比较喜欢它的--summary模式跑完自动汇总每种调用的次数和耗时相当于strace-c的增强版perf trace -p 12345 --summary sleep 5实际体验下来perf trace的开销比strace低一个量级。对一个每秒几万次系统调用的进程strace可能拖慢5到10倍perf trace一般能控制在1.5倍以内。不过话说回来perf trace对内核版本要求高一些老内核上有些tracepoint可能不存在建议在4.4内核上使用。4.2 bpftrace一行脚本打天下如果说ftrace是内窥镜、perf trace是显微镜那bpftrace就是一把可编程的手术刀。它基于BPF技术允许你用类awk语法编写小段脚本挂载在内核跟踪点、kprobe、uprobe上安全地在内核态做过滤、统计、聚合然后只把精简结果返回用户态。先感受一下它的一行脚本能做什么# 统计每个进程打开文件次数按进程名聚合 bpftrace -e tracepoint:syscalls:sys_enter_openat { [comm] count(); } # 实时打印新进程创建事件 bpftrace -e tracepoint:sched:sched_process_fork { printf(%s - %s\n, comm, args-child_comm); } # 统计各进程实际占用的CPU运行时间ns输出直方图 bpftrace -e tracepoint:sched:sched_stat_runtime { [comm] lhist(args-runtime, 1000, 100000, 10000); }第一个脚本的[comm]表示用一个以进程名comm为键的map做累加count()是计数函数。第二个脚本直接读args-child_comm从tracepoint参数里取新进程名。第三个用lhist输出线性直方图能看到每个进程的运行时间分布——这在分析CPU调度不均的时候特别直观。对于“某个函数调用慢”这类假设验证bpftrace更是利器。比如怀疑openat慢可以用kprobe和kretprobe配对计算每次调用耗时bpftrace -e kprobe:do_sys_openat2 { start[tid] nsecs; } kretprobe:do_sys_openat2 /start[tid]/ { open_lat_ns hist(nsecs - start[tid]); delete(start[tid]); }这里的逻辑是进函数时记下时间戳nsecs出函数时算差值hist输出耗时直方图。跑几百次操作后就能看到耗时的中位数、尾延迟分布而不是只能拍脑袋猜“好像有点慢”。4.3 bpftrace的适用边界和我踩过的坑bpftrace能做的远不止这些它还能挂用户态uprobe、跟踪内核函数栈、打印数据结构里的指定字段。但也有几个现实约束内核必须开启BPF支持且部分老内核或云厂商裁剪内核上探针可能不全。用前先跑bpftrace -l | head看看探测点列表是否正常。kprobe是基于函数名的内核升级后函数名可能失效tracepoint相对稳定优先使用tracepoint是更稳妥的工程选择。bpftrace脚本在繁忙系统上长时间跑也会带来额外开销虽然比strace低很多但不建议当成常驻监控进程。它更适合“发现问题后做定点取证”。我现在的习惯是先用perf trace --summary快速判断系统调用层有没有异常再用bpftrace写一段针对性的统计脚本把问题按进程、函数、耗时分布精确切出来。这套组合拳比单纯盯着strace日志高效太多。5. 一个真实案例复盘接口偶发超时我是怎么用trace层层定位5.1 现象业务侧P99抖动常规指标全部正常某天线上服务反馈接口P99从正常20ms漂移到500ms而且不是持续高是偶发毛刺。我第一时间打开监控面板CPU利用率30%多内存充足磁盘IO等待很低网络流量也没有异常。top、vmstat、pidstat看了一圈全是“正常”两个字。这种场景最折磨人。常规监控告诉你一切正常但业务端明明在超时。我当时的判断是问题大概率不出在“资源不够”而是出在“某个等待环节”。5.2 第一层strace确认业务自身没有阻塞先用strace采样一下业务线程目的不是看全部调用而是验证“这个接口慢”到底是不是发生在read/write这些IO环节。timeout 10 strace -f -tt -T -e traceread,write,epoll_wait,futex -p 业务PID -o /tmp/strace.log结果出乎意料在报慢的时间窗口内线程从epoll_wait返回后马上就能read到数据read本身耗时不到0.1ms。也就是说网络数据早就到了但线程没有及时被唤醒——问题在更底层业务系统调用本身并没有慢。strace看的是“进程做了什么”但它看不到“进程没被调度时发生了什么”。这一步只能排除业务阻塞下一个怀疑对象变成了调度器。5.3 第二层perf sched还原调度时序接下来用perf sched抓一段调度事件看线程从“被唤醒”到“真正在CPU上运行”之间花了多少时间perf sched record -g -p 业务PID -- sleep 10 perf sched latency --sort max输出里有一列wakeup latency代表任务从唤醒到获得CPU的平均/最大等待时间。我一看最大等待时间直接飙到400多毫秒。这个数字对应上业务报的P99超时基本对上了。这说明一个问题线程不是没就绪而是就绪了却一直没有被调度到。为什么会这样要么CPU被别的进程抢占要么cgroup配额限了流要么机器本身CPU steal严重。5.4 第三层bpftrace量化runqueue delay锁定cgroup配额perf sched已经定位到“调度延迟”但还没回答“为什么延迟这么高”。这个阶段继续用perf sched也能查但信息太杂。我改用bpftrace在调度器相关探针上直接统计排队时间bpftrace -e tracepoint:sched:sched_wakeup { wake_ts[args-pid] nsecs; } tracepoint:sched:sched_switch { if (wake_ts[args-prev_pid] ! 0) { runq_delay_us[args-prev_comm] hist((nsecs - wake_ts[args-prev_pid]) / 1000); delete(wake_ts[args-prev_pid]); } }这段脚本的意思是记录每个进程被唤醒的时间点在下一次切换进来时用当前时间减去唤醒时间得到它在运行队列里排队等待的时长并按进程名输出直方图。跑了几分钟后直方图显示大部分排队的进程都集中在某个容器组内延迟中位数在几十毫秒尾部在几百毫秒。我再去查容器cgroup的CPU配额确认是cpu.max设置过小业务突发流量把配额打满了线程只能在runqueue里等着。问题根因不是代码、不是锁、更不是单机性能而是共享计算资源的配额配置不合理。5.5 修复与验证把cgroup的CPU配额从2核扩到4核同时和业务方沟通错峰任务时间再跑同样的监控P99回落到了25ms左右。整个排查过程用时一个下午如果用穷举法调配置、看代码可能几天都不一定有结果。trace工具在这里扮演的角色不是“锦上添花”而是把问题从“业务层”一步步逼到“调度层”再到“资源配额层”每一层都有数据支撑。这个案例也让我形成了一条固定的排查路径先strace排除用户态问题再perf sched看调度时序最后bpftrace量化关键路径指标。三级递进很少落空。6. 工具选型决策指南和三条我踩出来的铁律6.1 五步决策现场到底该掏哪把刀很多读者可能会问strace、ftrace、perf trace、bpftrace各有擅长到了故障现场怎么快速选我总结了一个五步判断法基本能覆盖大部分情况判断问题发生在用户态还是内核态。如果是“打开文件失败”“连接被拒”“读写超时”先用strace如果是“线程不执行”“进程D状态”“内核函数时延异常”直接跳到ftrace或perf。判断你需要“逐条事件”还是“聚合统计”。逐条事件看strace或perf trace要按pid、函数、耗时做聚合分布bpftrace更合适。判断环境能不能装工具。装不了就跑ftrace内核自带能装就优先perf trace和bpftrace查询能力上一个台阶。判断时间窗口长短。strace适合秒级取证perf trace可以撑几十分钟要跑几小时全量记录还是LTTng这类带缓冲和落盘的方案更稳。判断要不要跟业务变量联动。比如“按请求URL统计打开文件次数”这种关联用户态业务数据的场景bpftrace动态追踪是唯一解。下面这张表把常见场景对应到推荐工具方便收藏故障现象第一选择备选文件打开失败、权限错误straceperf trace网络连接超时、read/write慢strace -e tracenetworkperf trace进程不响应、注册不上strace gdbftrace sched_switch内核函数时延异常ftrace function_graphperf ftraceCPU调度延迟高perf schedbpftrace sched统计用户态库函数调用可疑ltraceuprobebpftrace自定义指标统计、长尾耗时分布bpftraceperf record script6.2 生产环境使用trace的三条铁律踩过几次坑之后我给自己定了三条规矩分享出来供大家参考。第一限时限量。线上环境任何trace操作都要带时限timeout 10 strace ...、-- sleep 5目的就是防止忘记关导致服务被拖垮。ftrace的buffer也要控制大小我通常先把buffer_size_kb调小到几百KB够用就行。第二输出必须落文件。trace命令的原始输出量远超想象不重定向时终端会被几十万行日志刷爆造成额外负载。所有trace输出统一写到/tmp下用完清理。第三先猜后证别盲目抓包。trace工具是验证假设的不是漫无目的扫描仪。上工具前先想清楚“我怀疑是哪个环节慢”“这个工具能不能证明或证伪这个假设”。带着问题抓trace效率是瞎抓的十倍不止。6.3 trace工具和线上监控体系的分工协作最后说一个经常被误解的点trace工具不是用来代替监控体系的它们解决的是不同时间尺度的问题。监控告警负责“发现在先”7x24小时盯着CPU、内存、延迟、错误率一旦超阈值就告警。trace工具负责“定性在后”在收到告警后的几分钟内上机器快速还原现场、定位根因。一个是体温计一个是手术刀完全可以共存。我比较推荐的做法是把trace工具接到一个便捷入口比如在跳板机上放一套整理好的脚本集strace的几个固定组合、perf sched采集、bpftrace统计模板故障发生时一键执行输出统一落盘。这样即使不是资深内核专家也能按图索骥完成第一轮取证大幅缩短平均恢复时间。从我个人的实践经验看trace工具学的不是“命令怎么敲”而是一种排查思维任何性能问题都不是凭空出现的它一定对应着某个环节的等待或争抢trace只是帮你把这个“等待”具象化。把这套思维练熟再复杂的疑难杂症也有章可循。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。