GDB十分钟教程

作者: liigo
原文链接: http://blog.csdn.net/liigo/archive/2006/01/17/582231.aspx
日期: 2006年1月16日

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

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

GDB中的命令固然非常多,但我们仅仅需掌握当中十个左右的命令,就大致能够完毕日常的主要的程序调试工作。

 命令  解释  演示样例
file <文件名称> 载入被调试的可运行程序文件。
由于一般都在被调试程序所在文件夹下运行GDB,因而文本名不须要带路径。
(gdb) file gdb-sample
r Run的简写,执行被调试的程序。
假设此前没有下过断点,则执行完整个程序;假设有断点,则程序暂停在第一个可用断点处。
(gdb) r
c Continue的简写,继续运行被调试程序,直至下一个断点或程序结束。 (gdb) c
b <行号>
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, n s: 运行一行源程序代码,假设此行代码中有函数调用,则进入该函数;
n: 运行一行源程序代码,此行代码中的函数调用也一并运行。

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

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

(gdb) s
(gdb) n
si, ni si命令相似于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

i Info的简写,用于显示各类信息,详情请查阅“help i”。 (gdb) i r
q Quit的简写,退出GDB调试环境。 (gdb) q
help [命令名称] GDB帮助命令,提供对GDB名种命令的解释说明。
假设指定了“命令名称”參数,则显示该命令的具体说明;假设没有指定參数,则分类显示全部GDB命令,供用户进一步浏览和查询。
(gdb) help display

 

废话不多说,以下開始实践。

先给出一个演示样例用的小程序,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 (a + 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) y

版权全部:liigo.com

转载请事先征得作者liigo允许。

liigo@sina.com
www.liigo.com
http://blog.csdn.net/liigo/
QQ: 175199125

GDB十几分钟教程的更多相关文章

  1. [转载]GDB十分钟教程

    转自:http://blog.csdn.net/liigo/article/details/582231/ GDB十分钟教程 作者: liigo原文链接: http://blog.csdn.net/l ...

  2. GDB十分钟教程 (链接)

    未联系作者,只能放个链接了. 十分赞的gdb教程. GDB十分钟教程

  3. 【项目管理和构建】十分钟教程,eclipse配置maven + 创建maven项目(三)

    [项目管理和构建]十分钟教程,eclipse配置maven + 创建maven项目(三) 上篇博文中我们介绍了maven下载.安装和配置(二),这篇博文我们配置一下eclipse,将它和maven结合 ...

  4. 十大免费教程资源帮助新手快速学习JavaScript

    “JavaScript”的名头相信大家肯定是耳熟能详,但只有一小部分人群了解它的使用与应用程序构建方式.这“一小部分”人指的当然是技术过硬的有为青年.网络程序员以及IT专业人员.但对于一位新手或者说外 ...

  5. JUnit三分钟教程 ---- 实际应用

    JUnit三分钟教程 ---- 实际应用 摘自http://lavasoft.blog.51cto.com/62575/65775   接上文"JUnit三分钟教程 ---- 快速起步&qu ...

  6. JUnit三分钟教程 ---- 快速起步

    JUnit三分钟教程 ---- 快速起步 摘自http://lavasoft.blog.51cto.com/62575/65625/ JUnit是个好东西,做大点的项目离不开这东西,实际中用的时候也因 ...

  7. Gradle 1.12 翻译——第十四章. 教程 - 杂七杂八

    有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...

  8. Org-mode五分钟教程ZZZ - Kaka Abel的日志 - 网易博客

    Org-mode五分钟教程ZZZ - Kaka Abel的日志 - 网易博客 Org-mode五分钟教程ZZZ  

  9. 【翻译】Apache Shiro10分钟教程

    本文为翻译文,原文地址:http://shiro.apache.org/10-minute-tutorial.html 介绍 欢迎来到Apache Shiro的10分钟教程! 通过这个教程,你会明白一 ...

随机推荐

  1. firebug使用

    查看网页的header是优化web应用的关键一环,从header中,可以检查到非常 多的信息:网页在squid中的缓存时间(age).客户端缓存时间(expires).是否有最后修改时间(last-m ...

  2. Fragment总结

    一.总体工程图: 二.main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android& ...

  3. 浅谈 Python 程序和 C 程序的整合

    源地址:http://www.ibm.com/developerworks/cn/linux/l-cn-pythonandc/ 概览 Python 是一种用于快速开发软件的编程语言,它的语法比较简单, ...

  4. 如何高效地向Redis插入大量的数据(转)

    最近有个哥们在群里问,有一个日志,里面存的是IP地址(一行一个),如何将这些IP快速导入到Redis中. 我刚开始的建议是Shell+redis客户端. 今天,查看Redis官档,发现文档的首页部分( ...

  5. 关于java 日文输出信息到 Jenkins Console Output 乱码问题

    java 将从读取到的外部调用程序的带有日文字符的输出信息 输出到Jenkins 上的Console Output 上乱码. 现象分析: Jenkins 上可以将日文正常显示出来,但是读取外部程序的输 ...

  6. Android开发之模板模式初探

    模板模式我认为在Android的开发中是最长用到的,基本是随处可见的,认识该模式,有助于我们对Android的源代码及框架有一个更深层次的认识.那什么是模板模式呢,模板模式就是定义一个基本框架,将当中 ...

  7. httpd的简单配置(转)

    一般网站都采用httpd作web服务器提供web页面,本文主要介绍下几个httpd中常用的配置属性和配置方式,当然具体应用更具具体需求来定. 代理模块配置: 由于网页动态化,网页的生成基本代理到后端服 ...

  8. Net Kafka

    Net Kafka Kafka 协议实现中的内存优化 Jusfr 2016-04-18 08:28 阅读:241 评论:1     Kafka API: TopicMetadata Jusfr 201 ...

  9. 23、Cocos2dx 3.0游戏开发找小三之粒子系统:你那里下雪了吗?

    重开发人员的劳动成果.转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/30485919 春雨惊春清谷天,夏满芒夏暑相连, 秋处 ...

  10. 理解Javascript的动态语言特性

    原文:理解Javascript的动态语言特性 理解Javascript的动态语言特性 Javascript是一种解释性语言,而并非编译性,它不能编译成二进制文件. 理解动态执行与闭包的概念 动态执行: ...