linux C/C++ 后端服务问题排查(gdb, pstack,valgrind)

  • Post author:
  • Post category:linux


在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”寻找确定内存泄漏的地方



版权声明:本文为hacker_lpy原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。