Chinaunix首页 | 论坛 | 博客
  • 博客访问: 351458
  • 博文数量: 83
  • 博客积分: 5322
  • 博客等级: 中校
  • 技术积分: 1057
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-11 11:27
个人简介

爱生活,爱阅读

文章分类

全部博文(83)

文章存档

2015年(1)

2013年(1)

2012年(80)

2011年(1)

分类:

2012-09-17 20:27:13

Using GNU's GDB Debugger

Breakpoints And Watchpoints

By Peter Jay Salzman

断点,观察点,捕捉点

到目前为止,你知道了如何在gdb内列出源代码和运行程序。但是,之前你就知道如何做到这些而没有使用gdb。除此之外,gdb还能为我们提供什么呢?使用gdb无论做任何有用的事情,你都需要设置断点(set breakpoint)。断点(breakpoint)暂时停止程序的运行。这样,你就可以进行有益的调试工作,像观察变量,以及使用原子的(atomic)、“逐行(line-by-line)”的方式观察程序的执行。这些正是符号调试器(symbolic debugger)的魅力。

断点有三种特点:

1.   A breakpoint stops your program whenever a particular point in the program is reached. We will discuss breakpoints momentarily.

当程序执行到一个特殊的点(particular point)时,断点(breakpoint)就会终止程序的执行。我们现在就讨论断点。【That means a breakpoint is a particular point in the program set by the  programmer

2.   A watchpoint stops your program whenever the value of a variable or expression changes. We'll discuss watchpoints later on in this chapter.

当一个变量或者表达式的值发生了改变,观察点(watchpoint)停止该程序的执行。我们将在本章的稍后部分讨论观察点。

3.   A catchpoint stops your program whenever a particular event occurs. We won't discuss catchpoints until I get a chance to write about them.

当一个特殊的事件发生时,捕捉点(catchpoint)将停止程序的执行。我们将不会讨论捕捉点,直到我有机会来写作它们。

断点

当程序执行到一个特殊的点(particular point)时,断点(breakpoint)就会停止程序的执行。下面是关于断点的用途的例子:

计算机先生,能不能在下面的情况时停止下来呢。。。

l  you reach line 420 of the current source code file?

当你执行到当前源代码文件的第420行?

l  you enter the function validateInput()?

当你进入函数validateInput()?

l  you reach line 2718 of the file video.c?

当你执行到文件video.c的第2718行?

所有的这些需求有一个共同点:当程序运行到一些位置时,请求gdb停止该程序的执行。这就是断点所做的。在开始之前,我需要说明两点:

1.   What does "stopping at line 5" mean?

“在第5行”停止是什么意思?

gdb在“第5行”停止时,它的意思是说:gdb正在第4行与第5行“之间”等待。第5行还没有被执行。记住这一点!你可以通过next命令执行第5行,但是现在还没有发生。

2.   Why did gdb stop here?

为什么gdb要在此处停止?

有时候,你可能对于gdb停止的地方感到吃惊。你可能在源代码第5行指定了一个断点,但是gdb却在例如第7行停了下来。有两种情况可导致这种现象。首先,如果你采用了优化的方式对程序进行编译,此时一些源代码行可能被优化掉了;它们在你的源代码中存在,但并不在可执行程序中存在。其次,并不是所有的源代码都会编译出机器代码指令(machine code instruction)。参见“直到(until)”部分(提醒我:当我书写它时)【】。考虑下面的代码:

1   #include

2     

3   int main( void )

4   {

5        int i;

6        i = 3;

7  

8        return 0;

9   } 

X行插入断点使得你的程序在行Y处暂停。。。

unoptimized code

optimized code

Breakpoint at line

Program pauses at line

Breakpoint set at line

Program pauses at line

1--4, main()

4

1--4, main()

4

5, 6

6

5--9

9

7, 8

8

9

9


