目录
- 一、gdb功能简介
- 三、gdb最常见的几个用法:
- 2.调试正在运行的程序:
- 五、程序中断机制:监视点(watchpoint)、断点(breakpoint)和捕捉点(catchpoint):
- 1. 监视点。
- 2. 断点
- 六,调试core文件
- 1, 造成segment fault,产生core dump的可能原因
- 2, 配置操作系统使其产生core文件
- 3,调试core
- 七、跟踪点(tracepoint):
- 八、检查点(checkpoint):
@
一、gdb功能简介
GDB主要帮忙你完成下面四个方面的功能:
- 1.启动你的程序,可以按照你的定制要求随心所欲的运行程序。
- 2.可让被调试的程序在你所指定的调置的断点处停住。
- 3.当程序被停住时,可以检查此时你的程序中所发生的事,以及内存状态等。
- 4.动态的改变你程序的执行环境。
## 二、gdb使用前置条件:编译时加入debug信息。
gcc/g++是在编译时加入-g(注意 -g 参数要在 -o 之前,否则可能没有调试信息)
-g分4个等级:
- -g0等于不加-g。即不包含任何信息
- -g1只包含最小信息,一般来说只有你不需要debug,只需要backtrace信息,并且真的很在意程序大小,或者有其他保密/特殊需求时才会使用-g1。
- –g2为gdb默认等级,包含绝大多数你需要的信息。
- –g3包含一些额外信息,例如包含宏定义信息。当你需要调试宏定义时,请使用-g3
三、gdb最常见的几个用法:
### 1.gdb的启动,加载程序:
1) gdb ${你的程序} 进入gdb后,输入run(简写r) ${arg1} ${arg2} … ${argN}
2) gdb --args ${你的程序} ${arg1} ${arg2} … ${argN} 进入gdb后,运行run。
3) gdb进入gdb后,输入file ${你的程序}。然后使用set args ${arg1} ${arg2} … ${argN} 设定好你的程序参数,再运行run。
2.调试正在运行的程序:
- gdb ${你的程序} ${程序pid}
3. 查core:
- gdb ${你的程序} ${core文件}
四、gdb常用命令:
backtrace
:显示栈信息。简写为bt。frame x
切换到第x帧。其中x会在bt命令中显示,从0开始。0表示栈顶。简写为f。up/down x
往栈顶/栈底移动x帧。当不输入x时,默认为1。print x
打印x的信息,x可以是变量,也可以是对象或者数组。简写为p。print */&x
打印x的内容/地址。call
调用函数。注意此命令需要一个正在运行的程序。set substitute-path from_path to_path
,替换源码文件路径。当编译机与运行程序的机器代码路径不同时,需要使用该指令替换代码路径,否则你无法在gdb中看到源码。break x.cpp:n
在x.cpp的第n行设置断点,然后gdb会给出断点编号m。命令可简写为b。后面会对break命令进行更详细的解释。- command m 设置程序执行到断点m时要看的内容,例如:如果command后面没有参数n,则命令被赋给最后一个breakpoint,这其实是说break和command连在一起用,在脚本里用就非常方便了。
command n
>printf "x is %d\n",x
>c
>end
x /nfu ${addr}
打印addr的内容。addr可以是任何合法的地址表达式,例如0x562fb3d,一个当前有效的指针变量p,或者一个当前有效的变量var的地址&var。nfu是格式,n表示查看的长度,F表示格式(例如16进制或10进制),U表示单位(例如单字节b,双字h,四字w等)。举个栗子:
(gdb) x /3xw 0x562fb3d //这个指令的意思为:以16进制格式显示地址0x562fb3d处3个单位,每个单位四字节的内容。你将得到下列数值:
0x562fb3d: 0x00282ff4 0x080484e0 0x00000000
continue
继续运行程序到下一个断点,或结束。可简写为cuntil
执行到当前循环完成。可简写为ustep
单步调试,步入当前函数。可简写为snext
单步调试,步过当前函数。可简写为nfinish
执行到当前函数返回set var x=10
改变当前变量x的值。也可以这样用:set {int}0x83040 = 10把内存地址0x83040的值强制转换为int并赋值为10info locals
打印当前栈帧的本地变量jump
使当前执行的程序跳转到某一行,或者跳转到某个地址。由于只会使程序跳转而不会改变栈值,因此若跳出函数到另外的地方 会导致return出错。另外,熟悉汇编的人都知道,程序运行时,有一个寄存器用于保存当前代码所在的内存地址。所以,jump命令也就是改变了这个寄存器中的值。于是,你可以使用“set $pc”来更改跳转执行的地址。如: set $pc = 0x485return
: 强制函数返回。可以指定返回值
五、程序中断机制:监视点(watchpoint)、断点(breakpoint)和捕捉点(catchpoint):
1. 监视点。
监视点是监视内存中某个地址,当该地址的数据被改变(或者被读取)时,程序交出控制权进入调试器。注意监视点分为软件模式和硬件模式:GDB 使用软件监视点的方式是在单步执行你的程序的同时测试变量的值,所以执行程序的速度会变慢。同时,软件监视点仅在当前线程有效。幸运的是,32 位的 Intel x86 处理器提供了 4 个特殊的调试寄存器用来方便调试程序,GDB 可以使用这些寄存器建立硬件监视点。GDB 总是会优先使用硬件监视点,因为这样不会减慢程序的执行速度。然而,可用的(enable的)硬件监视点的个数是有限的。如果你设置了过多的硬件监视点,当程序从中断的状态变为执行的状态(例如continue,until或者finish)时,GDB 可能无法把它们全部激活。另外,活动的硬件监视点的数量只有在试图继续执行程序时才能知道,也就是说,即使你设置了过多的硬件监视点,gdb在你运行程序之前也不会警告你。
设置监视点的命令有3个,watch(写监视),rwatch(读监视)以及awatch(读写监视)。他们的使用方法一样,皆为以下几种:
1) (r/a)watch x
。x是一个变量名。当x的值改变/被读取时,程序交出控制权进入调试器。
2) (r/a)watch 0xN
。N为一个有效地址。当该地址的内容变化/被读取时,程序交出控制权进入调试器。
3) (r/a)watch *(int *)0xN
。N为一个有效地址。当该地址的中的int指针指向的内容变化/被读取时,程序交出控制权进入调试器。
4) (r/a)watch -l *(int *)0xN
。N为一个有效地址。当该地址的中的int指针指向的内容变化/被读取,或者该地址的内容变化/被读取时,程序交出控制权进入调试器。
注意3)和4)的区别在于,当加入-l选项后,会同时监视表达式本身以及表达式指向的内容。
2. 断点
是指当执行到程序某一步时,程序交出控制权进入调试器。值得注意的是,break会有一些变体:tbreak,hbreak,thbreak与rbreak。tbreak与break功能相同,只是所设置的断点在触发一次后自动删除。hbreak是一个硬件断点。thbreak则既是一个临时的硬件断点。注意硬件断点需要硬件支持,某些硬件可能不支持这种类型的断点。rbreak稍微特殊一些,它会在匹配正则表达式的全部位置加上断点,后面会有详细讲解。除去rbreak,其他break家族的使用方法如下:
查看断点: info break
, 简写info b
添加断点
break 可以缩写为 b
1) break xxx.cpp:y
。在文件 xxx.cpp 的第 y 行加入断点。
不指定文件时候,则会以当前执行的文件作为断点文件。
若程序未执行,则以包含main函数的源代码文件作为断点文件。
若x.cpp和y都不指定,则以当前debugger的点作为断点处。
2) break x.cpp:func
。在x.cpp的func函数入口处加入断点。x.cpp可以不提供直接使用break func。注意由于重载(overload)的存在,因此gdb可能会询问你希望在哪个函数加上断点。
3) break 0xN
。在地址N处加入断点。N必须为一个有效的代码段(code segment)地址。
4) 删除多个断点 del 1-10
保存加载断点
1, save break file.bp
保存断点到 file.bp
2, source file.bp
加载断点(so file.bp)
六,调试core文件
core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIGSEGV信号,然后就会core dump)
1, 造成segment fault,产生core dump的可能原因
1.内存访问越界
a) 由于使用错误的下标,导致数组访问越界
b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符
c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。
2 多线程程序使用了线程不安全的函数。
3 多线程读写的数据未加锁保护。对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump
4 非法指针
a) 使用空指针
b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型 的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它 时就很容易因为bus error而core dump.
5 堆栈溢出.不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。
2, 配置操作系统使其产生core文件
首先通过ulimit命 令查看一下系统是否配置支持了dump core的功能。通过ulimit -c或ulimit -a,可以查看core file大小的配置情况,如果为0,则表示系统关闭了dump core。可以通过ulimit -c unlimited来打开。若发生了段错误,但没有core dump,是由于系统禁止core文件的生成。
解决方法:
$ulimit -c unlimited (只对当前shell进程有效)
或在~/.bashrc 的最后加入: ulimit -c unlimited (一劳永逸)
3,调试core
gdb [exec file] [core file]
如: gdb ./test test.core
可以产生core的c语言语句: *(char *)0 = 0;
控制core文件保存位置和文件名格式
修改文件命令: echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
或者:
sysctl -w kernel.core_pattern=/corefile/core-%e-%p-%t kernel.core_pattern = /corefile/core-%e-%p-%t
可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
以下是参数列表:
%p - insert pid into filename 添加pid(进程id)
%u - insert current uid into filename 添加当前uid(用户id)
%g - insert current gid into filename 添加当前gid(用户组id)
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加导致产生core的命令名
七、跟踪点(tracepoint):
跟踪点与上面三个断点不同之处在于,它只是跟踪记录信息而不会中断程序的运行。当你的程序是realtime程序,或者与其他的程序有交互时,你可能会希望使用跟踪点达到监视程序而又不破坏程序自身行为的目的。与断点相同的是,跟踪点会保存下在跟踪点时的一些内存信息供使用者查阅,例如数组或者对象。
另外,tracepoints可以通过save命令保存,以方便使用者下次再次进入程序调试时不需要重设这些跟踪点。
八、检查点(checkpoint):
gdb可以保存某一个时间点的程序状态或者说是程序映像,并且稍后又可以返回到这个状态。这个称之为checkpoint。
每个检查点是进程的一个拷贝。这样当一个bug很难重现,而又担心调试过头了又要从头开始重现时,可以在估计要重现这个bug之前,做一个checkpoint,这样即使debug过头了,也可以从这个checkpoint开始,而不用重启整个程序并且期待它重现这个bug(也许需要很久!!)。
但是每个checkpoint有一个唯一的进程id,这个pid与原始程序的pid不同,因此如果程序需要使用pid的信息时,需要慎重考虑。
https://www.cnblogs.com/xsln/p/gdb_instructions1.html
原文地址:https://www.cnblogs.com/ims-/p/10529393.html