(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命令中可用) |
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)。