每个设置的断点,观察点以及捕捉点均被设置一个从1开始的号码。你使用该号码来引用该断点。为了查看你设置的所有断点与观察点列表,输入info breakpoints(也可以简洁地使用i b)。下面就是采用该命令产生的一个输出的例子:

          (gdb) info breakpoints

          Num Type           Disp Enb Address    What

          1   breakpoint     keep y   0x080483f6 in main at try5.c:4

                                breakpoint already hit 1 time

          2   breakpoint     keep n   0x0804841a in display at try5.c:14

                                breakpoint already hit 1 time

          3   hw watchpoint  keep y   i

根据输出可以看到,共设置了两个断点,一个在第4行,另一个在第14行。它们分别被设置为号码1与号码2.也设置了一个观察点:当变量i(函数display()的的局部变量)0的值发生改变的时候,程序将暂停。

除了设置一个号码之外,每个断点与观察点可以被“使能”(enabled)或者“去使能”(disabled)。程序不会在“去使能”的断点或者观察点上停止。默认情况下,当你创建一个断点或者观察点,它是“使能”的。为了“去使能”号码为n的断点或者观察点,输入:

          disable n

为了重新使能(re-enable)断点或者观察点,输入:

          enable n

如果你在查看上例中info breakpoints的输出,你将会看到断点2已经被去使能了。

阻断

   $ gcc -c -g -W -Wall fgets.c main.c

   $ gcc -o fgets fgets.o main.o

注意:编译器会产生一条告警。这是因为我们使用了-W –Wall。它将通知gcc在其发现可能的编程错误时,来通知我们。调试程序最好的方法就是不要在程序的开始就引入缺陷。你应当总是使用这些gcc 缺陷查找选项(bug finding option)。直率地讲,我不想得罪任何人。在编译程序时候没有采用-W -Wall选项是非常愚蠢的。清晰与常见。【】愚蠢的,开头的字母为大写S。大部分人,甚至是比我聪明得多的程序员都不使用这些选项。这是因为即使聪明的人也会做些傻事。不要再傻了,总是使用-W –Wall吧。

该程序是一个猜测密码(password guessing)的程序。花点时间查看代码来了解它是如何工作的。这个程序相当的简单,所以我们可以仅关注于学习GDB,而不是弄明白像链表一样复杂的代码。你应该在几秒钟之后就能够推断程序是如何工作的。我们首先专心学习如何设置断点,然后调试该程序。

Setting Basic Breakpoints

设置基本断点

设置断点有四种主要的方式,大体上,我使用它们的顺序是:

1.  By function name.

按照函数名。

2.  By line number.

按照行号。

3.  By filename and line number.

按照文件名与行号。

4.  By address.

地址。

按照函数名

我们已经看到,设置断点最常见的方式是:通过函数名。

   $ gdb fgets

   Using host libthread_db library "/lib/tls/libthread_db.so.1".

   (gdb) break main

   Breakpoint 1 at 0x8048464: file main.c, line 6.

   (gdb)

break main”命令在main()函数的顶部设置了一个断点,碰巧为main.c的第6行。如果我们现在运行该程序,那么该程序将在第6行停止。回忆一下前面的讨论,这意味着GDB将在第5行与第6行之间暂停。第6行还没有被执行,直到我们执行step命令:

   (gdb) run

   Starting program: code/fgets/fgets

  

   Breakpoint 1, main () at main.c:6

   6               char *word = "password";

   (gdb)

按照行号

另一种设置断点的方式是通过行号。这里所说的行号指的是GDB所在的文件的行号。从现在开始,我们进入了main.c文件,因此,我们所说的行号是关于main.c的。让我们在第9行设置一个断点,该断点位于printf()语句上。

   (gdb) break 9

   Breakpoint 2 at 0x804846b: file main.c, line 9.

   (gdb)

GDB有一个之前我们没有看到的continue命令。一旦GDB在一个断点上暂停下来,那么“继续(continue)”命令将重新恢复执行。使用continue来确认GDB在第9行暂停:

   (gdb) continue

   Continuing.

  

   Breakpoint 2, main () at main.c:9

   9               printf("I'm thinking of a word.  Let's see if you can guess it.\n");

   (gdb)

