gdb

来源:互联网 发布:软件项目建设方案 编辑:程序博客网 时间:2024/06/10 05:59

其他代码窗口相关命令:

info win    显示窗口的大小
layout next 切换到下一个布局模式
layout prev 切换到上一个布局模式
layout src   只显示源代码
layout asm   只显示汇编代码
layout split 显示源代码和汇编代码
layout regs   增加寄存器内容显示
focus cmd/src/asm/regs/next/prev 切换当前窗口
refresh     刷新所有窗口
tui reg next 显示下一组寄存器
tui reg system 显示系统寄存器
update      更新源代码窗口和当前执行点
winheight name +/- line 调整name窗口的高度
tabset nchar 设置tab为nchar个字符


http://www.cnblogs.com/kekec/archive/2012/10/16/2693548.html

# 重要说明

(1)  使用GCC进行编译时,需在编译选项中加入"-g"参数

       cc -g main.c -o main
       g++ -g main.cpp -o main

(2)  使用gdb调试前,要先进入待调试模块所在目录(使得能正确关联到源代码文件)

(3)  进入gdb调试状态,直接回车表示"重复上一次命令"

(4)  按上下方向键可以浏览和选择以前输入过的命令

(5)  gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令。

      在Linux下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其列出来

(6)  可从这儿下载最新的gdb [http://www.gnu.org/software/gdb/download/

# 调试进程

gdb test         // 调试当前目录下的名为test的可执行程序    输入r,运行程序

gdb -p PID     // 勾住目标进程进行调试  PID为目标进程ID,可通过ps -ux来查看当前运行的进程

attach 2146   // 附加到PID为2146的进程上

detach 2146  // 释放对PID为2146进程的调试

q                 // 结束gdb调试 [quit]

# 启动额外参数

-se symbolfile    // 从指定文件中读取符号表信息,并用在可执行文件中

-c corefile      // 调试core dump的core文件

如:gdb gamesvrd.0 -c /data/corefile/core_gamesvrd.0_11833_1358767740  // 需要进入gamesvrd.0的目录

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

# 调试

0. 程序参数

set args -b -x  // 修改发送给程序的参数,将-b和-x参数发送给程序

show args       // 显示程序当前的参数

1. 设置断点  [break]

b worker.cpp : 45   // 在worker.cpp文件的45行处设置一个断点

b 45   // 在当前文件的45行处设置一个断点

b worker.cpp : CWorker::DoWork  // 在worker.cpp文件的DoWork函数起始处设置一个断点

------------------

若有命名空间,还应带上命名空间  b worker.cpp : MYPRJ::CWorker::DoWork

若函数被重载,还应带上参数类型  b worker.cpp : CWorker::DoWork(bool)

------------------

b +5  // 在距离当前位置的后5行处设置一个断点

b -6  // 在距离当前位置的前6行处设置一个断点

b ... if <条件>  // 条件断点

例:b worker.cpp : 45 if workerNum==5 

例:b worker.cpp : CWorker::DoWork if workerNum==5

2. 查看断点   [info breakpoints]

i b    // 查看当前所有断点(每个断点都有一个编号)

3. 禁用/启用断点

disable 1  // 禁用编号为1的断点

enable 1   // 启用编号为1的断点

disable     // 禁用所有断点

enable      // 启用所有断点

4. 删除断点  [delete breakpoints]

d 1   // 删除编号为1的断点

d      // 删除所有断点

5. 调试执行控制

r  // 调试启动进程 [run]

k  // 结束当前调试的进程 [kill]

c  // 继续运行 [continue]   相当于vc中的F5

ctrl+c  // 暂停程序运行

n  // 单步执行 [next] 相当于vc中的F10

n 2  // 2为步进数目

s  // step into到函数内部 相当于vc中的F11

finish/fin  // step out出函数 相当于vc中的shift+F11

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

u 145  //执行至145行代码停下 

6. 查看变量  [print]

p nSum   // 打印nSum变量的值 

p /fmt nSum  // 按指定格式打印nSum变量的值

------------------

fmt为以下值 
x 十六进制  d 十进制
u 无符号数  o 八进制 
t 二进制     a 十六进制打印【地址】
c 字符格式  f 浮点数   s 字符串

------------------

h@10  // 查看数组h的前10个值

p *bar  // 查看指针bar所指对象内容

whatis nSum  // 查看变量nSum的类型

ptype foo   // 查看foo的类型定义

w exp  // 查看表达式exp的值 [watch]

7. 修改变量

p x=4    // 将x的值修改为4

8. 查看内存

x/<n/f/u> <addr>

n、f、u是可选的参数。
n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
f 表示显示的格式,参见【6.查看变量fmt的说明】。如果地址所指的是字符串,那么格式可以是s,如果地址是指令地址,那么格式可以是i。
u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。
u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节
当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。

如:x/3uh 0x54320EED

// 从内存地址0x54320EED读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。

9. 其他调试信息查看

bt  // 查看堆栈调用 [backtrace]

frame 3  // 跳转到函数堆栈中索引为3的函数上下文

i reg // 显示寄存器信息 [info]

i func  // 显示所有的函数名

i local  // 显示当前函数的所有局部变量的信息

i prog  // 显示调试程序的执行状态

10. 代码载入  [程序必须被断点断住]

Ctrl+ x  Ctrl + a  // 将代码载入到命令行中进行调试

l  // 载入当前代码行的前后各4行代码 [list]

l 45  // 显示当前文件按第45行的周围代码

l worker.cpp : 70 // 显示work.cpp第70行的周围代码

l worker.cpp : sum // 显示work.cpp中sum函数的周围代码

l CWorker::DoWork(bool)  // 显示函数名为CWorker::DoWork(bool)的函数的代码

l 0xabcd1234  //  查看地址为0xabcd1234的周围的代码

11. 多线程

i threads  // 显示线程信息

thread 2   // 切换到线程表中索引为2的线程

b foo 2  // 在函数foo设置断点,并且只在索引为2的线程中命中

12. 强制操作

call A(1,2)  // 强制调用函数A,并输出其返回值

return   // 使用return命令取消当前函数的执行,并立即返回

return exp  // 如果指定了exp,那么该表达式的值会被认作函数的返回值





本文写给主要工作在Windows操作系统下而又需要开发一些跨平台软件的程序员朋友,以及程序爱好者。

GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。

GDB中的命令固然很多,但我们只需掌握其中十个左右的命令,就大致可以完成日常的基本的程序调试工作。

命令解释示例file <文件名>加载被调试的可执行程序文件。
因为一般都在被调试程序所在目录下执行GDB,因而文本名不需要带路径。
(gdb) file gdb-samplerRun的简写,运行被调试的程序。
如果此前没有下过断点,则执行完整个程序;如果有断点,则程序暂停在第一个可用断点处。
(gdb) rcContinue的简写,继续执行被调试程序,直至下一个断点或程序结束。(gdb) cb <行号>
b <函数名称>
b *<函数名称>
b *<代码地址>

d [编号]

b: Breakpoint的简写,设置断点。两可以使用“行号”“函数名称”“执行地址”等方式指定断点位置。
其中在函数名称前面加“*”符号表示将断点设置在“由编译器生成的prolog代码处”。如果不了解汇编,可以不予理会此用法。

d: Delete breakpoint的简写,删除指定编号的某个断点,或删除所有断点。断点编号从1开始递增。

(gdb) b 8
(gdb) b main
(gdb) b *main
(gdb) b *0x804835c

(gdb) d

s, ns: 执行一行源程序代码,如果此行代码中有函数调用,则进入该函数;
n: 执行一行源程序代码,此行代码中的函数调用也一并执行。

s 相当于其它调试器中的“Step Into (单步跟踪进入)”;
n 相当于其它调试器中的“Step Over (单步跟踪)”。

这两个命令必须在有源代码调试信息的情况下才可以使用(GCC编译时使用“-g”参数)。

(gdb) s
(gdb) n
si, nisi命令类似于s命令,ni命令类似于n命令。所不同的是,这两个命令(si/ni)所针对的是汇编指令,而s/n针对的是源代码。(gdb) si
(gdb) ni
p <变量名称>Print的简写,显示指定变量(临时变量或全局变量)的值。(gdb) p i
(gdb) p nGlobalVar
display ...

undisplay <编号>

display,设置程序中断后欲显示的数据及其格式。
例如,如果希望每次程序中断后可以看到即将被执行的下一条汇编指令,可以使用命令
“display /i $pc”
其中 $pc 代表当前汇编指令,/i 表示以十六进行显示。当需要关心汇编代码时,此命令相当有用。

undispaly,取消先前的display设置,编号从1开始递增。

(gdb) display /i $pc

(gdb) undisplay 1

iInfo的简写,用于显示各类信息,详情请查阅“help i”。(gdb) i rqQuit的简写,退出GDB调试环境。(gdb) qhelp [命令名称]GDB帮助命令,提供对GDB名种命令的解释说明。
如果指定了“命令名称”参数,则显示该命令的详细说明;如果没有指定参数,则分类显示所有GDB命令,供用户进一步浏览和查询。
(gdb) help display

/add************************************/

j命令              回跳

ret               设置返回值        (例ret 0/ret -1)

/add************************************/

废话不多说,下面开始实践。

先给出一个示例用的小程序,C语言代码,简单的不能再简单了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

//此程序仅作为“GDB十分钟教程”的示例代码, by liigo
//Email: 
liigo@sina.com
//blog: http://blog.csdn.net/liigo
//WebSite: www.liigo.com
 

#include
 <stdio.h>

int
 nGlobalVar = 0;

int
 tempFunction(int a, int b)
{
    printf("tempFunction is called, a = %d, b = %d /n", a, b
);
    return (+ b
);
}


int
 main()
{
    int n
;
        n = 1
;
        n
++;
        n
--;

        nGlobalVar += 100
;
        nGlobalVar -= 12
;

    printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar
);

        n = tempFunction(1, 2
);
    printf("n = %d", n
);

    return 0
;
}

