GDB 调试笔记


笔记来源:GDB 中文调试手册

1.使用方式

1.编译的命令中加上 -g 选项

gcc -o hello hello.c -g
g++ -o hello hello.cpp -g

2.GDB的启动方式

  • gdb filename, 其中filename为要调试的目标文件

  • gdb filename core, 当文件运行产生core dump时,可以同时调试程序和core文件,一般需执行ulimit -c unlimited将core 文件设置为无限大。

  • gdb filename pid,指定运行程序的进程ID, 并调试它。

GDB调试已经运行的程序
在这里插入图片描述
常用参数:
在这里插入图片描述

2.GDB命令补全

gdb 的命令很多,gdb 把之分成许多个种类。help 命令只是例出 gdb 的命令
种类,如果要看种类中的命令,可以使用 help 命令,如:help breakpoints,
查看设置断点的所有命令。也可以直接 help 来查看命令的帮助。
在 Linux 下,你可 以敲击两次 TAB 键来补齐命令的全称,如果有重复的,那么 gdb 会把其例出来。
在这里插入图片描述

3.GDB中运行shell程序

在这里插入图片描述

如:

(gdb)shell ls 

使用run时可能需要设置的命令
在这里插入图片描述

3.暂停 / 恢复程序的运行

在 gdb 中,我们可以有以下几种暂停方式:断点(BreakPoint)、观察点
(WatchPoint)、捕捉点(CatchPoint)、信号(Signals)、线程停止(Thread Stops)。
如果要恢复程序运行,可以使用 c 或是 continue 命令

1.设置断点


	break <class::function(type,type)>
	break<linenum>
	break filename:linenum
	break filename:function
	break *addr(在某个内存地址处设置断点)
	break [line / function] if <cond>(如 break if i=100,表示循环中i = 100时停住)

查看断点信息:

info break [n] (n表示断点号)

2.设置观察点
观察点一般来观察某个表达式(变量也是一种表达式)的值是否有变化了,
如果有变化,马上停住程序。watchpoint只能在程序启动后设置。

watch <expr> 为表达式(变量)expr 设置一个观察点。一量表达式值有变化时,马上停住程序。
rwatch <expr> 当表达式(变量)expr 被读时,停住程序。
awatch <expr>  当表达式(变量)的值被读或被写时,停住程序。
info watchpoints  列出当前所设置了的所有观察点。

3.设置捕捉点
捕捉点用来补捉程序运行时的一些事件。如:载入共享库(动态链接 库)或是 C++的异常。
在这里插入图片描述

4.维护停止点
可以使用 delete、clear、 disable、enable来对上述的停止点进行维护。

clear

 清除函数上所有的停止点。
clear <function> 
clear <filename:function>
清除所有设置在指定行上的停止点。
clear <linenum> 
clear <filename:linenum>

delete

delete [breakpoints] [range...]
 删除指定的断点,breakpoints 为断点号。如果不指定断点号,则表示删 除所有的断点。range 表示断点号的范围(如:3-7)。

disable
让相应的停止点失效,当需要时可以使用enable再次启用

disable [breakpoints] [range...]
  disable 所指定的停止点,breakpoints 为停止点号。如果什么都不指定,
  表示 disable 所有的停止点。简写命令是 dis.

enable

enable [breakpoints] [range...] 
  enable 所指定的停止点,breakpoints 为停止点号。
enable [breakpoints] once range... 
  enable 所指定的停止点一次,当程序停止后,该停止点马上被 GDB 自动disable。
enable [breakpoints] delete range... 
   enable 所指定的停止点一次,当程序停止后,该停止点马上被 GDB 自动删除。

修改条件断点

condition <bnum> <expression> 
 修改断点号为 bnum 的停止条件为 expression。 
 condition <bnum> 
 清除断点号为 bnum 的停止条件。
 ignore <bnum> <count> 
 表示忽略断点号为 bnum 的停止条件 count 次。

停止点设置运行命令

commands [bnum] 
... command-list ... 
end
为断点号 bnum 指写一个命令列表。当程序被该断点停住时,gdb 会依次运行命令列表中的命令。

在这里插入图片描述
断点菜单
在 C++中,可能会重复出现同一个名字的函数若干次(函数重载),在这种
情况下,break 不能告诉 GDB 要停在哪个函数的入口。当然,你可以
使用 break <function(type)>也就是把函数的参数类型告诉 GDB,以指定一个函
数。否则的话,GDB 会给你列出一个断点菜单供你选择你所需要的断点。你只
要输入你菜单列表中的编号就可以了。

继续执行和单步调试
在这里插入图片描述

单步跟踪

step <count>
如果有函数调用,他会进入该函数
后面可以加 count 也可
以不加,不加表示一条条地执行,加表示执行后面的 count 条指令,然后再停住.

Finsh

Finsh
 运行程序,直到当前函数完成返回。并打印函数返回时的堆栈地址和返
回值及参数值等信息。

until 或 u

until 或 u 
当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退 出循环体。