按照文件与行号

第三种设置断点的方式是通过一个用冒号(colon)分开的文件名与行号。让我们在fgets.c的第10行设置断点:

   (gdb) break fgets.c:10

   Breakpoint 3 at 0x80483fd: file fgets.c, line 10.

   (gdb)

按照地址

第四种设置断点的方法是使用进程虚拟空间中的内存地址。我们将查找到TakeGuess()的地址并在该地址设置一个断点:

   (gdb) print TakeGuess

   $1 = {int (const char *)} 0x80483f4

   (gdb) break *0x80483f4

   Breakpoint 4 at 0x80483f4: file fgets.c, line 7.

断点号

你或许已经注意到了每一个断点均被设置了一个整数标识符(integer identifier)。例如,我们已经设置了4个断点,最后设置的断点(按照地址)的整数标识符被设置为4。如果你没有注意到这一点,返回去查看一下。在一个断点上可以执行不同的操作,像删除它们。你可以通过引用其整数标识符来对一个特殊的断点执行操作。

删除断点

正如你可以设置断点一样,你也可以删除它们。这里有很多种方法删除断点:

l  If you want to remove the breakpoint by its location, use clear.

如果你想按照断点位置来删除断点,使用clear

l  If you want to remove the breakpoint by its identifier, use delete.

如果你想按照其标识符来删除断点,使用delete

让我们使用clear来删除按照位置设置的四个断点;一种类似于“取消(undoing)”的操作:

   (gdb) clear *0x80483f4

   Deleted breakpoint 4

   (gdb) clear fgets.c:10

   Deleted breakpoint 3

   (gdb) clear 9

   Deleted breakpoint 2

   (gdb) clear main

   Deleted breakpoint 1

   (gdb)

Delete命令按照标识符删除断点,这与clear命令恰恰相反:clear是基于断点的位置进行删除的。事实上,delete n命令是根据标识符n删除断点n的。我们将在练习中更全面地研究这个命令。

Exercises

练习

1.   If you've been following along with the tutorial, you shouldn't have any breakpoints set since we deleted them all with clear. Set three breakpoints wherever you like by the methods of your choice. Before you do, guess what their identifiers will be.

如果你一直按照教程操作,那么你不应该有任何断点了,因为我们已经通过clear命令将之删除了。按照你选择的方式在任意位置设置三个断点。在操作之前,猜一猜它们的标识符将会是什么。

2.   Use delete, not clear, to remove only the last breakpoint you set. This will leave you with two remaining breakpoints.

使用delete 而不是clear删除最后一个你设置的断点。这将剩下两个断点。

3.   You should have two breakpoints left. delete with no arguments removes all breakpoints. Try it out, then quit GDB.

你应该剩余两个断点。没有参数的delete将删除说有的断点。尝试一下,然后退出GDB

使能,去使能以及忽略

一旦进行了设置,只有两种方式摆脱断点:删除它或者退出GDBGDB将在断点处不断的终止。然而,有时候你会发现,暂时的去使能断点非常有用,也就是说,你不希望GDB在某个断点处暂停,但是你想保留该断点,因为你需要在再次的调试该块代码。

断点可以被“使能”(enable)与“去使能”(disable)。通过简单的设置,你的程序将在“使能的”(enabled)断点暂停,但在“去使能”(disabled)的断点处不再暂停。

你可以使用enable 或者 disable命令,加上一个标识了你想“使能”(enable)或者“去使能”(disable)的断点标识,来使能或者去使能断点。让我们通过使用之前使用过的一个fgets程序来查看之。启动fgets调试会话,并在main.c的行6,9,12设置三个断点:

   $ gdb fgets

   (gdb) break 6

   Breakpoint 1 at 0x8048464: file main.c, line 6.

   (gdb) break 9

   Breakpoint 2 at 0x804846b: file main.c, line 9.

   (gdb) break 12

   Breakpoint 3 at 0x8048477: file main.c, line 12.