请将此代码复制出来并保存到文件 gdb-sample.c 中,然后切换到此文件所在目录,用GCC编译之:

gcc gdb-sample.c -o gdb-sample -g

在上面的命令行中,使用 -o 参数指定了编译生成的可执行文件名为 gdb-sample,使用参数 -g 表示将源代码信息编译到可执行文件中。如果不使用参数 -g,会给后面的GDB调试造成不便。当然,如果我们没有程序的源代码,自然也无从使用 -g 参数,调试/跟踪时也只能是汇编代码级别的调试/跟踪。

下面“gdb”命令启动GDB,将首先显示GDB说明,不管它:

GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu".
(gdb)

上面最后一行“(gdb) ”为GDB内部命令引导符,等待用户输入GDB命令。

下面使用“file”命令载入被调试程序 gdb-sample(这里的 gdb-sample 即前面 GCC 编译输出的可执行文件):

(gdb) file gdb-sample
Reading symbols from gdb-sample...done.

上面最后一行提示已经加载成功。

下面使用“r”命令执行(Run)被调试文件,因为尚未设置任何断点,将直接执行到程序结束:

(gdb) r
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample
n = 1, nGlobalVar = 88
tempFunction is called, a = 1, b = 2
n = 3
Program exited normally.

下面使用“b”命令在 main 函数开头设置一个断点(Breakpoint):

