gdb+gdbserver远程调试

gdb+gdbserver远程调试

一. gdb+gdbserver交叉编译

1.源码下载

https://ftp.gnu.org/gnu/gdb/

2.编译gdb

$ sudo apt install texinfo #安装依赖

$ tar zxvf ./gdb-9.2.tar.gz
$ cd gdb-9.2
$ mkdir build
$ cd build/
$ /home/pz/tool/gdb-9.2/configure --target=aarch64-linux-gnu
$ make

gdb 运行在虚拟机上,所以它不需要交叉编译。–build 和 --host 参数留空,实际使用的是虚拟机的平台参数。gdb 虽运行在虚拟机上,但它处理的是开发板平台的程序,所以指定 --target 为 aarch64-linux-gnu,值取的是交叉编译工具链前缀。

3.交叉编译gdbserver

$ cd gdb-9.2/gdb/gdbserver

$ ./configure --prefix=/home/zf/tool/build/gdb --host=aarch64-linux-gnu LD=aarch64-linux-gnu-ld CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++

$ sudo make -j8
$ sudo make install

gdbserver 运行在开发板上,所以需要交叉编译。

二. 远程使用gdbserver

1、目标开发板上输入:gdbserver [设备ip]:[端口] [需要运行的程序]

$ gdbserver 192.168.1.0:8000 test

设备ip:可忽略不写;

端口:用于远程连接的端口,默认为2345,这里使用8000;

需要运行的程序:该程序必须在编译时打开-g选项,编译成可调试版本,例子为test;

2、PC端/虚拟机

$ cd tool/gdb-9.2/build/gdb
$ ./gdb
$ target remote 192.168.1.0:8000 

1.attach调试运行中程序

1、通过top等命令获取程序的pid。

2、设备上输入:gdbserver [设备ip]:[端口] --attach [运行中程序的pid]

$ gdbserver 192.168.1.0:8000 --attach pid

3、PC端执行gdb,输入:target remote [目标设备ip]:[端口]

$ cd tool/gdb-9.2/build/gdb
$ ./gdb
$ target remote 192.168.1.0:8000 

2. 使用core文件进行调试

2.1. core文件的生成

​ 使用ulimit -c unlimited命令解除core文件的大小限制是生成core文件的前提,程序出现异常退出时,会自动在程序的当前路径下生成core文件;

​ 当程序没有出现异常退出时想要生成core文件,可以通过向程序发送SEGSEGV信号 (kill -11 [pid])使程序退出并生成core文件;

2.2. core文件在pc上调试

1、将core文件、库文件(设备上的/usr/lib文件夹)与对应的应用程序test转移到pc上,放在与gdb可执行文件的上一级。

2、使用gdb执行应用程序

$ ./gdb test

3、使用:core-file [core文件路径] 读取core文件

$ core-file ../core	#会报错:找不到库
$ set solib-search-path ../lib #加载库
$ core-file ../core #再次执行core文件

三. gdb部分指令

1. 启动程序:r/run、start

根据不同场景的需要,GDB 调试器提供了多种方式来启动目标程序,其中最常用的就是 r/run 指令,其次为 start 指令。也就是说,run 和 start 指令都可以用来在 GDB 调试器中启动程序。

run与start的区别:
默认情况下,run 指令会一直执行程序,直到执行结束。如果程序中手动设置有断点,则 run 指令会执行程序至第一个断点处;
start 指令会执行程序至 main() 主函数的起始位置,即在 main() 函数的第一行语句处停止执行(该行代码尚未执行)。

2. 继续运行程序:c/continue

运行程序直到程序结束或遇到断点,使用方式:

$ c/continue [n]

如果从断点处使用c命令,可以使用参数n

n:表示忽略该断点n-1次,直到第n次到该断点处程序才会停下

3. 运行程序到指定位置:u/until

运行程序到指定位置后停下,相当于在指定位置打临时断点然后运行到断点处,使用起来比临时断点方便

使用方式:

