首页 热点资讯 义务教育 高等教育 出国留学 考研考公
您的当前位置:首页正文

GDB调试器使用总结

2023-11-08 来源:花图问答

    (3).转储文件的压缩:通过在/etc/sysctl.conf文件的core_pattern中加入压缩脚本以及管道命令,可以对生成的转储文件进行压缩。首先在/etc/sysctl.conf文件中加入下列两行(如果已经存在则修改成下面的形式):        kernel.core_pattern = |/usr/localsbin/zipsh %t %e %p        kernel.core_uses_pid = 0    保存文件,然后执行:>sysctl -p命令。    /usr/local/sbin/zipsh文件的内容如下:        #!/bin/sh        exec gzip - > /var/core/$1-$2-$3.core.gz    这样的话,以后都会在/var/core下生成压缩的转储文件。1.基本信息查看:    (1).栈信息:不管是操作转储文件还是用GDB设置断点进行调试,都可以输入(GDB)bt打印栈内容进行查看。一般的当机BUG,看下当机的位置,然后看下源代码基本就可以解决了。但是很多情况下简单的(GDB)bt还查不到问题,这时候就要涉及到比较复杂的操作。下面罗列了一些对栈的操作:        (GDB) bt:显示所有栈帧。        (GDB) bt 10:显示前面10个栈帧。        (GDB) bt -10:显示后面10个栈帧。        (GDB) bt full:显示栈帧以及局部变量。        (GDB) bt full 10:显示前面10个栈帧以及局部变量。        (GDB) bt full -10:显示后面10个栈帧以及局部变量。        (GDB) frame <栈帧编号>:进入指定的栈帧中,然后可以查看当前栈帧中的局部变量,以及栈帧内容等信息。        (GDB) info frame <栈帧编号>:可以查看指定栈帧的详细信息。        (GDB) up:进入上层栈帧。        (GDB) down:进入下层栈帧。     (2).变量:调试BUG过程中查看变量信息是很有帮助的操作,查看方式如下:        (GDB) p <变量名>     (3).寄存器:对于调试来说寄存器中的值也很重要,可以查看到当前正在执行的指令的地址等。具体操作罗列如下:           (GDB) info reg:显示所有寄存器。可以简写为:i r。如果要查看具体的寄存器可以这样:i $ebx        (GDB) p $eax:显示eax寄存器内容。        (GDB) p/c $eax:用字符显示eax寄存器内容,反斜杠后面的是显示格式,可使用的格式见下表:该表在显示内存内容的x命令中也是通用的。

