线上故障排查神器!用strace和ftrace揪出系统调用的“幕后黑手“

前几天凌晨2点,我又被监控报警给吵醒了。服务器CPU飙到90%,但是top看了半天也找不到罪魁祸首。这种时候,就得请出我们运维人员的两大法宝了——strace和ftrace。

说实话,刚开始接触这两个工具的时候,我也是一脸懵逼。什么系统调用、内核跟踪,听起来就很高大上的样子。但是用多了你就会发现,这玩意儿简直就是排查问题的神器!今天就跟大家分享一下我这些年用下来的一些心得。

先说说系统调用这个东西

你可能会问,系统调用到底是个啥?简单来说,就是应用程序想要干点什么事情的时候,必须要通过内核来完成。比如你的程序要读个文件,要发个网络请求,要分配点内存,这些都得通过系统调用来实现。

就像你在家里想吃外卖,你不能直接跑到餐厅后厨去炒菜吧?你得通过外卖平台下单,然后平台帮你处理。系统调用就是这个"外卖平台",应用程序就是你,内核就是后厨。

我记得刚工作那会儿,有个同事总是说"程序跑得慢,肯定是代码写得烂"。后来用strace一看,好家伙,程序每秒钟调用了几万次write系统调用,每次只写一个字节!这不是坑爹吗?

strace:系统调用的"偷窥狂"

strace这个工具,说白了就是个"偷窥狂",专门监视程序都调用了哪些系统调用。

基本用法很简单

最简单的用法就是直接在命令前面加个strace:

strace ls /tmp

然后你就会看到一堆输出,密密麻麻的,第一次看肯定会被吓到。不过别慌,我们慢慢来分析。

如果你想跟踪一个正在运行的进程,用-p参数:

strace -p 1234

这里1234是进程ID,你可以通过ps命令找到。

输出内容怎么看

strace的输出格式基本是这样的:

系统调用名(参数1, 参数2, ...) = 返回值

比如:

open("/etc/passwd", O_RDONLY) = 3
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 1024
close(3) = 0

这三行就是一个完整的文件读取过程:

  • open打开文件,返回文件描述符3

  • read从文件描述符3读取数据,读了1024字节

  • close关闭文件描述符3

有时候你还会看到一些错误,比如:

open("/tmp/nonexistent", O_RDONLY) = -1 ENOENT (No such file or directory)

这就是说文件不存在,open调用失败了。

实用参数组合

我平时用得最多的几个参数组合:

只看文件操作:

strace -e trace=file ls /tmp

只看网络操作:

strace -e trace=network curl baidu.com

统计系统调用次数和耗时:

strace -c ls /tmp

这个-c参数特别有用,能给你一个统计报告,告诉你哪个系统调用用得最多,哪个最耗时。

输出到文件:

strace -o trace.log -p 1234

线上环境建议都输出到文件,不然屏幕刷得你眼花缭乱。

真实案例分享

去年有个Java应用启动特别慢,用户都投诉了。开发说代码没问题,我就用strace跟踪了一下启动过程:

strace -f -o startup.log java -jar myapp.jar

这里-f参数很重要,可以跟踪子进程。结果发现这个应用启动的时候,疯狂地在读取/dev/random,每次读取都要等好几秒。原来是某个加密库的配置有问题,改成/dev/urandom就好了。

ftrace:内核级别的"透视镜"

如果说strace是应用层的监控,那ftrace就是内核层的透视镜了。它能让你看到内核内部发生了什么,包括函数调用关系、执行时间等等。

ftrace的工作原理

ftrace是Linux内核自带的跟踪框架,通过debugfs文件系统来操作。说白了就是内核在关键位置埋了很多"探针",你可以选择性地开启这些探针来收集信息。

基本操作步骤

ftrace的操作都是通过/sys/kernel/debug/tracing目录下的文件来完成的。

首先检查ftrace是否可用:

ls /sys/kernel/debug/tracing/

如果看不到这个目录,可能需要挂载debugfs:

mount -t debugfs debugfs /sys/kernel/debug

查看可用的跟踪器:

cat /sys/kernel/debug/tracing/available_tracers

你会看到function、function_graph、nop等选项。

启用函数跟踪:

echo function > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on

查看跟踪结果:

cat /sys/kernel/debug/tracing/trace

停止跟踪:

echo 0 > /sys/kernel/debug/tracing/tracing_on

输出内容解读

ftrace的输出格式大概是这样:

# tracer: function
#
# entries-in-buffer/entries-written: 140080/250280   #P:4
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
            bash-1994  [000] .... 12345.678901: sys_read <-system_call_fastpath
            bash-1994  [000] .... 12345.678902: vfs_read <-sys_read

这里面信息量很大:

  • TASK-PID:进程名和进程ID

  • CPU#:在哪个CPU核心上执行

  • TIMESTAMP:时间戳

  • FUNCTION:调用的函数名

那些点和字母表示系统状态,比如中断是否关闭、是否需要重新调度等等。

function_graph跟踪器

我个人比较喜欢用function_graph跟踪器,它能显示函数的调用关系和执行时间:

echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 运行你要跟踪的程序
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace

输出会是这样的:

 0)               |  sys_read() {
 0)   0.632 us    |    fget_light();
 0)               |    vfs_read() {
 0)   0.112 us    |      rw_verify_area();
 0)   0.887 us    |      ext4_file_read();
 0)   2.221 us    |    }
 0)   3.005 us    |  }

这样就能清楚地看到函数调用的层次关系和每个函数的执行时间。

过滤和限制

ftrace的输出通常会非常多,你需要学会过滤。比如只跟踪特定的函数:

echo 'sys_read sys_write' > /sys/kernel/debug/tracing/set_ftrace_filter

或者只跟踪特定进程:

echo 1234 > /sys/kernel/debug/tracing/set_ftrace_pid

实战技巧和注意事项

用了这么多年,我总结了一些实战经验。

性能影响要考虑 strace和ftrace都会对性能产生影响,特别是ftrace。线上环境使用的时候一定要小心,最好在业务低峰期操作。我见过有同事在生产环境开启了全量ftrace跟踪,直接把服务器搞宕机了。

日志文件会很大 特别是ftrace,输出量可能非常大。记得设置好日志轮转,或者及时清理。有一次我忘记关闭ftrace,结果/var/log被撑爆了。

结合其他工具使用 strace和ftrace最好配合其他工具一起用。比如先用top、iotop找到可疑进程,再用strace详细分析。或者用perf配合ftrace做性能分析。

学会看关键信息 输出信息太多的时候,要学会抓重点。比如看系统调用的返回值、执行时间、调用频率等。错误信息特别要关注,往往问题就藏在那里。

我记得有次排查一个网络问题,strace显示connect系统调用一直返回ETIMEDOUT,但是开发坚持说网络没问题。后来发现是防火墙规则有问题,特定端口被屏蔽了。

一些高级用法

多进程跟踪

strace -f -p 1234

-f参数可以跟踪子进程,对于多进程应用很有用。

时间戳显示

strace -t -p 1234

-t显示时间戳,-tt显示微秒级时间戳,排查性能问题的时候特别有用。

系统调用统计

strace -c -p 1234

运行一段时间后按Ctrl+C,会显示统计信息,告诉你哪些系统调用最频繁、最耗时。

自定义ftrace脚本 我写了个简单的脚本来自动化ftrace操作:

#!/bin/bash
TRACE_DIR="/sys/kernel/debug/tracing"

# 清理之前的跟踪
echo 0 > $TRACE_DIR/tracing_on
echo > $TRACE_DIR/trace

# 设置跟踪器
echo function_graph > $TRACE_DIR/current_tracer

# 设置过滤器
echo $1 > $TRACE_DIR/set_ftrace_pid

# 开始跟踪
echo 1 > $TRACE_DIR/tracing_on

echo "正在跟踪进程 $1,按回车键停止..."
read

# 停止跟踪
echo 0 > $TRACE_DIR/tracing_on

# 显示结果
cat $TRACE_DIR/trace