4. GDB处理信号

可以要求 GDB 收到你所指定的信号时,马上停住正在运行的程
序,以供你进行调试。

handle <signal> <keywords...>

信号< signal >可以以 SIG 开头或不以 SIG 开 头,可以用定义一个要处理信号的范围(如:SIGIO-SIGKILL,表示处理从 SIGIO
信号到 SIGKILL 的信号,其中包括 SIGIO,SIGIOT,SIGKILL 三个信号),也
可以使用关键字 all 来标明要处理所有的信号。
< keywords >可以是以下几种关键字
的一个或多个。

Nostop 
 当被调试的程序收到信号时,GDB 不会停住程序的运行,但会打出消息告 诉你收到这种信号。 
Stop
 当被调试的程序收到信号时,GDB 会停住你的程序。 
 print 
 当被调试的程序收到信号时,GDB 会显示出一条信息。 
 noprint 
 当被调试的程序收到信号时,GDB 不会告诉你收到信号的信息。
pass 
noignore 
当被调试的程序收到信号时,GDB 不处理信号。这表示,GDB 会把这个信号交给被调试程序会处理。
 nopass 
ignore 
当被调试的程序收到信号时,GDB 不会让被调试程序来处理这个信号。 info signals 
info handle 
 查看有哪些信号在被 GDB 检测中。

5.线程断点

在线程上进行调试:

break <linespec> thread <threadno> 
break <linespec> thread <threadno> if ...

linespec 指定了断点设置在的源程序的行号。threadno 指定了线程的 ID,注意, 这个 ID 是 GDB 分配的,你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定 thread < threadno >则表示你的断点设在所有线程上面。
你还可以为某线程指定断点条件。当你的程序被 GDB 停住时,所有的运行线程都会被停住。这方便你你查看运
行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕 是主进程在被单步调试时。

thread num 
切换线程,num为gdb分配的线程号

6.查看栈的信息

当我们调用函数后我们一般会查看栈的信息,GDB查看栈的信息如下:

backtrace 或者 bt
打印当前调用栈的信息

backtrace <n>或者 bt<n>
 n 是一个正整数,表示只打印栈顶上 n 层的栈信息。

 backtrace <-n>  或者 bt <-n> 
-n 表一个负整数,表示只打印栈底下 n 层的栈信息。

frame <n>  或者 f <n> 
n 是一个从 0 开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。

up <n> 
表示向栈的上面移动 n 层,可以不打 n,表示向上移动一层。 down <n> 
表示向栈的下面移动 n 层,可以不打 n,表示向下移动一层。

f 或者 frame
栈的层编号,当前的函数名,函数参数值,函数所在文
件及行号,函数执行到的语句。

info f
这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时内存地址。

info args
打印出当前函数的参数名及其值。

info locals 
打印出当前函数中所有局部变量及其值。

info catch 
打印出当前的函数中的异常处理信息。

7 .查看源程序

显示源代码

list <linenum> 
 显示程序第 linenum 行的周围的源程序。
list <function> 
 显示函数名为 function 的函数的源程序。
list 
 显示当前行后面的源程序。
list – 
 显示当前行前面的源程序。

set listsize <count> 
 设置一次显示源代码的行数。
 show listsize 
 查看当前 listsize 的设置。


list <first>, <last> 
 显示从 first 行到 last 行之间的源代码。
list , <last> 
 显示从当前行到 last 行之间的源代码。
list +
 往后显示源代码。

 一般来说在 list 后面可以跟以下这们的参数: 
 <linenum> 行号。
<+offset> 当前行号的正偏移量。
<-offset> 当前行号的负偏移量。
<filename:linenum> 哪个文件的哪一行。 
<function> 函数名。 
<filename:function> 哪个文件中的哪个函数。
<*address> 程序运行时的语句在内存中的地址。

搜索源程序

forward-search <regexp> 
 向前搜素 
 search <regexp> 
 向后面搜索。
reverse-search <regexp> 
 全部搜索。
 其中,<regexp>就是正则表达式

8.查看运行时的数据

查看变量

print 命令的格式是:
print < expr >
print / < expr >
< expr >是表达式,是你所调试的程序的语言的表达式(GDB 可以调试多 种编程语言),< f >是输出的格式,比如,如果要把表达式按 16 进制的格式输出, 那么就是/x。

如果当前停止点在函数中,用 print 显示出的变量的值会是函数中的局部
变量的值。如果此时你想查看全局变量的值时,你可以使用“::”操作符:

file::variable   一般文件名为字符,’filename‘
查看全局变量
function::variable
查看局部变量

查看数组

对于数组我们需要使用@来显示动态分配的数组的长度和首地址,才能将数组所有的数据打印出来。
如下的代码:

	int *arr = (int *)malloc(sizeof(int)*len);
	for(int i = 0; i < len; i++ ){
		arr[i] = i;
	}

如果我们想要打印arr中的所有的数据,我们应该这样使用。

(gdb) p *arr@len

如果是静态数组,我们只需要直接