$ u/until [行号或函数地址]

4. 单步执行:n/next、s/step

单步调试代码,其最大的特点是当遇到包含调用函数的语句时,无论函数内部包含多少行代码,next 指令都会一步执行完。也就是说,对于调用的函数来说,next 命令只会将其视作一行代码。

$ next [count]

参数 count 表示单步执行多少行代码,默认为 1 行。

step 命令和 next 命令的功能相同,都是单步执行程序。不同之处在于,当 step 命令所执行的代码行中包含函数时,会进入该函数内部,并在函数第一行代码处停止执行。

step [count]

参数 count 表示一次执行的行数,默认为 1 行。

5. 结束函数:finish、return

finish命令:执行函数到正常退出该函数

finish

return命令:立即结束执行当前函数并返回,可指定返回值

return [...]

注:使用return时,即使当前函数还有剩余的代码未执行完毕,也不会执行了

6. 设置断点b/break命令

b/break命令用于设置断点,当程序运行到断点所在位置且满足设置的条件时,程序将在断点处停止执行

break 命令(可以用 b 代替)常用的语法格式有以下 2 种。

break[location][thread threadnum] [if cond]

location:用于指定打断点的具体位置,其表示方式有多种,如下表所示。

location 的值含义
linenumlinenum 是一个整数,表示要打断点处代码的行号。
filename:linenumfilename 表示源程序文件名;linenum 为整数,表示具体行数。
+ offset - offsetoffset 为行数偏移值,整数
functionfunction 表示程序中包含的函数的函数名
filename:functionfilename 表示远程文件名;function 表示程序中函数的函数名。

thread threadnu:指定断点的触发线程,其中thread为线程编号

$ b HighAccuracyTimeStamp(void*) thread 1
$ info b #查看断点详情 

if cond:指定断点的触发条件,其中cond为某个表达式。

$ b 280 if index == 10 

注:设置断点后,程序会运行到达当前帧中大于指定位置的源行,即在第10行设置断点,程序在运行完第10行的代码处停止

7. 删除断点:delete、clear

delete 命令(可以缩写为 d )通常用来删除所有断点:

delete [断点编号]	#不指定断点编号时删除全部断点

clear 命令可以删除指定位置处的所有断点:

clear [location]

参数 location 通常为某一行代码的行号或者某个具体的函数名。当 location 参数为某个函数的函数名时,表示删除位于该函数入口处的所有断点。

8. 设置观察断点:watch

watch 命令的功能是:当被监控变量(表达式)的值发生改变,程序才会停止运行

$ watch [cond]

#其中,cond 指的就是要监控的变量或表达式。 

#注:1、若局部变量(表达式)失效,则监控操作也随即失效
#	2、如果监控的是一个指针变量(例如 *p),则 watch *p 和 watch p 是有区别的,前者监控的是 p 所指数据的变化情况,而后者监控的是 p 指针本身有没有改变指向
#	3、watch可以用于监控数组中元素值的变化情况,例如对于 a[10] 这个数组,watch a 表示只要 a 数组中存储的数据发生改变,程序就会停止执行

和 watch 命令功能相似的,还有 rwatchawatch 命令。其中:

rwatch 命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;

awatch命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。

9. 信号处理:handle

handle 命令的语法格式如下:

$ handle [signal] [mode]

signal :要设定的目标信号,它通常为某个信号的全名(SIGINT)或者简称(去除‘SIG’后的部分,如 INT);如果要指定所有信号,可以用 all 表示。

mode :明确 GDB 处理该目标信息的方式:

nostop:当信号发生时,GDB 不会暂停程序,其可以继续执行,但会打印出一条提示信息,告诉我们信号已经发生;

stop:当信号发生时,GDB 会暂停程序执行。

noprint:当信号发生时,GDB 不会打印出任何提示信息;

print:当信号发生时,GDB 会打印出必要的提示信息;

nopass(或者 ignore):GDB 捕获目标信号的同时,不允许程序自行处理该信号;

