目录
调试和带参调试
进入调试 gdb 【可执行文件】 or
在编译时带 -g:gcc -g -o test test.c
接着运行: 没有参数 run
有参数 run【参数】 or set args 【参数】 然后 run
退出调试:按下Ctrl-D退出
Core Dump调试
- 启用Core Dump
在Linux中,如果程序由于异常或bug突然崩溃,会产生一个叫做core的文件。core文件包含程序运行时的内存,寄存器状态,堆栈指针,内存管理信息以及各种函数调用堆栈信息,它存储了程序工作在当前状态的各种信息,通过gdb工具分析这个文件,分析前后变量,检查程序异常退出对应的堆栈调用等信息,找出问题所在。
首先检查系统有没有禁止core文件的产生
ulimit -c 如果为0表示core dump处于关闭状态
ulimit -c unlimited 开启core dump,不限制core文件大小
ulimit -c 10 #设置最大大小,单位为块,一块默认为512字节
但这种方法只是当前有效,重启后就不再生效。下面介绍永久配置core的方法:
方法1: /etc/profile:找到ulimit -S -c 0 > /dev/null 2>&1,把0改为 unlimited ,保存退出; 然后 执行source /etc/profile 设置生效
若只针对某一用户配置,则修改此用户的~/.bashrc或者~/.bash_profile文件:limit -c unlimited
方法2: /etc/security/limits.conf 按如下信息配置
<domain> <type> <item> <value>
* soft core unlimited
- 调试core文件
gdb 程序文件名 core 进入core调试
调试正在运行的程序
查找进程ID : ps -ef | grep 进程名 or pidof 进程名
在GDB中通过进程ID调试: gdb 进入 然后 (gdb) attach 进程ID
若没有调试信息,在attach前加一句:file hello
如有报错,将/etc/sysctl.d/10-ptrace.conf中
kernel.yama.ptrace_scope = 1 改为 0
直接调试相关id进程:gdb hello 20829 or gdb hello --pid 20829
断点
查看已设置断点:info breakpoints
设置断点:b num 或 b program:num 或 b funcname
条件断点:break test.c:23 if b==0(如果b=0,就在23行停止)
condition 1 b==0(如果b=0,断点1)condition bnum(取消断点条件)
rbreak printNum*(对函数printNum设置断点,调用该函数时都要停下来)
rbreak file:regex (对所有函数设置断点)rbreak . or rbreak test.c:. or rbreak test.c:^print
临时断点:tbreak test.c:l0
ignore 1 30(跳过前面30次)
设置观察点:watch a rwatch当变量值被读时断住,awatch被读或者被改写时断住
禁用断点:
禁用所有断点disable 禁用标号为bnum的断点 disable bnum
启用所有断点enable 启用标号为bnum的断点 enable bnum
启动标号为bnum的断点,并且在此之后删除该断点enable delete bnum
清除断点:
删除当前行所有断点:clear 删除函数处断点clear function
删除文件filename中函数function处的断点 :clear filename:function
删除行号为lineNum处的断点 :clear lineNum
删除文件filename中行号为lineNum处的断点 :clear f:lename:lineNum
删除所有breakpoints,watchpoints和catchpoints :delete
删除断点号为bnum的断点 :delete bnum
变量查看
普通查看: p a 查看变量a的值,p=print
p *d 打印指针内容,但只能打印一个数值;如果不解引用,打印地址
p *d@10 打印指针内容,使用@并指定长度
指定查看: p 'testGdb.h'::a 查看指定函数or文件中的变量
$可表示上一个变量:p *linkNode 显示节点指向的内容 p *$.next 显示下一个节点的内容
set $index=0 设置类unix环境变量的下标 p b[$index++]累加下标访问并显示
格式控制:x 十六进制 d十进制 u 十六进制无符号整型 a 十六进制 o 八进制 t 二进制 c 字符格式 f 浮点格式
p/x c 按十六进制打印
查看内存地址的值:x/[n][f][u] addr
n表示要显示的内存单元数,默认值为1;f 表示要打印的格式;u 要打印的单元长度;addr 内存地址
单元类型:b 字节 h 半字,即双字节 w 字,即四字节 g 八字节
自动显示: display e 程序停止时自动显示e
into display查看哪些变量设置了自动显示
delete display num 清除编号为num的自动显示 disable display num 去使能编号为num的自动显示
查看寄存器:info registers
查看源码
list 简写为l l num 显示num附近的源码
l funcname 显示函数funcname附近的源码
list first,last 显示first到last之间的源码
l test.c:1 l test.c:1,test.c:3 显示指定路径中的源码
设置源码一次列出行数: set listsize 20 show listsize
指定源码目录 : dir ./temp
查看原来的源码路径 :readelf main -p .debug_str
改变源码目录: set substitute-path from to
查看设置的源码路径 :show substitute-path
执行调试
单步执行:next 简写为n n num 单步执行num次
单步进入:step简写为s 单步跟踪到函数内部,但前提是该函数有调试信息并且有源码信息
如果没有函数源码会报错 No such file or directory 可以用finish结束这个步骤
show step-mode 查看step-mode的状态
set step-mode on 打开step-mode,即当遇到没有调试信息的函数,不跳过这个函数
set step-mode off 关闭step-mode,默认为off,没有调试信息就跳过这个函数
si 单步执行一个机器指令
继续执行到下一个断点:-continue 简写为c或fp
继续执行到指定位置:-until 简写为 u
跳过执行: skip skip function xxx skip file xxx info skip
skip delete [num] skip enable [num] skip disable [num]
补充1:产生core的原因分析
(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)多线程读写的数据未加锁保护:
对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成coredump。
(4)非法指针:
a) 使用空指针;
b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump。
(5)堆栈溢出:
不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。
参考:https://blog.csdn.net/tenfyguo/article/details/8159176
补充2:ulimit命令
ulimit命令用来限制系统用户对shell资源的访问。限制 shell 启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。同时,它支持硬资源和软资源的限制。
ulimit -a # 显示当前所有limit信息
-a:显示目前资源限制的设定
-c <core文件上限>:设定core文件的最大值,单位为区块
-d <数据节区大小>:程序数据节区的最大值,单位为KB
-f <文件大小>:shell所能建立的最大文件,单位为区块
-H:设定资源的硬性限制,也就是管理员所设下的限制
-m <内存大小>:指定可使用内存的上限,单位为KB
-n <文件数目>:指定同一时间最多可开启的文件数
-p <缓冲区大小>:指定管道缓冲区的大小,单位512字节
-s <堆叠大小>:指定堆叠的上限,单位为KB
-S:设定资源的弹性限制
-t <CPU时间>:指定CPU使用时间的上限,单位为秒
-u <程序数目>:用户最多可开启的程序数目
-v <虚拟内存大小>:指定可使用的虚拟内存上限,单位为KB