通俗易懂说GDB调试(二)进阶

通俗易懂说GDB调试(一)
https://blog.csdn.net/lqy971966/article/details/88963635
通俗易懂说GDB调试(二)
https://blog.csdn.net/lqy971966/article/details/102812016
通俗易懂说GDB调试(三)总结
https://blog.csdn.net/lqy971966/article/details/103118094

gdb是一个在UNIX环境下的命令行调试工具。

1. bt 查看栈信息

bt 打印当前的函数调用栈的所有信息

2. finish 结束调试-跳出某函数

2.1 finish 结束这个函数的调试,回到调用函数

如果想跳过某函数里面的执行,可使用finish命令,跳出该函数。
让程序执行到当前函数返回之后停止,用finish

3. 函数名补充

(gdb) b test_ <按TAB键>
( 再按下一次TAB键, 你会看到:以 test 开头的全部函数)

4. until 直接跳到某一行/跳出循环体

4.1 until 直接跳到某一行,忽略前面的行

假如我们在25行停住了,现在想要运行到29行停住,就可以使用until命令
u 29
例子:

(gdb) u 659
XXXXXPolicy (pData=0xbf92c434, uiDataLen=68, iSocketFd=148)
	at xxf/app/xxx_yyy_policy.c:659
	659     in xxf/xxx/xxx_yyy_policy.c
(gdb) n
	662     in xxf/xxx/xxx_yyy_policy.c
(gdb) u 672

4.2 退出循环体 如for while等(也可以使用finish)

在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体,可简写为u

(gdb)
963     in xx/yyy_fun.c
(gdb) u
983     inxx/yyy_fun.c
(gdb)

5. display 设置自动显示的信息

跟踪查看某个变量,每次停下来都显示它的值

5.1 display 断住就显示变量的值

  1. 设置跟踪点:
    (gdb) display iIndex
    2: iIndex = 4
    那么每次程序断住时,就会打印e的值

  2. 查看哪些变量被设置了display,可以使用:
    (gdb) info display
    Auto-display expressions now in effect:
    Num Enb Expression
    2: y iIndex
    1: y iIndex
    这里设置了两次

  3. 删除跟踪点
    delete + display 编号  delete 3  
    用于删除一个要显示值的表达式,被删除的表达式将不被显示

  4. 暂时失效/使能
    disable/enable + display 编号  disable/enable 3  使一个要显示值的表达式暂时失效/使能

6. Watch 设置观察点,监控变量值的变化(只要观察点的值有变化就断住)

观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,如果有变化,马上停住程序。

代码例子

#include <stdio.h>

int main()
{
	int iLoop = 0;
	int iRet = 0;
	for(; iLoop < 3; iLoop++ )
	{
		iRet += iLoop;
	}
	
	printf("ret = %d\n",iRet);
	
	return 0;
}

watch 调试实例:

[root@localhost gdb]# gcc -g watch.c 
[root@localhost gdb]# gdb a.out 
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-110.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/gdb/a.out...done.
(gdb) l
1       #include <stdio.h>
2        
3       int main()
4       {
5           int iLoop = 0;
6               int iRet = 0;
7           for(; iLoop < 3; iLoop++ )
8               {
9               iRet += iLoop;
10              }
(gdb) b 6
Breakpoint 1 at 0x40052c: file watch.c, line 6.
(gdb) r
Starting program: /home/gdb/a.out 

Breakpoint 1, main () at watch.c:6
6               int iRet = 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.x86_64
(gdb) watch iRet		// 1. watch 监控变量 iRet
Hardware watchpoint 2: iRet
(gdb) c					// 2. 运行 查看变化
Continuing.
Hardware watchpoint 2: iRet	// 3. 可以看到变化 

Old value = 0		
New value = 1
main () at watch.c:7
7           for(; iLoop < 3; iLoop++ )
(gdb) bt				// 4. 可以查看堆栈 这里简单就一条
#0  main () at watch.c:7
(gdb)

7. 调试core文件

  1. 命令limit -c查看 系统没有限制core文件的产生,如果结果是0,那么即便程序core dump了也不会有core文件留下
    修改 $ ulimit -c unlimied #表示不限制core文件大小
    2.调试core文件也很简单:

    gdb a.out xxx.core或者 gdb /sbin/xxd abc.core

7.1 总结:段错误没有生成core文件处理及自测(VIP)

参考:通俗易懂说GDB调试(三)总结
https://blog.csdn.net/lqy971966/article/details/103118094

步骤1:设置core文件生成无限制
[root@H3C flash:]# 
[root@H3C flash:]# ulimit -c
unlimited
[root@H3C flash:]#

步骤2:设置core文件路径
echo "/var/core-%e-%p-%t" >  /proc/sys/kernel/core_pattern 
cat /proc/sys/kernel/core_pattern  是否正确

步骤3:自测是否生成core文件
测试是否正确的命令
kill -s SIGSEGV $$

例子:

[root@H3C flash:]# ulimit -c
unlimited
[root@H3C flash:]#
root@H3C flash:]# echo "/var/core-%e-%p-%t" > /proc/sys/kernel/core_pattern 
[root@H3C flash:]# cat /proc/sys/kernel/core_pattern
/var/core-%e-%p-%t
[root@H3C flash:]#
[root@H3C flash:]# kill -s SIGSEGV $$
Segmentation fault (core dumped)
sh-4.2$ su
Password: 
[root@H3C flash:]# ls -l /var/core-*
-rw------- 1 root root 794624 Apr 28 08:40 /var/core-bash-16387-1651135248
[root@H3C flash:]#