去使能断点2,运行程序。然后执行continue命令来确认断点2已经不再暂停程序的执行。

   (gdb) disable 2

   (gdb) run

   Starting program: code/fgets/fgets

  

   Breakpoint 1, main () at main.c:6

   6               char *word = "password";

   (gdb) continue

   Continuing.

   I'm thinking of a word.  Let's see if you can guess it.

  

   Breakpoint 3, main () at main.c:12

   12              while ( KeepGoing )

确认断点2已经去使能。最后,使能断点2并重新运行程序。使用continu命令确认断点2现在暂停程序的执行:

   (gdb) enable 2

   (gdb) run

   The program being debugged has been started already.

   Start it from the beginning? (y or n) y

  

   Starting program: /www/p/linux/gdb/code/fgets/fgets

  

   Breakpoint 1, main () at main.c:6

   6               char *word = "password";

   (gdb) continue

   Continuing.

  

   Breakpoint 2, main () at main.c:9

   9               printf("I'm thinking of a word.  Let's see if you can guess it.\n");

确认一点:一旦使能,断点2将再次暂停程序的执行。

Exercises

练习

1.   The disable command permanently disabled a breakpoint until you explicitly enable it with enable. However, it's possible to temporarily disable a breakpoint. Use GDB's help utility to read about the ignore command, which disables a breakpoint "for n crossings".

Disable命令将永久地去使能一个断点,直到显式采用enable命令将之使能。然而,可以临时地去使能断点。使用GDB的帮助功能,阅读ignore命令,该命令将去使能断点“n次”。

2.   Personally, I don't use ignore a whole lot. It seems like conditional breaking makes ignore not very useful, but you should still know of its existence. Hopefully you have GDB still open. Use ignore to disable breakpoint 3 (the one at line 12) for 3 crossings. Verify that it works.

个人而言,我没有经常使用ignore。它看起来就像条件阻断(conditional breaking),使得ignore不是那么有用,但是你应该知道它的存在。希望你的GDB还在运行。使用ignore来去使能断点3三次。确认它可以工作。

列出断点

到目前为止,我们已经看到了将断点的标识符作为参数的三个命令:deleteenabledisable。也还有很多其它命令使用其作为参数,这些我们稍后提及。关键的是,断点标识符很有用处,你将发现你会非常多地使用它们。但是你是怎样记忆你设置断点的标识符的,或者你的断点是在何处开始设置的?有一条命令:info breakpoints,它将列出所有的断点,它们的标识符,以及很多用信息。希望你之前部分的GDB还在运行,现在检查一下:

   (gdb) info breakpoints

   Num Type           Disp Enb Address    What

   1   breakpoint     keep y   0x08048464 in main at main.c:6

           breakpoint already hit 1 time

   2   breakpoint     keep y   0x0804846b in main at main.c:9

           breakpoint already hit 1 time

   3   breakpoint     keep y   0x08048477 in main at main.c:12

这是非常重要的命令,我发现我总是在使用它。包括下面的几点之外,所有的信息的意思都是很明显的:

1.   The Num field gives the identifier.

Num域列出了断点的标识符。

2.   The Type field gives the type of breakpoint. There are different types of breakpoints, like hardware watchpoints, which we'll cover shortly.

类型域列出了断点的类型。有不同类型的断点,包括硬件的观察点(hardware watchpoint),我们将简短的讲解。

3.   The Disp field (short for disposition) describes what will happen to the breakpoint the next time it's activated (the next time it pauses execution). Keep indicates nothing will happen to the breakpoint, however, it's possible to disable or even remove a breakpoint the next time it's reached. These situations are identified by the Disp field.

Disp域(是disposition的简写)描述了下次遇到该断点会发生什么事情。Keep表示该断点不会发生什么事情,然而,在下次遇到该断点时,可能去使能甚至删除该断点。这些情况就在Disp标识的域中进行描述。

阅读(1709) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~