gdb调试命令

来源:互联网 发布:教育大数据 编辑:程序博客网 时间:2024/06/05 05:52

gdb常用命令

gcc -g hello.cgdb ./a.outl           // list,显示函数start       // 开始逮捕调试,显示即将运行的行n           // next,运行到下一行,到函数时不会进入函数s           // step,运行到下一行,到函数时进入函数p a         // 显示函数内a的值bt          // 查看函数堆栈,当前栈序号为0,main栈序号为1f 1         // 切换到main的栈p a         // 显示main中a的值p &a        // 打印a的地址q           // 退出调试

gdb实践

测试代码:

#include <stdio.h>  int main()  {          int *p = NULL;          *p = 0;          printf("bad\n");          return 0;  }  

编译运行,会发生Segmentation fault (core dumped) 错误,使用gdb调试:

[taoge@localhost test]$ gcc -g main.c   [taoge@localhost test]$ ./a.out   Segmentation fault (core dumped)  [taoge@localhost test]$ gdb a.out  (gdb) r  Starting program: /home/taoge/test/a.out   Program received signal SIGSEGV, Segmentation fault.  0x080483c9 in main () at main.c:6  6               *p = 0;  

可以看到, 在gdb调试的时候, 用r命令让程序再跑起来, 可以定位到问题出在第6行。 我们再来看看bt命令:

(gdb) bt  #0  0x080483c9 in main () at main.c:6  (gdb)    

可以看到,通过bt查看函数堆栈, 也能定位到第6行。

我们知道,在实际中, 有很多问题是概率发生的, 很难重现。 此时, 如果用gdb的r命令(实际相当于重新运行程序)则是不可能的。 所以,对于概率性问题, 如果程序出现堆栈错误时, 产生了core文件, 我们一定要视之为宝贝, 记得保存, 否则很可能被冲掉。 拿到core文件后, 我们可以用gdb调试core。

gdb调试Core Dump

1,什么是Core Dump?

Core的意思是内存, Dump的意思是扔出来, 堆出来.
开发和使用Unix程序时, 有时程序莫名其妙的down了, 却没有任何的提示(有时候会提示core dumped). 这时候可以查看一下有没有形如core.进程号的文件生成, 这个文件便是操作系统把程序down掉时的内存内容扔出来生成的, 它可以做为调试程序的参考.
core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump.

2,怎么生成Core Dump?

在linux平台下,设置core dump文件生成的方法:

1) 在终端中输入ulimit -c 如果结果为0,说明当程序崩溃时,系统并不能生成core dump。

2) 使用ulimit -c unlimited命令,开启core dump功能,并且不限制生成core dump文件的大小。如果需要限制,加数字限制即可:ulimit - c 1024,如果生成的信息超过此大小,将会被裁剪,最终生成一个不完整的core文件。在调试此core文件的时候,gdb会提示错误。

永久设置, 修改/etc/security/limits.conf文件:

soft    core            0修改成:soft    core            unlimited

3) 默认情况下,core dump生成的文件名为core,而且就在程序当前目录下。新的core会覆盖已存在的core。通过修改/proc/sys/kernel/core_uses_pid文件,可以将进程的pid作为作为扩展名,生成的core文件格式为core.xxx,其中xxx即为pid。为0则表示生成的core文件统一命名为core。可通过以下命令修改此文件:

echo "1" > /proc/sys/kernel/core_uses_pid

注意 proc 是内存文件,使用vi 是不能 编译的,所有只能使用append 的方式

/proc 这个目录是虚拟在内存中的,不在硬盘保存,
proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。用户和应用程序可以通过proc得到系统的信息,并可以改变内核的某些参数。由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的。

4) 通过修改/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件格式。例如:

# 将所有core文件生成到/corefile下,格式为core-命令名-pid-时间戳.echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern# 以下是参数列表:%%  单个%字%p  dump进程的进程ID%u  dump进程的用户ID%g  dump进程的组ID%s  导致core dump的信号%t  core dump的时间%h  主机名%e  程序文件名

永久设置, 修改/etc/sysctl.conf配置文件,添加:

kernel.core_pattern = /corefile/core-%e-%p-%tkernel.core_uses_pid = 1 #即使core_pattern中没有设置%p,最后生成的core dump文件名仍会加上进程ID/* 执行以下命令立即生效 */# sysctl -p

3,如何使用core文件?

gdb [exec file] [core file]

在编译程序的时候加入选项-g,下面我们可以在发生运行时信号引起的错误时发生core dump了。发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行。比如:

gdb ./a.out corefile/core-a.out-3219-1512819153 

在进入gdb后, 用bt命令查看backtrace以检查发生程序运行到哪里, 来定位core dump的文件->行.
进去后输入where回车, 也可以显示程序在哪一行down掉的, 在哪个函数中.

如果不知道coredump对应的执行文件,可以使用以下方式查找:

gdb -c corefile  # 使用gdb调试core文件info auxv        # 索引31对应的是core文件的应用程序exec_filefind / -name exec_file

命令小记:

1. GDB进入正在运行的进程    gdb 可执行文件 core文件     gdb -p pidGDB提示符1. 查看调用栈信息    bt / backtrace / bt full    frame n    info locals    info args2. 查看、设置变量    p 变量    p 变量 = 新值    set 变量 = 新值3. 查看内存    x/<n/f/u> <addr>4. 线程调试    info thread    thread n

core dump原因分析

造成程序core dump的原因很多,这里根据以往的经验总结一下:

1 内存访问越界

a) 由于使用错误的下标,导致数组访问越界
b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符
c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。

2 多线程程序使用了线程不安全的函数。

应该使用下面这些可重入的函数,尤其注意红色标示出来的函数,它们很容易被用错:
asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n) ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c) getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c) fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c) getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3) getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n) nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3) getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c) getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c) getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)

3 多线程读写的数据未加锁保护。

对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump

4 非法指针

a) 使用空指针
b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump.

5 堆栈溢出

不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。

原创粉丝点击