8. attach 调试已运行程序

  1. 首先使用ps命令找到进程id:
    $ ps -ef|grep 进程名
  2. attach 方式

例子 gdb att 902

  1. 1.# ps | grep xxxd
    902 1 309m S /sbin/xxxxd --YYYYYY=7
    940 0 1104 R grep xxxd
  2. 2# gdb att 902
    即可

其中:

9. tbreak 设置临时断点

假设某处的断点只想生效一次,那么可以设置临时断点,这样断点后面就不复存在了:
tbreak test.c:l0 #在第10行设置临时断点

10. ignore 跳过多次设置断点

假如有某个地方,我们知道可能出错,但是前面5次都没有问题,虽然在该处设置了断点,但是想跳过前面30次,可以使用下面的方式:
ignore 5 5 //在断点5 处设置 跳过前面5次循环

10.1 ignore 例子:

/* iIndex 没有初始化 默认随机值
for 循环有7次 我跳过前面5次 */

int iIndex;
(gdb) watch iIndex
Hardware watchpoint 5: iIndex
(gdb) p iIndex
$2 = -1233534152
(gdb) info b
Num     Type           Disp Enb Address    What
…… ……
5       hw watchpoint  keep y              iIndex
(gdb) ignore 5 5
Will ignore next 5 crossings of breakpoint 5.
(gdb) c
Continuing.
Hardware watchpoint 5: iIndex

Old value = 4
New value = 5
0x08099c37 in XXX_FUNC (pcxxx=0xb6789f88 "/tmpxxx")
	at xxx/xxx/xxxfun.c:963
963     in xx/xxx/dxxx_fun.c
(gdb)

11. disable/enable 临时禁用或启用所有断点

有些断点暂时不想使用,但又不想删除,可以暂时禁用或启用。例如:

disable #禁用所有断点
disable bnum #禁用标号为bnum的断点
enable #启用所有断点
enable bnum #启用标号为bnum的断点
enable delete bnum #启动标号为bnum的断点,并且在此之后删除该断点

12. clear/d 断点清除

断点清除主要用到clear和delete命令。常见使用如下:

clear #删除当前行所有breakpoints
clear function #删除函数名为function处的断点
clear filename:function #删除文件filename中函数function处的断点
clear lineNum #删除行号为lineNum处的断点
clear f:lename:lineNum #删除文件filename中行号为lineNum处的断点
delete #删除所有breakpoints,watchpoints和catchpoints
delete bnum #删除断点号为bnum的断点

13. skip 跳过执行某些函数

skip可以在step时跳过一些不想关注的函数或者某个文件的代码
skip function add //step时跳过add函数
skip file gdbStep.c //跳过这个文件,这样gdbStep.c中的函数都不会进入

14. info args 打印入参

15. info local 局部变量等

打印全局和局部变量: info variables

16. return 强制函数返回

可以使用return命令强制函数忽略还没有执行的语句并返回。

(gdb) return -1
Make DXXX_YYYock return now? (y or n) y

17. info registers 查看寄存器值

源码:

XX_ISP_ITEM_S* XX_ISP_Add(IN const CHAR *pcISPName, IN XX_ISP_OPERTYPE_E enOperType)
{
	BOOL_T bUserSet = BOOL_FALSE;
	XX_ISP_ITEM_S *pstISPItem = NULL;

	pstISPItem = XX_ISP_Find(pcISPName);
	if (NULL != pstISPItem)
	{
		……
	}
	……
	
}

反汇编源码:

(gdb) disassemble XX_ISP_Add
Dump of assembler code for function XX_ISP_Add:
0x0000000000494820 <+0>:     nopw   0x0(%rax,%rax,1)
0x0000000000494826 <+6>:     push   %rbp
0x0000000000494827 <+7>:     mov    %rsp,%rbp
0x000000000049482a <+10>:    sub    $0x20,%rsp
0x000000000049482e <+14>:    mov    %rdi,-0x18(%rbp)
0x0000000000494832 <+18>:    mov    %esi,-0x1c(%rbp)
0x0000000000494835 <+21>:    movw   $0x0,-0x2(%rbp)
0x000000000049483b <+27>:    movq   $0x0,-0x10(%rbp)
=> 0x0000000000494843 <+35>:    mov    -0x18(%rbp),%rax

查看寄存器内容:

(gdb) info registers 
rax            0x7ffe8f6c71a8   140731304669608
rbx            0x7ffe8f6ca438   140731304682552
rcx            0xa      10
rdx            0xffffffffffe978b7       -1476425	
rsi            0x2      2						//第二个参数 是值 2 
rdi            0x7ffe8f6c71a8   140731304669608	//第一个参数 是地址 值是字符串 1234
rbp            0x7ffe8f6c7110   0x7ffe8f6c7110
rsp            0x7ffe8f6c70f0   0x7ffe8f6c70f0
r8             0x2000000        33554432
r9             0x68     104
……

说明:

 x86-64 参数传递:%rdi, %rsi, %rdx, %rcx, %r8, %r9 用作函数参数,
 	依次对应第1个参数,第2个参数 …

参考:
寄存器(1)寄存器概念,x86寄存器种类说明及汇编代码详解
https://blog.csdn.net/lqy971966/article/details/106780755
打印值

(gdb) p (char*)0x7ffe8f6c71a8
$7 = 0x7ffe8f6c71a8 "1234"
(gdb)
(gdb) x /8c 0x7ffe8f6c71a8  
0x7ffe8f6c71a8: 49 '1'  50 '2'  51 '3'  52 '4'  0 '\000'        0 '\000'        0 '\000'        0 '\000'
(gdb)

18. call命令来执行函数:call V_GetCurrentVID() 调用函数

19. x 查看内存

用gdb查看内存

19.1 格式: x /nfu

x 是 examine 的缩写

19.2 参数说明:

1. n 表示要显示的内存单元的个数

2. f 表示显示方式, 可取如下值
	x 按十六进制格式显示变量。
	d 按十进制格式显示变量。
	u 按十进制格式显示无符号整型。
	o 按八进制格式显示变量。
	t 按二进制格式显示变量。
	a 按十六进制格式显示变量。
	i 指令地址格式
	c 按字符格式显示变量。
	f 按浮点数格式显示变量。
	
3. u 表示一个地址单元的长度
	b表示单字节,
	h表示双字节,
	w表示四字节,
	g表示八字节

19.3 例子说明

例子1:

(gdb) p (char*)0x7ffe8f6c71a8
$7 = 0x7ffe8f6c71a8 "1234"
(gdb)
(gdb) x /8c 0x7ffe8f6c71a8  //打印8个字符内容
0x7ffe8f6c71a8: 49 '1'  50 '2'  51 '3'  52 '4'  0 '\000'        0 '\000'        0 '\000'        0 '\000'
(gdb)

(gdb) x /32c 0x7ffe8f6c71a8 
0x7ffe8f6c71a8: 100 'd' 102 'f' 102 'f' 0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'
0x7ffe8f6c71b0: 0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'
0x7ffe8f6c71b8: 0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'
0x7ffe8f6c71c0: 0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'        0 '\000'
(gdb)

例子2:

//定义
typedef struct stTest{
	……
	UINT            uiID;/*ID */
	UINT            uiLoadId;
	UINT            uiWhoisSum;
	UINT            uiFileWhoisSum;
	……
}