格式说明
x显示为十六进制数
d显示为十进制数
u显示为无符号十进制数
o显示为八进制数
t显示为二进制数
a显示为地址
c显示为字符(ASCII)
f显示为浮点小数
s显示为字符串
i显示为机器语言(仅在显示内存的x命令中可用)
    (4).内存:可以查看具体内存地址中的内容,比如:目前执行的汇编指令,以及栈中内容等。        (GDB) x $pc:显示程序指针指向位置的内容。        (GDB) x/i $pc:显示程序当前位置的汇编指令。        (GDB) x/10i $pc:显示程序当前位置开始往后的10条汇编指令。        (GDB) disassem $pc:反汇编当前函数。简写为:disas $pc。   2.调试:    (1).断点:调试程序中,设置断点进行调试是最方便有效的手段,因此学会如果灵活设置断点是调试的基本功。:        A.设置断点:            (GDB) break <函数名>:对当前正在执行的文件中的指定函数设置断点。可简写为:(GDB) b <函数名>            (GDB) break <行号>:对当前正在执行的文件中的特定行设置断点。可简写为:(GDB) b <行号>            (GDB) break <文件名:行号>:对指定文件的指定行设置断点。最常用的设置断点方式。可简写为:(GDB) b <文件名:行号>            (GDB) break <文件名:函数名>:对指定文件的指定函数设置断点。C++类中的方法似乎不好使。可简写为:(GDB) b <文件名:函数名>            (GDB) break <+/-偏移量>:当前指令行+/-偏移量出设置断点。可简写为:b <+/-偏移量>            (GDB) break <*地址>:指定地址处设置断点。可简写为:b <*地址>        B.查看、删除断点:                        (GDB) info break :显示所有断点以及监视点。可简写为:(GDB) i b            (GDB) delete <编号>:删除编号指向的断点或者监视点。可简写为:(GDB) d <编号>            (GDB) clear <行号>:删除改行的断点。            (GDB) clear <文件名:行号>:删除改行的断点。        C.设置无效、有效断点:            (GDB) disable <断点编号> : 当前断点设置为无效。            (GDB) enable <断点编号>:当前断点设置为有效。                (2).监视点:可以监视某个变量,在变量被访问或者被修改时程序会在当前点进入断点。删除,查看监视点的方式与断点相同。设置监视点方式如下:                    (GDB) watch <表达式>:表达式发生变化时暂停。        (GDB) awatch <表达式>:表达式访问或者改变时暂停。        (GDB) rwatch <表达式>:表达式被访问时暂停。     (3).条件断点:在调试程序过程中,有时候我们只想在某个条件下停止程序,然后进行单步调试,而条件断点就是为此而设计。下面是条件断点的操作方式:            (GDB) b <断点> if <条件表达式> : 例如:b main.cpp:8 if x=10 && y=10        (GDB) condition <断点编号>:删除该断点的条件。        (GDB) condition <断点编号> <条件表达式>:修改断点条件。例如:condition 1 x=10 && y=10            (4).断点命令:每次断点发生时候,想要查看的变量很多时,如果每个变量都手动print则需要浪费很多时间。断点命令可以在断点发生时批量执行GDB命令。下面是断点命令的设置方式:        (GDB) commands <断点编号>        (GDB) >print x        (GDB) >print y        (GDB) >end        首先输入GDB命令commands <断点编号>然后回车,这时候会出现>提示符。出现>提示符后可以输入断点发生时需要执行的GDB命令,每行一条,全部输入完成后输入end结束断点命令。            (5).反复执行:单步执行时如果进入了你不关心的函数,你想立即跳出函数;或者进入了大循环中,你想立即循环。下面的命令可以帮到你:            (GDB) ignore <断点编号> <次数>:忽略N次断点。        (GDB) c N: 执行N次指令,会忽略断点。        (GDB) s/stepi/n/nexti N:往后执行N行,不会忽略断点。        (GDB) finish:执行完当前函数后停止,不会忽略断点。        (GDB) until:执行完当前循环后停止,不会忽略断点。        (GDB) until <地址>:执行到指定地址停止。            (6).设置变量值:对变量的值进行控制,可以更快的调试自己的程序。下面就是设置变量值的方法:         (GDB) set variable <变量> = <表达式>:将变量的值设定为指定表达式的值。例如 set variable x=10     (7).手动生成转储文件:        (GDB) generate-core-file 简写为:(GDB) gcore 3.调试在线进程:    (1).启动GDB时链接目标进程:在启动GDB的时候,通过参数-p指定目标进程,就可以进入调试状态。刚链接成功后,程序是暂停运行状态,你可以进行设置断点等操作,然后输入(GDB) c 命令继续运行。命令如下:        >gdb -p <PID>:PID是进程ID,可以通过>ps aux | grep <程序名> 获得。或者直接 >gdb -p  `pidof <程序名>`也可以。>pidof <程序名>是通过名字获取进程ID的命令。     (2).GDB中链接目标进程:        (GDB) attach <PID>     (3).断开链接:        (GDB) detach 4.调试多线程程序:    (1).查看线程:        (GDB) info thread:查看所有线程信息,可简写为:i thr     (2).切换到指定线程:         (GDB) thread <线程编号>:选中出现问题的线程,可简写为:thr <线程编号>     (3).调试守护者进程:守护者进程在启动好子进程后,会自动关闭主进程,如果没有设定监控模式的话,GDB会提示断开与进程的链接。所以必须设定监控对象,设置方式如下:        (GDB) set follow-fork-mode child/parent

 

GDB调试器使用总结

标签:

小编还为您整理了以下内容,可能对您也有帮助:

linuxgdb调试教程linuxgdb调试

gdbserver和gdb如何通信?

