骨灰级大神用print就够了,比如Linux之父Linus Torvalds从不用任何debugger工具: Linus Torvalds: Debugging hell ,还有Python之父 Guido van Rossum 自称使用print语句排查90%的bug,参考 I do not use a debugger 。我也经常使用print大法(或者log),基本思路是先定位大体bug位置,比如某个函数调用,然后把关心的变量打印出来,或者在某个分支位置使用print打个tag。尤其是涉及多线程的时候,断点不一定凑效,或者bug不是100%稳定重现,只能使用print大法,触发bug时查看输出的信息。
当然,如果能用debug工具,我还是更倾向于使用debug,比如gdb、jdb、pdb,这些工具能够单步调试、监控变量等,非常方便,前面介绍了很多,这里不再重复。
如果你只有二进制文件,没有源码,或者编译时没有打开debug, strace 是非常不错的debugger工具,它能够跟踪进程的所有系统调用和信号。先看一段代码:
#include <stdio.h> │
#include <pthread.h> │
#include <unistd.h> │
static void * simple_thread(void *); │
pthread_mutex_t mutex_1= PTHREAD_MUTEX_INITIALIZER; │
pthread_mutex_t mutex_2= PTHREAD_MUTEX_INITIALIZER; │
static void * simple_thread(void * dummy) │
{ │
pthread_mutex_lock(&mutex_2); │
sleep(1); │
pthread_mutex_lock(&mutex_1); │
pthread_mutex_unlock(&mutex_1); │
pthread_mutex_unlock(&mutex_2); │
printf("exit simple thread\n"); │
return NULL; │
} │
int main() │
{ │
pthread_t tid = 0; │
pthread_create(&tid, 0, &simple_thread, 0); │
sleep(1); │
pthread_mutex_lock(&mutex_1); │
pthread_mutex_lock(&mutex_2); │
pthread_mutex_unlock(&mutex_2); │
pthread_mutex_unlock(&mutex_1); │
pthread_join(tid, NULL); │
printf("exit main thread\n"); │
return 0; │
}
先暂且不管这个程序是干什么的(其实根本没用),我们编译运行:
int32bit $ gcc -l pthread test.c
int32bit $ ./a.out
这个进程block了,什么原因呢?
另开一个终端,使用strace工具看看到底block在哪个系统调用:
由输出结果看,futex堵塞了,显然是在等待锁,也就是说,进程发生死锁了。
我们再次运行看看:
int32bit $ strace -f -e futex ./a.out
Process 104573 attached
[pid 104573] futex(0x601080, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 104572] futex(0x6010c0, FUTEX_WAIT_PRIVATE, 2, NULL
从结果发现两个线程都block在futext,因此进一步确定是由于死锁导致的。如果你的程序经常莫名其妙的卡住,可以试试strace。
另外一个例子,参考 Linux Troubleshooting with strace | Benjamin Cane ,
[root@laptop ~]# systemctl start vsftpd.service
Job failed. See system logs and 'systemctl status' for details.
[root@laptop ~]# systemctl status vsftpd.service
vsftpd.service - Vsftpd ftp daemon
Loaded: loaded (/lib/systemd/system/vsftpd.service; disabled)
Active: failed since Sat, 24 Mar 2012 11:55:25 -0700; 2min 28s ago
Process: 19356 ExecStart=/usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf (code=exited, status=1/FAILURE)
CGroup: name=systemd:/system/vsftpd.service
问题是vsftpd服务起不来,也没有看出什么有用的日志。
使用strace启动:
[root@laptop ~]# strace -f /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
<truncated output (it is seriously a lot of output)>
[pid 19499] socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
[pid 19499] setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
[pid 19499] rt_sigaction(SIGCHLD, {0xf5ac00, ~[RTMIN RT_1], 0}, NULL, 8 ) = 0
[pid 19499] rt_sigaction(SIGALRM, {0xf5abd0, ~[RTMIN RT_1], 0}, NULL, 8 ) = 0
[pid 19499] rt_sigaction(SIGHUP, {0xf5ac00, ~[RTMIN RT_1], 0}, NULL, 8 ) = 0
[pid 19499] rt_sigaction(SIGALRM, {0xf5abd0, ~[RTMIN RT_1], 0}, NULL, 8 ) = 0
[pid 19499] bind(3, {sa_family=AF_INET, sin_port=htons(21), sin_addr=inet_addr("0.0.0.0")}, 16) = -1 EADDRINUSE (Address already in use)
[pid 19499] fcntl64(0, F_GETFL) = 0x8002 (flags O_RDWR|O_LARGEFILE)
[pid 19499] fcntl64(0, F_SETFL, O_RDWR|O_NONBLOCK|O_LARGEFILE) = 0
[pid 19499] write(0, "500 OOPS: ", 10) = 10
[pid 19499] write(0, "could not bind listening IPv4 so"..., 36) = 36
[pid 19499] write(0, "rn", 2) = 2
[pid 19499] exit_group(1) = ?
Process 19499 detached
显然是由于端口被占用导致服务起不来的。
还有最经典的内存泄露问题:
int main(int argc, char **argv)
int *p;
while(1) {
p = new int;
return 0;
}
我们使用strace运行,你会发现满屏的brk调用:
data segment地址不断增大(brk(0)是获取当前地址),说明进程不断申请内存,并且没有释放,因此可能存在内存泄露。
如果把代码改成:
#include<cstdio>
int main(int argc, char **argv)
int *p;
while(1) {
printf("HelloWorld!\n");
p = new int[10000];
delete p;
return 0;
}
就不会存在不断调data segment地址的问题,说明进程运行时正常的,至少说明申请和释放基本是平衡的。
还有一次经历是OpenStack cinder-volume启动失败,日志显示rbd driver初始化失败,通过strace 启动发现是由于连接不了ceph mon导致的。
好好利用strace工具,可以解决各种疑难杂症,不过strace不能定位到代码,因此发现问题后,还是需要自己去进一步分析。
如果程序跑的很慢,可以试试 perf 工具,这是一个性能调优工具,能够发现程序的性能瓶颈,定位热点代码,这篇文章 Linux下的系统性能调优工具 写得不错,可以快速入门。找到热点代码后,如果不是非你所望,可能就是程序bug了。
Linux下的系统性能调优工具 中的例子:
void longa()
int i,j;
for(i = 0; i < 1000000; i++)
j=i; //am I silly or crazy? I feel boring and desperate.
void foo2()
int i;
for(i=0 ; i < 10; i++)
longa();
void foo1()
int i;
for(i = 0; i< 100; i++)
longa();
int main(void)