(gdb) b main
Breakpoint 1 at 0x804835c: file gdb-sample.c, line 19.

上面最后一行提示已经成功设置断点,并给出了该断点信息:在源文件 gdb-sample.c 第19行处设置断点;这是本程序的第一个断点(序号为1);断点处的代码地址为 0x804835c(此值可能仅在本次调试过程中有效)。回过头去看源代码,第19行中的代码为“n = 1”,恰好是 main 函数中的第一个可执行语句(前面的“int n;”为变量定义语句,并非可执行语句)。

再次使用“r”命令执行(Run)被调试程序:

(gdb) r
Starting program: /home/liigo/temp/gdb-sample

Breakpoint 1, main () at gdb-sample.c:19
19 n = 1;

程序中断在gdb-sample.c第19行处,即main函数是第一个可执行语句处。

上面最后一行信息为:下一条将要执行的源代码为“n = 1;”,它是源代码文件gdb-sample.c中的第19行。

下面使用“s”命令(Step)执行下一行代码(即第19行“n = 1;”):

(gdb) s
20 n++;

上面的信息表示已经执行完“n = 1;”,并显示下一条要执行的代码为第20行的“n++;”。

既然已经执行了“n = 1;”,即给变量 n 赋值为 1,那我们用“p”命令(Print)看一下变量 n 的值是不是 1 :

(gdb) p n
$1 = 1

果然是 1。($1大致是表示这是第一次使用“p”命令——再次执行“p n”将显示“$2 = 1”——此信息应该没有什么用处。)

下面我们分别在第26行、tempFunction 函数开头各设置一个断点(分别使用命令“b 26”“b tempFunction”):

(gdb) b 26
Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26.
(gdb) b tempFunction
Breakpoint 3 at 0x804832e: file gdb-sample.c, line 12.