pass(或者 noignore):GDB 调试在捕获目标信号的同时,也允许程序自动处理该信号。

由于在接收到信号后,gdb的默认处理方式会使程序暂停,因此在项目中进行gdb调试时,先输入:

$ handle SIGUSR1 SIGUSR2 SIG44 SIG45 noprint nostop
$ info signal [信号名] #查看当前gdb对该信号的处理方式,不加信号名默认全部

使程序能流畅执行,不被频繁打断。若需要在某一个信号的信号处理函数处暂停,可自行修改此命令。

10. 查看堆栈信息:bt

$ bt [full] [n]
$ bt full #显示bt命令显示的内容外,还会显示每一层栈内变量的值

full:打印局部变量的值

n:一个整数值,当为正整数时,表示打印最里层的 n 个栈帧的信息;n 为负整数时,那么表示打印最外层 n 个栈帧的信息

11. 跳转到某一层栈:f

$ f [栈帧的编号]	#跳转到栈后才可以查看该栈帧内的局部变量 

12. 各线程状态:info threads

1 带有*的线程为gdb调试的当前线程,默认会在1号线程

2 gdb调试的线程编号

3 linux系统中的线程id

4 线程当前执行函数

5 线程当前执行的位置(文件、行号)

线程处于__GI___clock_nanosleep说明该线程正在sleep 。

线程处于__lll_lock_wait 说明线程正在等锁。

13. 跳转到某一个线程:thread

$ thread [线程编号]

使用thread命令跳转线程时使用的线程编号是gdb调试时的线程编号而不是系统的pid。

跳转到线程后可以使用bt命令查看当前线程的调用栈

14. 打印表达式

1. p/print命令

使用:p[/输出类型] [变量名/表达式]

/x 以十六进制的形式打印出整数。

/d 以有符号、十进制的形式打印出整数。

/u 以无符号、十进制的形式打印出整数。

/o 以八进制的形式打印出整数。

/t 以二进制的形式打印出整数。

/f 以浮点数的形式打印变量或表达式的值。

/c 以字符形式打印变量或表达式的值。

$ p 0xfec0
$ p/x 66667
$ p/d (short)(0xfec0) 
$ p [结构体名] #可以直接得到结构体内的所有值 
$ p *(pthread_mutex_t*)[锁地址] #可以看到锁的持有者 例如 __owner = 29263 说明当前锁的持有者的pid为29263
2. 查看内存

x命令用于读取内存地址中的值,语法:

# x/<n/f/u> <addr>
$ x/10xb 0xffffaa7fb790

n:显示内存的长度,也就是说从当前地址向后显示几个数字(数字的长度由u参数决定)的内容。

f:表示显示的格式。如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i。

x 按十六进制格式显示变量

d 按十进制格式显示变量

u 按十六进制格式显示无符号整型

o 按八进制格式显示变量

t 按二进制格式显示变量

a 按十六进制格式地址显示变量

c 按字符格式显示变量

f 按浮点数格式显示变量

u:表示从当前地址往后请求的字节数,指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来,如果不指定的话,GDB默认是4个bytes。

b表示单字节

h表示双字节

w表示四字节

g表示八字节

addr:内存地址

3. display命令

display 相当于自动输出的 x 或 print。display 会在每次暂停的时候输出表达式的值。display 会根据指定的模式自动选择使用 x 或是 print:如果使用i或s格式, 会使用 x 输出值,其他情况下使用 print。

display命令的使用方式与print类似:

$ display[/输出类型] [变量名/表达式] #输出类型参考x命令 
$ info display #查看当前设置的显示内容
$ undisplay [变量或表达式的编号]	#删除不需要打印显示的变量或表达式
$ delete display [变量或表达式的编号] #删除不需要打印显示的变量或表达式
$ enable/disable display [变量或表达式的编号]  #以激活/禁用变量或表达式的打印

15. 其它

反汇编:disassemble
强制调用函数:print、call
显示源码:l/list

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值