在linux上运行的c/c++程序,一般是没有界面的,一般都需要打印运行日志,通过日志来定位问题。但是日志不是万能的,有时候日志没有输出我们的想要的信息,如果增加日志还要程序重新编译部署发布,如果是生产上的服务,为了排查一个问题就要发布版本,那这肯定是得不偿失的。除了日志,linux上我们还有很多手段去帮助我们排查程序问题,就是linux提供了很多有用的命令行工具让我们查找问题。这里我总结了经常使用的几个命令的用法,gdb, pstack,valgrind。
gbd命令
gdb命令就是我们调试程序用的,其实跟windows上的我们常用的visual studio调试是一样的,只是windows上的调试都是可视化的,直接在IDE上启动程序,然后在文件上打断点调试,用鼠标在变量上面一停就能看到变量的值,很方便。但是在linux下,调试的门槛感觉就高多了,黑洞洞的窗口,让人感觉无从下手。gdb的功能很强大,很多,我只介绍一些比较重要的使用方法。
(1)直接调试
gdb exefile
以上直接使用gdb进行调试,但是如果可执行文件需要启动参数才能正常运行的的话,上面的命令就导致程序执行不正常分支。此时应该按下面的步骤启动
(2)调试带参数的可执行程序
使用命令:gdb –args exefile arg1 arg2
上面的命令就会启动gdb,并给可执行文件设置启动参数,但是gdb启动后没有马上运行exefile,而是等待命令,此时你需要输入start或者run命令,gdb才会运行exefile的代码.程序运行的过程中,你是无法输入gdb命令的,此时你可以使用Ctrl+c,中断运行中的程序,此时你就可以输入gdb命令了,比如打印变量值,设置断点等等
(3)使用gdb查看core文件
gdb 程序名 corefile
使用参数-tui,启动可以直接将屏幕分成两个部分,上面显示源代码,比用list方便多了。这时候使用上下方向键可以查看源代码,想要命令行使用上下键就用[Ctrl]n和[Ctrl]p.
其实查看core文件和实际调试正在运行的程序都差不多,可以查看堆栈信息,可以查看变量的值,因为这些信息都会保存在core文件里了
(4)已经运行的程序,使用gdb attach进入调试
当程序已经处于运行状态,需要使用gdb调试可以使用attach命令:
gdb attach 进程id
attach上去之后就可以调试了
(5)gdb调试多线程
让程序停在某个断点,或者中断执行中的程序,就可以输入gdb命令进行多线程的调试。
1、首先查看线程信息:info threads
下面是输出的信息:
5 Thread 0x2aed315c7700 (LWP 4998) “camera_pro” 0x00002aecebe3156d in nanosleep () from /lib64/libc.so.6
4 Thread 0x2aed317c8700 (LWP 4999) “camera_pro” 0x00002aecec144d42 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
3 Thread 0x2aed319c9700 (LWP 5000) “camera_pro” 0x00002aecebe3156d in nanosleep () from /lib64/libc.so.6
2 Thread 0x2aed31bca700 (LWP 5001) “camera_pro” 0x00002aecebe6b183 in epoll_wait () from /lib64/libc.so.6
* 1 Thread 0x2aecea6b3040 (LWP 4972) “camera_pro” 0x00002aecebe5bc8d in read () from /lib64/libc.so.6
线程id为LWP后面的数字,其中前面的*号表示当前所在线程
2、切换线程: thread Id
3、将程序的执行流锁定在某个线程: set scheduler-locking on/off
4、显示当前的scheduler-locking 状态:show scheduler-locking
5、查看所有线程的堆栈信息:thread apply all bt 这个是终极大招,基本上程序跑到哪里都能清清楚楚的看到,只要你编译的是-d的版本,你还可以查看所有的变量值
pstack命令
pstack命令也和gdb调试很像,可以直接查看堆栈,但是功能远没有gdb那么强大,这个命令只有查看堆栈信息这个功能。但是我们有时候只需要堆栈信息。
查看某个进程的堆栈调用信息,直接后面跟进程id就可以
Usage: pstack <process-id>
例如:
[root@localhost ~]# ps -ef | grep redis
root 1363 1 0 11月10 ? 00:00:40 redis-server *:6379
root 26454 25948 0 15:12 pts/0 00:00:00 grep –color=auto redis
[root@localhost ~]# pstack 1363
Thread 3 (Thread 0x7fed01fff700 (LWP 1366)):
#0 0x00007fed09431995 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1 0x00000000004504d6 in bioProcessBackgroundJobs (arg=0x0) at bio.c:164
#2 0x00007fed0942de25 in start_thread () from /lib64/libpthread.so.0
#3 0x00007fed09157bad in clone () from /lib64/libc.so.6
Thread 2 (Thread 0x7fed017fe700 (LWP 1367)):
#0 0x00007fed09431995 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1 0x00000000004504d6 in bioProcessBackgroundJobs (arg=0x1) at bio.c:164
#2 0x00007fed0942de25 in start_thread () from /lib64/libpthread.so.0
#3 0x00007fed09157bad in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7fed09d5b780 (LWP 1363)):
#0 0x00007fed09158183 in epoll_wait () from /lib64/libc.so.6
#1 0x0000000000418d73 in aeApiPoll (tvp=<optimized out>, eventLoop=0x7fed020581a0) at ae_epoll.c:114
#2 aeProcessEvents (eventLoop=eventLoop@entry=0x7fed020581a0, flags=flags@entry=3) at ae.c:400
#3 0x000000000041919b in aeMain (eventLoop=0x7fed020581a0) at ae.c:455
#4 0x0000000000417ef7 in main (argc=<optimized out>, argv=0x7fff5dd1bc08) at redis.c:3382
valgrind命令
valgrind也是一个很强大的工具,用来查找内存泄漏和程序崩溃问题。
(1)安装valgrind程序
可以用源码安装
:
wget https://fossies.org/linux/misc/valgrind-3.15.0.tar.bz2
tar -jxvf valgrind-3.15.0.tar.bz2
cd valgrind-3.15.0
./configure
make
sudo make install
centos下也可以直接使用yum命令直接安装
:
yum install valgrind
(2)准备需要调试的程序
程序在编译的时候一般需要使用 -O0不进行优化-g带调试信息,这样才能看到具体是代码中的哪一行有内存泄漏
(3)valgrind的使用
调用 Valgrind 的通用格式是:valgrind [valgrind-options] your-prog [your-progoptions]
一般检查内存泄漏的都需要加–leak-check=full参数,例如:
valgrind –leak-check=full –tool=memcheck ./vg
valgrind是在程序执行结束后才打印输出信息,
输出信息样例如
下:
[root@localhost gittest]# valgrind –leak-check=full ./vg
==2447== Memcheck, a memory error detector
==2447== Copyright (C) 2002-2017, and GNU GPL’d, by Julian Seward et al.
==2447== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==2447== Command: ./vg
==2447==
finish
6
==2447==
==2447== HEAP SUMMARY:
==2447== in use at exit: 4 bytes in 1 blocks
==2447== total heap usage: 4 allocs, 3 frees, 72,746 bytes allocated
==2447==
==2447== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2447== at 0x4C29F73: malloc (vg_replace_malloc.c:309)
==2447== by 0x400B63: fun() (valgrindtest.cpp:16)
==2447== by 0x400B81: main (valgrindtest.cpp:22)
==2447==
==2447== LEAK SUMMARY:
==2447== definitely lost: 4 bytes in 1 blocks
==2447== indirectly lost: 0 bytes in 0 blocks
==2447== possibly lost: 0 bytes in 0 blocks
==2447== still reachable: 0 bytes in 0 blocks
==2447== suppressed: 0 bytes in 0 blocks
==2447==
==2447== For lists of detected and suppressed errors, rerun with: -s
==2447== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
其中by 0x400B63: fun() (valgrindtest.cpp:16)这句就是指明明确的内存泄漏代码所在的行号
valgrind也可以检测内存越界的问题
使用未初始化的内存也可以检测出来
使用类似strncp()这样的函数,导致内存重叠也可以检测出来
使用–log-file=filepath 来指定valgrind的输出信息:valgrind –leak-check=full –log-file=./vg.log ./vg
搜索“definitely lost”寻找确定内存泄漏的地方