使用“c”命令继续(Continue)执行被调试程序,程序将中断在第二 个断点(26行),此时全局变量 nGlobalVar 的值应该是 88;再一次执行“c”命令,程序将中断于第三个断点(12行,tempFunction 函数开头处),此时tempFunction 函数的两个参数 a、b 的值应分别是 1 和 2:

(gdb) c
Continuing.

Breakpoint 2, main () at gdb-sample.c:26
26 printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar);
(gdb) p nGlobalVar
$2 = 88
(gdb) c
Continuing.
n = 1, nGlobalVar = 88

Breakpoint 3, tempFunction (a=1, b=2) at gdb-sample.c:12
12 printf("tempFunction is called, a = %d, b = %d /n", a, b);
(gdb) p a
$3 = 1
(gdb) p b
$4 = 2

上面反馈的信息一切都在我们预料之中,哈哈~~~

再一次执行“c”命令(Continue),因为后面再也没有其它断点,程序将一直执行到结束:

(gdb) c
Continuing.
tempFunction is called, a = 1, b = 2
n = 3
Program exited normally.

 

有时候需要看到编译器生成的汇编代码,以进行汇编级的调试或跟踪,又该如何操作呢?

这就要用到display命令“display /i $pc”了(此命令前面已有详细解释):

(gdb) display /i $pc
(gdb)

此后程序再中断时,就可以显示出汇编代码了:

(gdb) r
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

Breakpoint 1, main () at gdb-sample.c:19
19 n = 1;
1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

看到了汇编代码,“n = 1;”对应的汇编代码是“movl $0x1,0xfffffffc(%ebp)”。

并且以后程序每次中断都将显示下一条汇编指定(“si”命令用于执行一条汇编代码——区别于“s”执行一行C代码):

(gdb) si
20 n++;
1: x/i $pc 0x8048363 <main+23>: lea 0xfffffffc(%ebp),%eax
(gdb) si
0x08048366 20 n++;
1: x/i $pc 0x8048366 <main+26>: incl (%eax)
(gdb) si
21 n--;
1: x/i $pc 0x8048368 <main+28>: lea 0xfffffffc(%ebp),%eax
(gdb) si
0x0804836b 21 n--;
1: x/i $pc 0x804836b <main+31>: decl (%eax)
(gdb) si
23 nGlobalVar += 100;
1: x/i $pc 0x804836d <main+33>: addl $0x64,0x80494fc

 

接下来我们试一下命令“b *<函数名称>”。

为了更简明,有必要先删除目前所有断点(使用“d”命令——Delete breakpoint):

(gdb) d
Delete all breakpoints? (y or n) y
(gdb)

当被询问是否删除所有断点时,输入“y”并按回车键即可。

下面使用命令“b *main”在 main 函数的 prolog 代码处设置断点(prolog、epilog,分别表示编译器在每个函数的开头和结尾自行插入的代码):

(gdb) b *main
Breakpoint 4 at 0x804834c: file gdb-sample.c, line 17.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample

Breakpoint 4, main () at gdb-sample.c:17
17 {
1: x/i $pc 0x804834c <main>: push %ebp
(gdb) si
0x0804834d 17 {
1: x/i $pc 0x804834d <main+1>: mov %esp,%ebp
(gdb) si
0x0804834f in main () at gdb-sample.c:17
17 {
1: x/i $pc 0x804834f <main+3>: sub $0x8,%esp
(gdb) si
0x08048352 17 {
1: x/i $pc 0x8048352 <main+6>: and $0xfffffff0,%esp
(gdb) si
0x08048355 17 {
1: x/i $pc 0x8048355 <main+9>: mov $0x0,%eax
(gdb) si
0x0804835a 17 {
1: x/i $pc 0x804835a <main+14>: sub %eax,%esp
(gdb) si
19 n = 1;
1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

此时可以使用“i r”命令显示寄存器中的当前值———“i r”即“Infomation Register”:

(gdb) i r
eax 0xbffff6a4 -1073744220
ecx 0x42015554 1107383636
edx 0x40016bc8 1073834952
ebx 0x42130a14 1108544020
esp 0xbffff6a0 0xbffff6a0
ebp 0xbffff6a8 0xbffff6a8
esi 0x40015360 1073828704
edi 0x80483f0 134513648
eip 0x8048366 0x8048366
eflags 0x386 902
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x33 51

当然也可以显示任意一个指定的寄存器值:

(gdb) i r eax
eax 0xbffff6a4 -1073744220

 

最后一个要介绍的命令是“q”,退出(Quit)GDB调试环境:

(gdb) q
The program is running. Exit anyway? (y or n)

原创粉丝点击