线上系统出问题了?strace工具让你分分钟定位根因!
说到线上排查问题,我想起前段时间遇到的一个头疼事。有个服务突然变得特别慢,用户投诉一大堆,领导也在催。各种监控指标看起来都正常,CPU、内存、网络都没啥异常,但就是慢得要死。
这种时候你说慌不慌?表面上风平浪静,实际上程序内部不知道在搞什么鬼。传统的监控工具告诉你的信息有限,日志也没发现啥异常。这时候就需要深入到系统调用层面去看看到底发生了什么。
今天我就来聊聊strace这个神器,它可以说是我们运维人员手里的"透视镜",能够清楚地看到程序和操作系统之间的每一次交互。
什么是strace?
strace就是system call trace的缩写,顾名思义就是用来跟踪系统调用的工具。你可以把它理解成一个"窃听器",专门监听程序和内核之间的对话。
每当程序需要做一些操作,比如读写文件、网络通信、创建进程等等,都需要通过系统调用来请求内核帮忙。而strace就能把这些"请求"全部记录下来,包括调用了什么函数、传递了什么参数、返回了什么结果,甚至连耗时多长都能告诉你。
我记得刚开始接触strace的时候,看到那密密麻麻的输出还挺懵的。但用熟了之后真的是爱不释手,很多看似复杂的问题都能通过它快速定位。
基础使用方法
最简单的用法就是直接跟踪一个程序的执行过程:
strace ./my_program这样就能看到my_program运行过程中的所有系统调用了。不过输出信息会很多,刷屏速度特别快,建议把结果重定向到文件里:
strace -o output.txt ./my_program这样所有的跟踪信息就保存到output.txt文件中了,可以慢慢分析。
有时候我们不关心所有的系统调用,只想看特定的几种,比如只关心文件操作相关的调用:
strace -e open,read,write ./my_program这个参数特别有用,能够过滤掉很多无关的噪音,让你专注于关心的部分。
实战案例:排查文件访问问题
我来分享一个真实的案例。之前有个应用启动特别慢,用户反馈说要等好几分钟才能正常使用。
一开始我们怀疑是数据库连接的问题,各种调优数据库配置,结果没什么效果。后来想到用strace来看看启动过程中到底在干什么:
strace -o startup.log -e trace=file ./myapp这里我用了trace=file参数,只关注文件相关的系统调用。分析日志后发现了问题所在:
openat(AT_FDCWD, "/usr/share/fonts/font1.ttf", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/fonts/font2.ttf", O_RDONLY) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/share/fonts/font3.ttf", O_RDONLY) = -1 ENOENT (No such file or directory)
...原来程序在启动时要加载几百个字体文件,但这些文件在服务器上根本不存在!每次尝试打开文件都会失败,然后程序还会重试,导致启动时间被大大延长。
找到根因后解决就简单了,要么安装对应的字体包,要么修改程序配置不加载这些字体。最终选择了后者,启动时间从几分钟缩短到了几秒钟。
这个案例说明了strace的威力,有时候问题的根因可能完全超出你的想象。
跟踪运行中的进程
除了跟踪新启动的程序,strace还能attach到已经运行的进程上:
strace -p <PID>这个功能在生产环境中特别有用。比如某个服务突然CPU使用率飙高,但又不能重启,这时候就可以用strace attach上去看看它在干什么。
我遇到过一个Java应用突然CPU占用100%的情况,用strace attach上去后发现:
futex(0x7f8a4c000b54, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7f8a4c000b54, FUTEX_WAKE_PRIVATE, 1) = 0
futex(0x7f8a4c000b54, FUTEX_WAKE_PRIVATE, 1) = 0
...大量的futex调用在疯狂执行,这通常表示线程间的锁竞争非常激烈。后来配合jstack等工具进一步分析,发现是代码中的一个死锁导致的。
分析网络相关问题
strace在排查网络问题时也很给力。比如程序连接数据库很慢,可以这样跟踪:
strace -e trace=network ./myapp或者更具体一点:
strace -e connect,send,recv ./myapp我曾经遇到过一个奇怪的问题,应用连接Redis总是超时。从监控看Redis服务器完全正常,网络也没问题。用strace跟踪后发现:
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("192.168.1.100")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=3, events=POLLOUT}], 1, 5000) = 0 (Timeout)发现程序确实在尝试连接Redis,但connect调用返回了EINPROGRESS,然后poll等待5秒钟后超时。这说明网络层面的连接建立就有问题。
进一步排查发现是防火墙规则的问题,某个安全策略阻止了到Redis服务器的连接。修改防火墙规则后问题立即解决。
性能分析技巧
strace还有一个很实用的功能就是统计系统调用的耗时:
strace -c ./myapp这会在程序结束后显示一个统计表格,告诉你哪些系统调用耗时最多、调用次数最频繁等信息。对于性能优化来说特别有价值。
比如某次分析一个批处理程序时,发现write调用占用了大部分时间:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
89.23 2.456780 2457 1000 write
5.12 0.141023 141 1000 read
3.45 0.094856 95 1000 open
2.20 0.060541 61 1000 close这说明程序在I/O写入上花费了太多时间。进一步分析发现程序每次只写很少的数据,但调用次数很频繁。后来通过缓冲机制批量写入,性能提升了好几倍。
过滤和定制输出
实际使用中,strace的输出往往非常多,需要合理过滤才能找到有用信息。除了前面提到的-e参数,还有一些其他的过滤选项:
只看失败的系统调用:
strace -e trace=all -e status=failed ./myapp只看特定的信号:
strace -e signal=all ./myapp显示时间戳:
strace -t ./myapp显示相对时间:
strace -r ./myapp这些选项可以组合使用,根据具体需求来定制输出格式。
注意事项和最佳实践
使用strace时有几个需要注意的地方:
性能影响:strace会显著降低程序的执行速度,因为每个系统调用都需要被拦截和记录。在生产环境使用时要谨慎,最好在低峰期或者测试环境中使用。
权限问题:要跟踪其他用户的进程,通常需要root权限。而且有些系统调用可能涉及敏感信息,使用时要注意安全性。
输出量大:对于长时间运行的程序,strace的输出可能会非常庞大。建议使用-o参数将输出重定向到文件,并且合理使用过滤选项。
我个人的经验是,刚开始使用strace时不要被复杂的输出吓到。多看几次就会发现规律,很多系统调用的含义其实很直观。比如open就是打开文件,read就是读取数据,connect就是建立连接等等。
结合其他工具使用
strace虽然强大,但单独使用有时候还不够。我通常会结合其他工具一起使用:
- 配合ps、top查看进程状态
- 配合netstat、ss查看网络连接
- 配合lsof查看打开的文件
- 配合tcpdump分析网络包
这样能够从多个角度全面分析问题,提高排查效率。
比如某次排查一个内存泄漏问题时,先用top发现内存使用率持续增长,然后用strace跟踪发现程序在不停地分配内存但很少释放,最后配合valgrind等内存分析工具定位到了具体的代码位置。
实战演练
我建议大家平时多练练手,可以用strace跟踪一些常见的命令看看它们的行为:
strace ls /tmp
strace cat /etc/passwd
strace ping -c 3 baidu.com通过观察这些简单命令的系统调用,能够加深对Linux系统工作原理的理解。
还可以写一些简单的测试程序,故意制造一些问题,然后用strace来排查。比如:
#include <stdio.h>
#include <unistd.h>
int main() {
FILE *fp = fopen("/nonexistent/file.txt", "r");
if (fp == NULL) {
perror("fopen failed");
return 1;
}
fclose(fp);
return 0;
}编译运行这个程序,然后用strace跟踪:
gcc test.c -o test
strace ./test你会看到程序尝试打开一个不存在的文件,系统调用返回错误,这样就能直观地理解程序的执行流程。
总的来说,strace是每个运维人员都应该掌握的重要工具。它能够帮助我们深入理解程序的行为,快速定位各种奇怪的问题。虽然刚开始可能觉得输出信息有点复杂,但只要多练习几次,很快就能上手。
在实际工作中,我经常把strace和其他监控工具结合起来使用,形成一套完整的问题排查流程。遇到问题时不要慌,先用基础监控工具看看大概情况,然后用strace深入到系统调用层面分析,基本上大部分问题都能找到根因。
记住,工具只是手段,关键还是要理解背后的原理。多实践、多思考,相信大家都能成为排查问题的高手!
如果觉得这篇文章对你有帮助,欢迎转发分享给更多的小伙伴。我们一起在运维的道路上互相学习,共同进步!
公众号:运维躬行录
个人博客:躬行笔记