通过linux虚拟机里的gdb,来向开发板里的gdbserver发送命令,比如设置断点,运行setp等,然后开发板上的gdbserver收到命令后,便会执行应用程序做相应的动作,来实现调试的功能

什么是GDB?

GDB是GNU开源组织发布的一个强大的Unix/Linux下的程序调试工具

gdb作用是:

1、启动用户程序后,可以按照用户的要求随意运行程序

2、可让被调试的程序在用户所设定的断点处停住

3、当程序被停住时,可以检查此时用户程序中所发生的事。

4、可动态改变用户程序的执行环境。

gdb是什么意思?

GDB是什么

GDB全称“GNUsymbolicdebugger”,从名称上不难看出,它诞生于GNU计划(同时诞生的还有GCC、Emacs等),是Linux下常用的程序调试器。发展至今,GDB已经迭代了诸多个版本,当下的GDB支持调试多种编程语言编写的程序,包括C、C++、Go、Objective-C、OpenCL、Ada等。实际场景中,GDB更常用来调试C和C++程序。

gcc,gdb,mingw,cygwin,的区别是什么,有什么联系。简单点写就可以了?

gcc是linux下的编译器,gdb是linux下的调试工具,mingw是qt下的编译器,cwgwin是在windows下的交叉编译器

GDB调试无法加载源码?

编译的时候用-g的参数,初始的时候只能看到main所在的源码用l打开其它源码文件后在用b加断点

怎样用GDB调试一个由脚本文件启动的程序

使用GDB

一般来说GDB主要调试的是C/C++的程序。要调试C/C++的程序,首先在编译时,我们必须要把调试信息加到可执行文件中。使用编译器(cc/gcc/g++)的 -g 参数可以做到这一点。如:

$gcc -g -Wall hello.c -o hello

$g++ -g -Wall hello.cpp -o hello

如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。当你用-g把调试信息加入之后,并成功编译目标代码以后,让我们来看看如何用gdb来调试他。

启动GDB的方法有以下几种:

gdb <program>

program也就是你的执行文件,一般在当前目录下。

gdb <program> core

用gdb同时调试一个运行程序和core文件,core是程序非法执行后core mp后产生的文件。

gdb <program> <PID>

如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。

以上三种都是进入gdb环境和加载被调试程序同时进行的。也可以先进入gdb环境,在加载被调试程序,方法如下:

*在终端输入:gdb

*在gdb环境中:file <program>

这两步等价于:gdb <program>

GDB启动时,可以加上一些GDB的启动开关,详细的开关可以用gdb -help查看。我在下面只例举一些比较常用的参数:

-symbols <file>

-s <file>

从指定文件中读取符号表。

-se file

从指定文件中读取符号表信息,并把他用在可执行文件中。

-core <file>

-c <file>

调试时core mp的core文件。

-directory <directory>

-d <directory>

加入一个源文件的搜索路径。默认搜索路径是环境变量中PATH所定义的路径。

如何使用gdb调试多进程

GDB 是 linux 系统上常用的 c/c++ 调试工具,功能十分强大。对于较为复杂的系统,比如多进程系统,如何使用 GDB 调试呢?考虑下面这个三进程系统:

进程

进程

Proc2 是 Proc1 的子进程,Proc3 又是 Proc2 的子进程。如何使用 GDB 调试 proc2 或者 proc3 呢?

实际上,GDB 没有对多进程程序调试提供直接支持。例如,使用GDB调试某个进程,如果该进程fork了子进程,GDB会继续调试该进程,子进程会不受干扰地运行下去。如果你事先在子进程代码里设定了断点,子进程会收到SIGTRAP信号并终止。那么该如何调试子进程呢?其实我们可以利用GDB的特点或者其他一些辅助手段来达到目的。此外,GDB 也在较新内核上加入一些多进程调试支持。

接下来我们详细介绍几种方法,分别是 follow-fork-mode 方法,attach 子进程方法和 GDB wrapper 方法。

follow-fork-mode

在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试。

follow-fork-mode的用法为:

set follow-fork-mode [parent|child]

parent: fork之后继续调试父进程,子进程不受影响。

child: fork之后调试子进程,父进程不受影响。