p arr

输出格式:

可以通过GDB来控制数据的输出格式,如控制变量的进制。

x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十六进制格式显示无符号整型。 o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
(gdb) p i 
$21 = 101 
(gdb) p/a i 
$22 = 0x65 
(gdb) p/c i 
$23 = 101 'e' 
(gdb) p/f i 
$24 = 1.41531145e-43 
(gdb) p/x i 
$25 = 0x65 
(gdb) p/t i 
$26 = 1100101

9.自动显示

当我们的程序停住或者我们进行但不调试时。我们可以采用display命令来来进行自动显示。

display <expr> 
display/<fmt> <expr> 
display/<fmt> <addr>
expr 是一个表达式,fmt 表示显示的格式,addr 表示内存地址,
当你用 display 设定好了一个或多个表达式后,只要你的程序被停下来,
GDB 会自动显 示你所设置的这些表达式的值。

可以用 info display 来进行查看display的信息

删除自动显示

undisplay <dnums...> 
delete display <dnums...>
dnums 意为所设置好了的自动显式的编号
删除自动显示,dnums 意为所设置好了的自动显式的编号。
如果要同时删 除几个,编号可以用空格分隔,
如果要删除一个范围内的编号,可以用减号表示 (如:2-5)

disable display <dnums...> 
enable display <dnums...>
disable 和 enalbe 不删除自动显示的设置,而只是让其失效和恢复。

显示选项设置

set print array off
设置后数组将会每个元素打印一行
set print array 
set print array on
set print array off 
show print array
打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的
话,每个元素则以逗号分隔。


set print elements <number-of-elements>
这个选项主要是设置数组的,如果你的数组太大了,那么就可以指定一
个<number-of-elements>来指定数据显示的最大长度,当到达这个长度时,GDB
就不再往下显示了。

set print pretty on
set print pretty off
如果打开 printf pretty 这个选项,那么当 GDB 显示结构体时会比较漂亮。

set print union <on/off>
设置显示结构体时,是否显式其内的联合体数据。

set print object < on/off >
show print object
在 C++中,如果一个对象指针指向其派生类,如果打开这个选项,GDB
会自动按照虚方法调用的规则显示输出,如果关闭这个选项的话,GDB 就不管 虚函数表了。

set print static-members <on/off>
show print static-members
这个选项表示,当显示一个 C++对象中的内容是,是否显示其中的静态
数据成员。默认是 on。

set print vtbl <on/off>
show print vtbl
当此选项打开时,GDB 将用比较规整的格式来显示虚函数表时。其默认是 关闭的。

历史记录
当你用 GDB 的 print 查看程序运行时的数据时,你每一个 print 都会被 GDB
记录下来。GDB 会以$1, $2, $3 …这样的方式为你每一个 print 命令编上号。于
是,你可以使用这个编号访问以前的表达式,如$1。这个功能所带来的好处是,
如果你先前输入了一个比较长的表达式,如果你还想查看这个表达式的值,你可 以使用历史记录来访问,省去了重复输入。

10.改变程序的执行

修改变量的值
修改程序运行过程中的值又两种方式,一种方式是采用print方式,另一种凡方式便是采用set var xxx= xxx 的方式进行。

(gdb) print x=4
(gdb) set var width=47

跳转执行

jump <linespec>
指定下一条语句的运行点。<linespce>可以是文件的行号,可以是 file:line
格式,可以是+num 这种偏移量格式。

jump 命令不会改变当前的程序栈中的内容,所以,当你从一个函数
跳到另一个函数时,当函数运行完返回时进行弹栈操作时必然会发生错误,可能 结果还是非常奇怪的,甚至于产生程序 Core Dump。所以最好是同一个函数中进 行跳转.

强制函数返回和调用

return 
return <expression> 
使用 return 命令取消当前函数的执行, 并立即返回,如果指定了
< expression >,那么该表达式的值会被认作函数的返回值。

call <expr> 
 表达式中可以一是函数,以此达到强制调用函数的目的。并显示函数的
返回值,如果函数返回值是 void,那么就不显示。

11. 多进程

两种方式:一种是先运行程序然后使用 attach + pid 方式进行调试,另一种方式则是通过命令 set follow-fork-mode child / parent 命令选择调试子进程或者父进程。
在这里插入图片描述

12. 多线程

❑info threads,显示当前可调试的所有线程。gdb会为每个线程分配一个ID,我们可以使用这个ID来操作对应的线程。ID前面有 “ * ” 号的线程是当前被调试的线程。

❑thread ID,调试目标ID指定的线程。

❑set scheduler-locking[off|on|step]。调试多线程程序时,默认除了被调试的线程在执行外,其他线程也在继续执行,但有的时候我们希望只让被调试的线程运行。这可以通过这个命令来实现。该命令设置 scheduler-locking 的值:off 表示不锁定任何线程,即所有线程都可以继续执行,这是默认值;on表示只有当前被调试的线程会继续执行;step表示在单步执行的时候,只有当前线程会执行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值