//赋值
stTest pstItem;
……
pstItem->uiID = XX_INVALID_INSTANCE_ID;	//(0xffffffff)
pstItem->uiLoadId = XX_FILELOAD_MAX;	//3
pstItem->uiWhoisSum = 0;
pstItem->uiFileWhoisSum = 2;

//看内存
(gdb) x /32x &pstItem->uiID  //打印32个内存单元,按照16进制显示
  //内存地址升高b0	    b1		b2		b3		b4		b5		b6		b7
0xfc62b0:       0xff    0xff    0xff    0xff    0x03    0x00    0x00    0x00
0xfc62b8:       0x00    0x00    0x00    0x00    0x02    0x00    0x00    0x00
0xfc62c0:       0x32    0x32    0x32    0x33    0x00    0x00    0x00    0x00
0xfc62c8:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
(gdb)

由此可以看出:uiLoadId 的值3,0x00 00 00 03低字节存放在低地址处,是小端序

参考:
gdb中x的用法.
https://blog.csdn.net/baidu_24256693/article/details/47298513

20. info variables 查询当前全局变量信息

(gdb) info variables 
All defined variables:

File xx_common.h:
static const unsigned char g_aucxx_token[256];
static const unsigned char g_aucxxoken[256];
static const unsigned char g_aucxxuffixIsToken[256];

File bb_addr.c:
struct tagLB_ADDR_
……

21. set print pretty 结构体打印的漂亮,整洁好看

打开printf pretty这个选项,那么当GDB显示结构体时会比较漂亮

(gdb) set print pretty
8185    in xx_yy.c
(gdb) p *pstXXItem
$2 = {
stNameHashNode = {
	pstNext = 0x0, 
	ppstPre = 0x16f5c78
}, 
……
uiXXID = 4294967295, 
uiFileWhoisSum = 0, 
stIspCfg = {
	szName = "dff", '\000' <repeats 60 times>, 

22. set follow-fork-mode [parent|child] 多进程中调试主进程还是子进程

背景:
若调试进程会fork一个子进程来处理,因此,在对函数设置断点之前,需要执行如下命令

set follow-fork-mode child
/* 表示让程序调用fork函数之后,调试子进程,父进程可以继续运行 */
  1. 默认设置下,在调试多进程程序时GDB只会调试主进程
  2. 使用follow-fork-mode,只能调试一个进程,不能同时调试父子进程

23. (gdb) help xx //help帮助信息

例子:

(gdb) help set follow-fork-mode 
Set debugger response to a program call of fork or vfork.
A fork or vfork creates a new process.  follow-fork-mode can be:
parent  - the original process is debugged after a fork
child   - the new process is debugged after a fork
The unfollowed process will continue to run.
By default, the debugger will follow the parent process.
(gdb)

24. info symbol addr 打印存储在地址addr中的符号名称

info symbol 内存地址" 可以获取内存地址所在的 symbol 相关信息

(gdb) help info symbol
Describe what symbol is at location ADDR.
Only for symbols with fixed locations (global or static scope).
(gdb)
(gdb) info symbol 0x000000000049955d
_xxx_ProcCreatexxx + 77 in section .text of /usr/sbin/lxx
(gdb)

25. 获取帮助信息的两个有用的命令:info 和 help all

看例子:
(gdb) info

(gdb) info    
"info" must be followed by the name of an info command.
List of info subcommands:

info address -- Describe where symbol SYM is stored
info all-registers -- List of all registers and their contents
info args -- Argument variables of current stack frame
…… ……

(gdb) help all

(gdb) help all

Command class: aliases

ni -- Step one instruction
rc -- Continue program being debugged but run it in reverse
rni -- Step backward one instruction
rsi -- Step backward exactly one instruction
si -- Step one instruction exactly
stepping -- Specify single-stepping behavior at a tracepoint
tp -- Set a tracepoint at specified line or function
tty -- Set terminal for future runs of program being debugged
where -- Print backtrace of all stack frames
ws -- Specify single-stepping behavior at a tracepoint

Command class: breakpoints

awatch -- Set a watchpoint for an expression
break -- Set breakpoint at specified line or function
break-range -- Set a breakpoint for an address range
catch -- Set catchpoints to catch events
…… ……

26.

参考

  1. https://www.jb51.net/article/36393.htm
  2. https://cloud.tencent.com/developer/article/1469554
  3. https://www.2cto.com/kf/201303/195119.html
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值