因此如果需要调试子进程,在启动gdb后:

(gdb) set follow-fork-mode child

并在子进程代码设置断点。

此外还有detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:

set detach-on-fork [on|off]

on: 断开调试follow-fork-mode指定的进程。

off: gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。

注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。

follow-fork-mode/detach-on-fork的使用还是比较简单的,但由于其系统内核/gdb版本,我们只能在符合要求的系统上才能使用。而且,由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程或曾孙进程的系统,例如上图3进程系统,调试起来并不方便。

Attach子进程

众所周知,GDB有附着(attach)到正在运行的进程的功能,即attach <pid>命令。因此我们可以利用该命令attach到子进程然后进行调试。

例如我们要调试某个进程RIM_Oracle_Agent.9i,首先得到该进程的pid

[root@tivf09 tianq]# ps -ef|grep RIM_Oracle_Agent.9i

nobody 6722 6721 0 05:57 ? 00:00:00 RIM_Oracle_Agent.9i

root 7541 27816 0 06:10 pts/3 00:00:00 grep -i rim_oracle_agent.9i

通过pstree可以看到,这是一个三进程系统,oserv是RIM_Oracle_prog的父进程,RIM_Oracle_prog又是RIM_Oracle_Agent.9i的父进程。

[root@tivf09 root]# pstree -H 6722

通过 pstree 察看进程

通过 pstree 察看进程

启动GDB,attach到该进程

用 GDB 连接进程

用 GDB 连接进程

现在就可以调试了。一个新的问题是,子进程一直在运行,attach上去后都不知道运行到哪里了。有没有办法解决呢?

一个办法是,在要调试的子进程初始代码中,比如main函数开始处,加入一段特殊代码,使子进程在某个条件成立时便循环睡眠等待,attach到进程后在该代码段后设上断点,再把成立的条件取消,使代码可以继续执行下去。

至于这段代码所采用的条件,看你的偏好了。比如我们可以检查一个指定的环境变量的值,或者检查一个特定的文件存不存在。以文件为例,其形式可以如下:

void debug_wait(char *tag_file)

{

while(1)

{

if (tag_file存在)

睡眠一段时间;

else

break;

}

}

当attach到进程后,在该段代码之后设上断点,再把该文件删除就OK了。当然你也可以采用其他的条件或形式,只要这个条件可以设置/检测即可。

Attach进程方法还是很方便的,它能够应付各种各样复杂的进程系统,比如孙子/曾孙进程,比如守护进程(daemon process),唯一需要的就是加入一小段代码。

GDB wrapper

很多时候,父进程 fork 出子进程,子进程会紧接着调用 exec族函数来执行新的代码。对于这种情况,我们也可以使用gdb wrapper 方法。它的优点是不用添加额外代码。

其基本原理是以gdb调用待执行代码作为一个新的整体来被exec函数执行,使得待执行代码始终处于gdb的控制中,这样我们自然能够调试该子进程代码。

还是上面那个例子,RIM_Oracle_prog fork出子进程后将紧接着执行RIM_Oracle_Agent.9i的二进制代码文件。我们将该文件重命名为RIM_Oracle_Agent.9i.binary,并新建一个名为RIM_Oracle_Agent.9i的shell脚本文件,其内容如下:

[root@tivf09 bin]# mv RIM_Oracle_Agent.9i RIM_Oracle_Agent.9i.binary

[root@tivf09 bin]# cat RIM_Oracle_Agent.9i

#!/bin/sh

gdb RIM_Oracle_Agent.binary

当fork的子进程执行名为RIM_Oracle_Agent.9i的文件时,gdb会被首先启动,使得要调试的代码处于gdb控制之下。

新的问题来了。子进程是在gdb的控制下了,但还是不能调试:如何与gdb交互呢?我们必须以某种方式启动gdb,以便能在某个窗口/终端与gdb交互。具体来说,可以使用xterm生成这个窗口。

xterm是X window系统下的模拟终端程序。比如我们在Linux桌面环境GNOME中敲入xterm命令:

xterm

xterm

就会跳出一个终端窗口:

终端

终端