用法就是 ./trace.sh 1234,比手动操作方便多了。

踩过的坑和经验教训

说起来都是泪啊。刚开始用这些工具的时候,我踩了不少坑。

有一次用strace跟踪一个数据库进程,结果发现性能下降了50%!原来strace对I/O密集型应用的影响特别大。后来我学会了用-e参数只跟踪特定的系统调用,影响就小多了。

还有一次,我在生产环境用ftrace跟踪内核函数,结果忘记设置过滤器,把所有内核函数调用都记录下来了。几分钟就产生了几个GB的日志,差点把磁盘撑爆。从那以后,我都会先设置好过滤条件再开启跟踪。

最坑的一次是,我用strace跟踪一个多线程程序,但是忘记加-f参数,只看到了主线程的系统调用。折腾了半天才发现问题出在子线程上。

与其他工具的配合

这两个工具虽然强大,但是单独使用有时候还不够。我通常会配合其他工具一起用:

配合lsof查看文件描述符 strace显示程序在操作文件描述符3,但是你不知道这是什么文件,这时候用lsof就能看到:

lsof -p 1234

配合netstat查看网络连接 看到程序在进行网络操作,但不知道连接状态,netstat能帮你:

netstat -anp | grep 1234

配合perf做性能分析 ftrace告诉你哪个函数调用频繁,perf能告诉你CPU时间都花在哪里了:

perf top -p 1234

一些实用的过滤技巧

输出太多的时候,学会过滤很重要。

strace过滤示例:

# 只看文件操作
strace -e trace=file,desc ls

# 只看网络操作
strace -e trace=network wget baidu.com

# 排除某些系统调用
strace -e trace=!write,read ls

ftrace过滤示例:

# 只跟踪特定函数
echo 'vfs_*' > /sys/kernel/debug/tracing/set_ftrace_filter

# 排除某些函数
echo '!schedule*' > /sys/kernel/debug/tracing/set_ftrace_notrace

调试技巧分享

多年的经验告诉我,用这些工具调试问题有一些套路。

先用strace找到异常的系统调用 比如程序卡住了,先看看是不是某个系统调用阻塞了。如果是网络问题,通常会看到connect或者read调用长时间不返回。

再用ftrace深入内核层面 如果strace没找到问题,可能问题在内核层面。这时候用ftrace跟踪相关的内核函数,看看是不是内核哪里出了问题。

结合时间戳分析性能 用-t参数显示时间戳,看看哪些操作耗时比较长。有时候一个简单的文件读取操作耗时几秒钟,那肯定有问题。

我记得有次排查一个程序启动慢的问题,strace显示程序在读取某个配置文件的时候特别慢。进一步分析发现,这个文件在NFS挂载的目录下,而NFS服务器响应很慢。换到本地文件系统后,启动时间从30秒缩短到3秒。

总结

strace和ftrace确实是排查问题的利器,但是需要一定的学习成本。我的建议是:

从strace开始学起,相对简单一些。多在测试环境练习,熟悉各种参数的用法。学会看关键信息,不要被大量的输出吓到。线上使用要谨慎,注意性能影响。最好结合其他工具一起使用,效果会更好。

最重要的是,工具只是手段,关键还是要理解系统的工作原理。当你对Linux系统调用、内核机制有了更深入的理解,这些工具才能真正发挥威力。

说实话,掌握了这两个工具之后,我解决问题的效率提高了不少。以前遇到奇怪的问题,只能靠猜测和试错。现在能直接看到程序在做什么,问题往往很快就能定位到。

当然,学习这些工具也不是一蹴而就的。我用了好几年才算比较熟练,期间也踩了不少坑。但是一旦掌握了,你会发现这些投入都是值得的。

希望这篇文章能帮到大家。如果你在使用过程中遇到什么问题,或者有什么好的经验想分享,欢迎留言讨论!


如果这篇文章对你有帮助,别忘了点个赞👍,转发给更多需要的朋友。想了解更多运维干货和实战经验,记得关注@运维躬行录,我会持续分享更多有价值的技术内容!

公众号:运维躬行录

个人博客:躬行笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值