如果你是在一台远程linux服务器上调试,那么可以使用VNC(Virtual Network Computing) viewer从本地机器连接到服务器上使用xterm。在此之前,需要在你的本地机器上安装VNC viewer,在服务器上安装并启动VNC server。大多数linux发行版都预装了vnc-server软件包,所以我们可以直接运行vncserver命令。注意,第一次运行vncserver时会提示输入密码,用作VNC viewer从客户端连接时的密码。可以在VNC server机器上使用vncpasswd命令修改密码。

[root@tivf09 root]# vncserver

New 'tivf09:1 (root)' desktop is tivf09:1

Starting applications specified in /root/.vnc/xstartup

Log file is /root/.vnc/tivf09:1.log

[root@tivf09 root]#

[root@tivf09 root]# ps -ef|grep -i vnc

root 19609 1 0 Jun05 ? 00:08:46 Xvnc :1 -desktop tivf09:1 (root)

-httpd /usr/share/vnc/classes -auth /root/.Xauthority -geometry 1024x768

-depth 16 -rfbwait 30000 -rfbauth /root/.vnc/passwd -rfbport 5901 -pn

root 19627 1 0 Jun05 ? 00:00:00 vncconfig -iconic

root 12714 10599 0 01:23 pts/0 00:00:00 grep -i vnc

[root@tivf09 root]#

Vncserver是一个Perl脚本,用来启动Xvnc(X VNC server)。X client应用,比如xterm,VNC viewer都是和它通信的。如上所示,我们可以使用的DISPLAY值为tivf09:1。现在就可以从本地机器使用VNC viewer连接过去:

VNC viewer:输入服务器

VNC viewer:输入服务器

输入密码:

VNC viewer:输入密码

VNC viewer:输入密码

登录成功,界面和服务器本地桌面上一样:

VNC viewer

VNC viewer

下面我们来修改RIM_Oracle_Agent.9i脚本,使它看起来像下面这样:

#!/bin/sh

export DISPLAY=tivf09:1.0; xterm -e gdb RIM_Oracle_Agent.binary

如果你的程序在exec的时候还传入了参数,可以改成:

#!/bin/sh

export DISPLAY=tivf09:1.0; xterm -e gdb --args RIM_Oracle_Agent.binary $@

最后加上执行权限

[root@tivf09 bin]# chmod 755 RIM_Oracle_Agent.9i

现在就可以调试了。运行启动子进程的程序:

[root@tivf09 root]# wrimtest -l 9i_linux

Resource Type : RIM

Resource Label : 9i_linux

Host Name : tivf09

User Name : mdstatus

Vendor : Oracle

Database : rim

Database Home : /data/oracle9i/920

Server ID : rim

Instance Home :

Instance Name :

Opening Regular Session...

程序停住了。从VNC viewer中可以看到,一个新的gdb xterm窗口在服务器端打开了

gdb xterm 窗口

gdb xterm窗口

[root@tivf09 root]# ps -ef|grep gdb

nobody 24312 24311 0 04:30 ? 00:00:00 xterm -e gdb RIM_Oracle_Agent.binary

nobody 24314 24312 0 04:30 pts/2 00:00:00 gdb RIM_Oracle_Agent.binary

root 24326 10599 0 04:30 pts/0 00:00:00 grep gdb

运行的正是要调试的程序。设置好断点,开始调试吧!

注意,下面的错误一般是权限的问题,使用 xhost 命令来修改权限:

xterm 错误

xterm 错误

[root@tivf09 bin]# export DISPLAY=tivf09:1.0

[root@tivf09 bin]# xhost +

access control disabled, clients can connect from any host

xhost + 禁止了访问控制,从任何机器都可以连接过来。考虑到安全问题,你也可以使用xhost + <你的机器名>。

小结

上述三种方法各有特点和优劣,因此适应于不同的场合和环境:

follow-fork-mode方法:方便易用,对系统内核和GDB版本有,适合于较为简单的多进程系统

attach子进程方法:灵活强大,但需要添加额外代码,适合于各种复杂情况,特别是守护进程

GDB wrapper方法:专用于fork+exec模式,不用添加额外代码,但需要X环境支持(xterm/VNC)。

显示全文