GDB调试

Debugging with gdb
The gnu Source-Level Debugger
Ninth Edition, for gdb version 6.8.50.20090706
PACKAGE
(GDB)
广告: c/c++学习群(4649839)
本群大部分时间讨论的是八卦、文艺、帝国的事情,偶尔说些叫做技
术的事情,群内多数人对于文艺比较热衷,如果你没有语言洁癖,精
神洁癖,神经比较大条,对于编程又有那么一点热衷和兴趣,乐于分
享和解答你编程中的困惑,疑问,而且不要求别人给你写作业,应付
老师检查,欢迎这样的英雄,尤其是女英雄加入。括弧(群内不乏热
衷于解答女英雄编程,人生,感情的困惑)
另,群成员 梭鱼(2109598) 诚征女友,意者单独与其联系。
前言 …6
第一章:一个 GDB 会话样例 …6
1 一个 GDB 会话样例 …6
第二章:进入和离开 GDB …10
2.1 调用 GDB …10
2.1.1 选择文件…11
2.1.2 选择模式…12
2.1.3 GDB 在启动阶段的活动 …14
2.2 退出 GDB …15
2.3 Shell 命令 …15
2.4 日志输出…15
第三章 GDB 命令…16
3.1 命令语法…16
3.2 命令补全…16
3.3 帮助…18
第四章 在 GDB 里运行程序 …20
4.1 为调试而编译…20
4.2 开始程序…21
4.3 程序参数…22
4.4 程序的环境…23
4.5 程序的工作目录…24
4.6 程序的输入输出…24
4.7 调试一个已经在运行的进程…25
4.8 杀死子进程…25
4.9 调试多线程进程…26
4.10 调试多个程序…28
4.11 为跳转设置书签…30
4.11.1 使用检查点的隐含好处…31
第五章 中断和继续…31
5.1 断点,监视点,捕获点…31
5.1.1 设置断点…32
5.1.2 设置监视点…36
5.1.3 设置捕获点…38
5.1.4 删除断点…39
5.1.5 禁用断点…40
5.1.6 中断条件…41
5.1.7 断点命令列表…42
5.1.8 断点菜单…43
5.1.9 “不能插入断点”…44
5.1.10 “断点地址已调整…”…44
5.2 继续和单步跟踪…45
5.3 信号…48
5.4 中断和开始多线程程序…49
第六章 检查栈…51
6.1 堆栈帧…51
6.2 回溯…52
6.3 选择堆栈帧…53
6.4 堆栈帧信息…54
第七章 检查源文件…55
7.1 打印源代码行…55
7.2 指定位置…56
7.3 编辑源文件…57
7.3.1 选择编辑器…58
7.4 搜索源文件…58
7.5 指定源文件目录…58
7.6 源代码和机器代码…61
第八章 查看数据…62
8.1 表达式…62
8.2 程序变量…63
8.3 伪数组…65
8.4 输出格式…65
8.5 查看内存…66
8.6 自动显示…68
8.7 打印设置…69
8.8 值历史…74
8.9 惯用变量…75
8.10 寄存器…76
8.11 浮点硬件…77
8.12 向量单元…77
8.13 操作系统辅助信息…78
8.14 内存区域属性…78
8.14.1 属性…79
8.14.2 内存访问检查…80
8.15 在内存和文件之间复制数据…80
8.16 如何从程序里产生 Core 文件…81
8.17 字符集…81
8.18 缓存远程目标的数据…84
第九章 C 预处理宏…84
第十章 跟踪点…88
10.1 设置跟踪点的命令…88
10.1.1 创建和删除跟踪点…88
10.1.2 激活和禁用跟踪点…89
10.1.3 跟踪点通过计数…89
10.1.4 跟踪点操作列表…90
10.1.5 跟踪点列表…91
10.1.6 开始和中止跟踪会话…91
10.2 使用已收集的数据…92
10.2.1 tfind n …92
10.2.2 tdump …94
10.2.3 save-tracepoints filename…95
第十一章 调试使用覆盖技术的程序…95
11.1 覆盖是如何工作的…96
11.2 覆盖命令…97
11.3 自动覆盖调试…99
11.4 覆盖示例程序…99
第十二章 用 GDB 调试不同语言编写的程序 …100
12.1 切换源代码语言…100
12.1.1 文件扩展名和语言列表…101
12.1.2 设置工作语言…101
12.1.3 让 GDB 推断源语言 …102
12.2 显示语言…102
12.3 类型和域检查…102
12.3.1 类型检查概述…103
12.4 语言支持…103
12.4.1 C 和 C++…104
12.4.1.1 C 和 C++操作符…104
12.4.1.2 C 和 C++常量…105
12.4.1.3 C++表达式…106
12.4.1.4 C 和 C++缺省值…107
12.4.1.5 C 和 C++类型和域检查…107
12.4.1.6 GDB 和 C…107
12.4.1.7 GDB 的 C++功能…107
12.4.1.8 十进制浮点格式…108
12.4.2 Objective-C …109
12.4.2.1 命令里的方法名…109
12.4.2.2 和 Objective-C 协作的 Print 命令…110
12.4.3 Fortran…110
12.4.3.1 Fortran 操作符和表达式…110
12.4.3.2 Fortran 的缺省值…110
12.4.3.3 Fortran 的特殊命令…110
12.4.4 Pascal …111
12.4.5 Modula-2…111
12.4.5.1 操作符…111
12.4.5.2 内建函数和过程…112
12.4.5.3 常量…113
12.4.5.4 Modula-2 类型…113
12.4.5.5 Modula-2 的缺省设置…115
12.4.5.6 与标准 Modula-2 的差异…116
12.4.5.7 Modula-2 类型和域检查…116
12.4.5.8 范围操作符::和…116
12.4.5.9 GDB 和 Modula-2…117
12.4.6 Ada …117
12.4.6.1 介绍…117
12.4.6.2 Ada 里的遗漏 …118
12.4.6.3 对 Ada 的扩展…119
12.4.6.4 在开头处停止…120
12.4.6.5…120
12.5 未支持的语言…120
第十三章 查看符号表…121
第十四章 改变执行…125
14.1 给变量赋值…126
14.2 在不同的位置上继续执行…127
14.3 为程序设置信号…127
14.4 从函数里返回…128
14.5 调用程序函数…128
14.6 为程序打补丁…129
第十五章 GDB 文件…129
15.1 设置文件的命令…129
15.2 调试信息位于不同文件中…135
15.3 读取符号文件的错误…138
第十六章 设置调试目标…139
16.1 有效目标…140
16.2 管理目标的命令…140
16.3 选择目标字节序…142
第十七章 调试远程程序…143
17.1 连接到远程目标…143
17.2 给远程系统发送文件…144
17.3 使用 gdbserver 程序…145
17.3.1 运行 gdbserver…145
17.3.1.1 附着到运行着的程序…146
17.3.1.2 gdbserver 的多进程模式…146
17.3.1.3 其它 gdbserver 命令行参数…146
17.3.2 连接 gdbserver…147
17.3.3 gdbserver 的监视命令…147
17.4 远程配置…147
17.5 实现远程代理…149
17.5.1 代理能为你做什么…150
17.5.2 你必须为代理做什么…151
17.5.3 集成…152
第十八章 配置相关的信息…152
18.1 本地…153
18.1.1 HP-UX …153
18.1.2 BSD libkvm 接口…153
18.1.3 SVR4 进程信息…153
18.1.4 调试 DJGPP 程序的功能…154
前言
翻译:shyboysby.spaces.live.com
本翻译遵从 GPL。参见:
gdb is free software, protected by the gnu General Public License (GPL). The GPL gives
you the freedom to copy or adapt a licensed program—but every person getting a copy also
gets with it the freedom to modify that copy (which means that they must get access to the
source code), and the freedom to distribute further copies. Typical software companies use
copyrights to limit your freedoms; the Free Software Foundation uses the GPL to preserve
these freedoms.
Fundamentally, the General Public License is a license which says that you have these
freedoms and that you cannot take these freedoms away from anyone else.
欢迎转载(请注明出处),但不允许用以商业赢利。本翻译保留相应权利。
自由软件需要自由文档。
自由属于人民。
第一章:一个 GDB 会话样例
1 一个 GDB 会话样例
你可以随意用这部手册来了解有关 GDB 的一切。然而,一些趁手的命令就足以开始使用
调试器。这一章介绍了这些命令。
在这个简单的会话里,我们强调用户输入用黑体来显示,这样可以和环境输出明确的区分
开来。
GNU m4(通用宏处理器)的以前版本有以下的一个 bug:有时候,在我们改变了宏默认的
引号字符串的时候,用来在别的宏里捕获
宏定义的命令就失效了。在接下来简短的 m4 例子里,我们定义了一个展开是“0000”的宏
foo;我们接着用 m4 内建的 defn 来定义宏 bar,bar 的
值也是“0000”。然而,在我们用来替代开引号字符和用替代闭引号
字符的后,定义一个同义词 baz 的相同的过程却失败了。
baz:
$ cd gnu/m4
$ ./m4
define(foo,0000)
foo
0000
define(bar,defn(‘foo’))
bar
0000
changequote(,)
define(baz,defn(foo))
baz
Ctrl-d
m4: End of input: 0: fatal error: EOF in string
让我们试着用 GDB 来看看发生了什么。
$ gdb m4
gdb is free software and you are welcome to 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.
gdb 6.8.50.20080307, Copyright 1999 Free Software Foundation, Inc…
(gdb)
GDB 只是读入仅够在有需要的时候用来发现哪里能够找到后续内容的数据;这将是 GDB
的第一个提示很开出现。现在我们
让 GDB 用一个比通常窄的显示区域,这样可以让本书的例子显示的更好。
(gdb) set width 70
我们需要探查 m4 内建函数 changequote 是如何工作的。因为已经看过了源代码,我们知道
相关的子函数是 m4_changequote,所以我们
用 GDB break 命令在这个函数上设置一个断点。
(gdb) break m4 changequote
Breakpoint 1 at 0x62f4: file builtin.c, line 879.
用 run 命令,m4 就在 GDB 的控制下运行了。只要还没有运行到 m4_changequote 子函数,
程序就如同往常一样运行:
(gdb) run
Starting program: /work/Editorial/gdb/gnu/m4/m4
define(foo,0000)
foo
0000
为了出发断点,我们要调用 changequote.GDB 挂起 m4,显示出在它停止执行处的上下文
信息。
changequote(,)
Breakpoint 1, m4_changequote (argc=3, argv=0x33c70)
at builtin.c:879
879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3))
现在我们可以用命令 n 接着运行当前函数的下一行指令。
(gdb) n
882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\
: nil,
set_quotes 看上去像有可能失败的子函数。为了进入子函数,我们可以用命令 s(step)而不
是用 next。step 进入被调用的
子函数的第一行,所以 step 进入 set_qoutes.
(gdb) s
set_quotes (lq=0x34c78 “”, rq=0x34c88 “”)
at input.c:530
530 if (lquote != def_lquote)
这些输出显示了在 m4 挂起时的调用函数栈。它显示了栈的概要。我们可以用命令
backtrace(也可以用缩写 bt)来看当前在哪里:
backtrace 命令显示了每一个活动子程序的栈。
gdb) bt
#0 set_quotes (lq=0x34c78 “”, rq=0x34c88 “”)
at input.c:530
#1 0x6344 in m4_changequote (argc=3, argv=0x33c70)
at builtin.c:882
#2 0x8174 in expand_macro (sym=0x33320) at macro.c:242
#3 0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30)
at macro.c:71
#4 0x79dc in expand_input () at macro.c:40
#5 0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195
我们 step 几行来观察会发生什么。在开始的两次为了避免进入 xstrdup 子程序,我们用’s’;接
下来的两次我们用’n’。
(gdb) s
0x3b5c 532 if (rquote != def_rquote)
(gdb) s
0x3b80 535 lquote = (lq == nil || *lq == ’\0’) ? \
def_lquote : xstrdup(lq);
(gdb) n
536 rquote = (rq == nil || *rq == ’\0’) ? def_rquote\
: xstrdup(rq);
(gdb) n
538 len_lquote = strlen(rquote);
最后一行输出看上去有点怪异;我们可以检查变量 lquote 和 rquote 的值来看看它们是否
是真的是我们所期待的新的
左右引号。我们用命令 p(print)
(gdb) p lquote
$1 = 0x35d40 “”
(gdb) p rquote
$2 = 0x35d50 “”
lquote 和 rquote 确实是新的左右引号。为了查看一些相关的信息,我们可以用命令 l(list)显
示当前代码附近的 10 行代码。
(gdb) l
533 xfree(rquote);
534
535 lquote = (lq == nil || *lq == ’\0’) ? def_lquote\
: xstrdup (lq);
536 rquote = (rq == nil || *rq == ’\0’) ? def_rquote\
: xstrdup (rq);
537
538 len_lquote = strlen(rquote);
539 len_rquote = strlen(lquote);
540 }
541
542 void
让我们 step 两行(设置 len_lquote 和 len_rquote),然后检查这两个变量的值。
(gdb) n
539 len_rquote = strlen(lquote);
(gdb) n
540 }
(gdb) p len lquote
$3 = 9
(gdb) p len rquote
$4 = 7
假设 len_lqoute 和 len_rquote 分别代表 lqoute 和 rquote 的长度,很显然这个结果是错误的,
我们可以用命令 p 来设置
较合理的值,p 命令不仅可以打印表达式的值,而且表达式也可以调用子函数,也可以给表
达式赋值。
(gdb) p len lquote=strlen(lquote)
$5 = 7
(gdb) p len rquote=strlen(rquote)
$6 = 9
是不是这样就足以修正 m4 内建的 defn 关于使用新的引号的错误了呢?我们可以用命令 c 让 m4 继续执行,接着试这个刚刚过引起问题的例子:
(gdb) c
Continuing.
define(baz,defn(foo))
baz
0000
成功了!这个新的引用现在和默认的一样正确了。这个问题看来是两个形参搞错了。我们输
入一个 EOF 让 m4 退出:
Ctrl-d
Program exited normally.
消息’Program exited normally.'是 GDB 输出的;它显示了 m4 已经执行完了。我们可以用 quit
命令来结束 GDB 会话。
(gdb) quit
第二章:进入和离开 GDB
这章讨论了如何开始和离开 GDB。
提要:

  1. 输入’gdb’开始 GDB
  2. 输入 quit or Ctrl-d 来退出
    2.1 调用 GDB
    运行 gdb 程序调用 GDB。一旦开始执行,GDB 会一直从终端读入命令,直到你告诉它结
    束为止。
    在需要制定一些调试环境的时候,你也可以在开始的时候就用可变长参数和选项来运行
    GDB。
    命令行参数描述了 GDB 可以适合多种情况;在某些环境下,某些选项不可以用。
    最常用的启动 GDB 的方式是使用一个制定要调试程序的参数:
    gdb program
    你也可以用可执行程序和 core 文件来启动:
    gdb program core
    如果你要调试一个进程,你可以用用进程 ID 来替代第二个参数:
    gdb program 1234
    这样可以 attach GDB 到进程 1234(除非你同时有一个文件叫“1234”,GDB 首先会检查
    是否有一个 core 文件)。
    要利用第二个命令行参数需要操作系统的支持;当你用 GDB 作为远程调试器去 attach 到
    一个裸机上时,很有可能
    得不到任何有关进程的信息,也不大可能得到 core dump 文件。如果不能 attach 到进程或读
    入 core dump 文件,GDB 会
    给出警告。 你可以用参数–args 来让 gdb 传递参数给被调试的可执行程序.这个选项可以停
    止参数的执行。
    gdb –args gcc -O2 -c foo.c
    这将让 gdb 调试 gcc, 设置 gcc 的命令行参数(参见 4.3 节[参数],27 页)为‘-O2 -c foo.c’.
    你可以运行 gdb 而不输出开头信息,开头信息描述了 GDB 的非保障性,用-silent 参数指
    定:
    gdb -silent
    你可以用命令行参数来进一步控制 GDB 的启动。GDB 本身可以提示参数信息。
    输入
    gdb -help
    来显示所有可选项和简短参数用法描述(’gdb -h’是对等的缩写)。
    所有的选项和命令行参数将以先后顺序处理。’-x’选项将会改变一规则。
    2.1.1 选择文件
    当 GDB 启动时它读入选项和参数,参数是固定指定为可执行程序和 core 文件的(或者
    进程 ID)。这和分别用选项
    ‘-se’和’-c’(‘-p’)指定的参数一样。(GDB 读入的第一个没有选项相联系的参数将和
    用’-se’选项相关的参数一样;如果有,
    第二个没有选项相关的参数和用’-c’/‘p’选项相关的参数一样)如果第二个参数用十进制
    数表示,GDB 首先会尝试着
    attach 到一个进程,如果失败了,GDB 会尝试着将它作为 core 文件打开。如果你有一个用
    十进制数字命名的 core 文件,
    你可以用前缀’./’(例如‘./12345′)来防止 GDB 将它误作为进程 ID.
    如果 GDB 没有配置为支持 core 文件,就像大多数嵌入式目标一样,那么 GDB 将会视第
    二个参数为多余而忽略它。
    很多选项都有长短形式;长短形式都将在下表中列出。如果你截断了长选项,只要这个
    选项参数足以明确的表达,
    GDB 也可以认识。(如果你喜欢,你可以用’–’标记选项参数而不是我们更都使用的’-’)
    -symbols file
    -s file 从 file 读入符号表.
    -exec file
    -e file 用文件 file 作为可执行文件来执行,或者在和 core dump 连接的时候用来检查出数据.
    -se file 从文件中读入符号表,而且将文件作为可执行程序.
    -core file
    -c 文件 file 将作为 core dump 来检查.
    -pid number
    -p number 连接到以 pid 为 number 的进程,作为命令 attach 的参数.
    -command file
    -x file 从文件 file 里执行 GDB 命令.参见 20.3 节[命令文件,221 页]
    -eval-command command
    -ex command 执行单一的 GDB 命令。这个选项可以多次调用来执行多个命令。它也可以用’
    -command’交叉.
    gdb -ex ’target sim’ -ex ’load’ \
    -x setbreakpoints -ex ’run’ a.out
    -directory directory
    -d directory
    加入路径 directory 作为源代码和脚本文件的搜索路径.
    -r
    -readnow 立即读入每一个符号文件的符号表,而不是默认的那种在需要时才渐次读入的方
    式。这将是初始阶段慢一点,
    而以后执行将更快。
    2.1.2 选择模式
    你可以用多种模式运行 GDB–例如,批处理模式和安静模式。
    -nx
    -n 不执行任何初始化文件里的命令。通常,在处理完所有的命令选项和参数之后,GDB
    会执行这些文件里的命令。
    -quiet
    -silent
    -q “安静”。不打印介绍和版权信息。在批处理模式下这些信息也不打印。
    -batch 以批处理模式运行。处理完所有命令文件(用’-x’指定)后以 0 状态退出(如果
    没有用‘-x’选项,需要执行完在
    初始化文件里的所有命令)。批处理模式在将 GDB 作为过滤器运行的时候很有用,例如下
    载和运行一个远程计算机
    上的程序;为了使这个模式更有用,信息‘Program exited normally.’将不输出(通常在 GDB
    控制下会输出的)
    -batch-silent
    类似于批处理模式运行,但是完全的安静。GDB 所有的输出到 stdout 的信息都将禁止
    (stderr 不受影响)。这个模式
    比’-silent’更安静而将是交互式的会话失效。这个模式在使用给出‘Loading section’信息
    的目标是特别有用。
    注意,通过 GDB 输出的那些目标也将变哑。
    -return-child-result
    GDB 的返回值是子进程(别调试的进程)的返回值,但是有以下的例外:
  3. GDB 异常退出。例如,由于不正确的参数或者内部错误。在此情况退出码和没有
    ‘-return-child-result’一样。
  4. 用户用明确的值退出。例如,’quit 1′
  5. 子进程没有运行,或者不可结束,这种情况下推出码是-1.
    这个选项在和’-batch’/'-batch-silent’联用,GDB 作为远程程序加载器或者仿真接口
    时很有用。
    -nowindows
    -nw
    “无窗口”。如果 GDB 内建图形用户接口,那么这个选项将让 GDB 只以命令行接口运
    行。如果 GUI 不可用,这个选项将
    不起效。
    -cd directory
    GDB 用 directory 作为它的工作目录,而不是当前目录
    -fullname
    -f GNU Emacs 在把 GDB 运行为子进程的时候设置这个选项。这个选项告诉 GDB 在每
    次栈显示的时候以标准且可识别
    的方式输出完整的文件名和行号(包括每次程序中断的时候)。可识别的形式看上去像两个’
    \032′字符开始,接下来是
    文件名,行号和字符位置和新行,他们用冒号分隔。Emacs-to-gdb 接口程序用两个’\032′
    字符作为信号来在一帧上显示源
    代码。
    -epoch Epoch在运行GDB作为子进程时Epoch Emacs-GDB接口设置这个选项.它让GDB
    修改打印例程以此来让 Epoch 在单
    独的窗口里显示表达式的值。
    -annotate level
    这个选项设置 GDB 的注释级别。这个选项和‘set annotate level’命令相同(参见 25
    张[注释],291 页)。注释级别控制着
    GDB 打印多少信息,提示,表达式的值,代码行和其它种类的输出。通常是 0 级,1 级为
    GNU Emacs 运行 GDB 而用,3 级
    是给控制 GDB 的程序的最高可用级别,2 级已经不再使用了。GDB/MI 可以极大的取代注
    释机制(参见第 24 章[GDB/MI],
    235 页)。
    –args 改变命令行的转译,一边把可执行文件参数后面的参数传递给它。这个选项将阻
    止选项的处理。
    -baud bps
    -b bps 设置被 GDB 用来远程调试的串口行速率(波特率或者 bits/每秒 )。
    -l timeout
    设置被 GDB 用来远程调试的链接超时(秒)。
    -tty device
    -t device
    将设备作为你的程序的标准输入输出。
    -tui 在启动时激活文本用户接口。文本用户接口在终端上管理多种文本窗口,用来显示
    代码,汇编,寄存器和 GDB 命令的
    输出(参见第 22 章[GDB 文本用户接口],227 页)。作为选择,文本用户接口可以用程序’
    gdbtui’激活。如果你在 Emacs 时不要
    使用这个选项(参见第 23 张[在 GNU Emacs 里使用 GDB])。
    -interpreter interp
    把解释器 interp 作为控制程序或设备的接口。这个选项由和 GDB 通讯的程序设置,并
    以此作为后台的。参见第 21 张[命令解
    释器],225 页。
    从 GDB6.0 版以后’–interpreter=mi’(或者’–interpreter=mi2′)导致 GDB 使用 GDB/MI
    接口(参见第 24 章[GDB/MI 接口],235 页)。
    以前的 GDB/MI 接口,包括 GDB5.3 版本和选择了‘–interpreter=mi1’都已经废止了。更
    早的 GDB?MI 接口也不再支持了。
    -write 以可读可写的方式打开可执行程序和 core 文件。和‘set write on’命令相同。(参
    见 14.6 节[补丁],152 页)
    -statistics
    在每次完成命令和回到提示符的时候,此选项可让 GDB 打印时间和内存使用统计信息。
    -version
    此选项可让 GDB 打印版本号和非保障性声明然后退出。
    2.1.3 GDB 在启动阶段的活动
    下文描述了 GDB 在启动阶段时的活动:
  6. 启动命令行解释器(由命令行制定)(参见 2.1.2 节[模式选项],13 页)
  7. 读入在你的 home 目录下的初始化文件(如果有的话)然后执行里面的所有命令。
  8. 处理命令行选项和参数。
    4 读入和执行在当前工作目录下的初始话文件(如果有的话)里的命令。只有在当前目录和
    你的 home 目录不同时才会执行。
    因此,在你启动 GDB 的目录下你可以有不止一个的初始化文件。
  9. 读入命令文件(用’-x’选项指定)。更多详细信息请请参见 20.3 节[命令文件],221 页。
  10. 读入记录在历史文件里的命令历史。更多详细信息请参见 19.3 节[命令历史]。
    初始化文件和命令文件使用相同的语法,并且 GDB 用相同的方式处理它们。你的 home
    目录下初始化文件可以设置选项(
    像’set complaints’), 这样可以影响此后的命令行选项和参数的处理。如果你用了’-nx’
    选项,初始化文件将不会被执行(参见 2.1.2 节 [选择模式],13 页)。
    GDB 初始化文件通常称为’.gdbinit’. 由于 DOS 文件系统的文件名限制,GDB DJGPP
    口使用’gdb.ini’这个名字。GDB 的 Windows
    口使用标准名称,但是如果发现’gdb.ini’文件,它会警告你并建议你重命名为标准名称。
    2.2 退出 GDB
    quit [expression]
    q 要退出 GDB,可以用 quit 命令(缩写为 q),或者敲入文件结束符(通常是 Ctrl-d).如果你
    不提供表达式,GDB 会正常结束;否则 GDB
    会用表达式的结果作为错误码结束。
    中断(常常是 Ctrl-c)并不从 GDB 里退出,而是结束正在处理中的 GDB 命令然后回到 GDB
    命令级。任何时候敲入中断都是安全的,
    因为 GDB 知道安全的时候才会让这个中断起效。
    如果你用 GDB attach 过一个进程,你可以用 detach 命令释放进程(参见 4.7 节[调试已经
    运行的进程],30 页)。
    2.3 Shell 命令
    在调试期间,如果你需要偶尔执行 shell 命令,不需要离开或者刮起 GDB;你可以直接使用
    shell 命令。
    shell command string
    启动标准 shell 执行 command string.如果环境变量 SHELL 存在,环境变量 SHELL 决定
    哪一个 shell 来运行。否则 GDB 将用默认的 shell(
    Unix 系统’/bin/sh’,MS-DOS 用’COMMAND.COM’).
    在编译环境里 make 工具通常都是必需的。在 GDB 里你不需要用 shell 命令调用 make:
    make make-args
    用指定参数执行 make 程序。和’shell make make-args’相同。
    2.4 日志输出
    你可能想要保存 GDB 命令的输出到一个文件里。有多个命令可以控制 GDB 的日志。
    set logging on
    激活日志功能.
    set logging off
    关闭日志功能.
    set logging file file
    改变当前的 logfile 名字 l. 默认的 logfile 是‘gdb.txt’.
    set logging overwrite [on|off]
    默认的,gdb 会以附加的方式保存日志。如果你想改为覆盖方式保存的话,可以设置为
    覆盖方式。
    set logging redirect [on|off]
    默认的,gdb 输出会打印到终端和 logfile。可以将终端重定向到 logfile 里,如果你只要
    它输出到 logfile 里。
    show logging
    显示当前日志设置
    第三章 GDB 命令
    假如缩写是无歧义的话,你可以将一个 GDB 命令缩写为开头的几个字母;你也可以用回车
    键来重复一些 GDB 命令。你也可以
    用 TAP 键来让 GDB 补全一个命令的剩余部分(或者告诉你可供选择的命令,假如不止一个
    命令可选的话)。
    3.1 命令语法
    一个 GDB 命令是单独的输入行。没有长度限制。命令由一个命令名开始,接着是提供给
    命令的参数。例如,命令 step 接收
    一个代表步长的参数,就像”step 5″.你也可以用不带参数的 step 命令。某些命令不允许参
    数。
    GDB 命令名总是在没有歧义的情况下允许截短。在某些情况下,即使是有歧义的缩写也
    是允许的;比如,s 是特别为 step 而定义的缩写,即使有其他的命令也是以 s 开头。你可以
    用这些缩写作为 help 命令的参数测试他们。
    一个空白行的输入(敲入回车键)对 GDB 而言意味着重复此前的命令。有些命令(例如
    run)不能用这种方式重复;这些命令不经意的重复可能导致麻烦或者你不大希望重复他们。
    用户定义命令可以关闭这些 feature;参见 20.1.1 节[定义],227 页。
    list 和 x 命令,在你用回车键重复他们的时候,会建构新的而不是重复此前输入的参数。
    这个特性可以很便捷扫描代码和内存。
    GDB 也可以以另外一种方式使用回车键:和通用工具 more 相似的方式来区分长输出(参
    见 19.4 节[屏幕大小],219 页)。因为在这种情况下很容易按下过多的回车键,在产生长输出
    时 GDB 关闭命令重复的功能。
    从#开始到行结束的文本都是注释;这些文本什么也不干。他们主要是在命令文件里起帮
    助理解的作用(参见 20.1.3 节[命令文件],229 页)。
    Ctrl-o 绑定对于重复复杂的命令序列很有帮助。这个命令接受一个当前行,例如一个回车,
    接着从命令历史里取得相对于当前行的下一行来编辑。
    3.2 命令补全
    GDB 可以为你补全命令的剩余部分,如果有且只有一个可能的命令;它也可以在任何时
    间为你显示一个命令里的下一个词的有效可能值。命令补全功能对 GDB 命令,子命令和你
    的程序里的符号都有效。
    无论何时你想要 GDB 补全一个单词的时候,按下 TAB 键就可以了。如果只有一个可能,
    GDB 会补全这个词,接着等待你去完成这个命令(按下回车键)。例如,如果你敲入
    (gdb) info bre
    GDB 补全’breakpoints’的剩余部分,因为只有 info 子命令以’bre’开头:
    (gdb) info breakpoints
    现 在你可以敲入回车键来运行 info breakpoints 命令;假如’breakpoints’看上去不像你期
    待的,你可以用回退键删除之,然后敲入别的。,假如 ‘breakpoints’看上去不像你期待的。
    (如果在开头你就确信你要的就是 info breakpoints,你就可以用缩写的形式来立即回车运
    行’info bre’,而不必等命令补全再回车)。
    如果在你按下 TAB 键的时候有过个候选项的话,GDB 会发出一个铃声。你可以多敲入几
    个字符后再试一下,或者再按一次 TAB 键 ;GDB 会为你显示所有可能补全的候选项。例如,你可能想要在一个名字开头是’make_’
    子函数里设置一个断点,而在你敲入 b make_的时候,GDB 会发出一声响。再次敲入
    键会显示所有以 make_开头的函数,例如:
    (gdb) b make_
    gdb sounds bell; press hTABi again, to see:
    make_a_section_from_file make_environ
    make_abs_section make_function_type
    make_blockvector make_pointer_type
    make_cleanup make_reference_type
    make_command make_symbol_completion_list
    (gdb) b make_
    显示完所有可能的候选项之后,GDB 会复制你刚才的输入(在这个例子里是’b make_’)
    以便你完成这个命令。
    如果你只是想要在开始的时候看看候选列表,你可以按下 M-?而不是按下两次。
    M-?是?.你可以在敲入?的时候按住
    键(假如键盘上有这个键的话),假如没有这个键,你可以按下再按下?来代
    替。
    有时候你需要的字符串可能含有圆括号,或者 GDB 认为这个字符串是不一个字。为了让
    补全功能在这种情况下生效,你可以用’
    (单括号)封起来。
    这种情况最有可能出现在你敲入一个 C++函数名的时候。这是因为 C++允许函数重载(同
    一个函数名多次定义,以参数类型来区分)。例如,在一个名为 name 的函数设置断点的时
    候,你需要区分是在参数为 int 的函数 name 上还是参数为 float 的函数 name 设置断点的。
    为了在这时用词补全功能,在 函数名之前敲入一个单引号’。这样 GDB 就可以知道需要考
    虑比通常只按下或者 M-?更多的信息:
    (gdb) b ?ˉbubble( M-?
    bubble(double,double) bubble(int,int)
    (gdb) b ?ˉbubble(
    在某些需要补全的情况下,GDB 可以提示你需要引号。这时,如果你开始的时候没有敲
    入引号,GDB 会为你插入一个引号:
    (gdb) b bub
    GDB 会以下面的输出提醒你,然后响一声:
    (gdb) b ’bubble(
    通常的,在有重载符号情况下,在你还没有开始敲入参数列表的时候就用补全功能的时候,
    GDB 提示需要一个引号然后插入它。
    更多有关重载函数信息,参见 12.4.1.3 节[C++表达式],126 页。你可以用 set overload-resolution
    off 命令关闭重载解决方案,参见 12.4.1.7 节,[GDB 的 C++功能],128 页。
    3.3 帮助
    用 help 功能,你可以获得 GDB 的命令信息。
    help
    h 你可以用 help(缩写 h)不带参数来显示一个命令分类的简短列表。
    (gdb) help
    List of classes of commands:
    aliases — Aliases of other commands
    breakpoints — Making program stop at certain points
    data — Examining data
    files — Specifying and examining files
    internals — Maintenance commands
    obscure — Obscure features
    running — Running the program
    stack — Examining the stack
    status — Status inquiries
    support — Support facilities
    tracepoints — Tracing of program execution without
    stopping the program
    user-defined — User-defined commands
    Type “help” followed by a class name for a list of
    commands in that class.
    Type “help” followed by command name for full
    documentation.
    Command name abbreviations are allowed if unambiguous.
    (gdb)
    help class
    用 help 分类作为参数,你可以得到这个分类里命令列表。比如,下面是 status 分类的帮
    助显示:
    (gdb) help status
    Status inquiries.
    List of commands:
    info — Generic command for showing things
    about the program being debugged
    show — Generic command for showing things
    about the debugger
    Type “help” followed by command name for full
    documentation.
    Command name abbreviations are allowed if unambiguous.
    (gdb)
    help command
    用命令名作参数,GDB 会显示一段如何使用这个命令的信息。
    apropos args
    apropos 命令会在命令和文档里文档搜索这个 args 指定的正则表达式。这个命令会打印
    所有符合的结果。例如:
    apropos reload
    结果:
    set symbol-reloading — Set dynamic symbol table reloading
    multiple times in one run
    show symbol-reloading — Show dynamic symbol table reloading
    multiple times in one run
    complete args
    complete args 命令列出所有可能的补全结果。用 args 指定你想要的命令的开头字母。例
    如:
    complete i
    结果:
    if
    ignore
    info
    inspect
    这个是为 GNU Emacs 设计的。
    更进一步的,你可以用 GDB 命令 info 和 show 来查询你程序的状态或者 GDB 本身的状态。
    这两个命令都支持多个主题的查询;这本手册会在恰当的时候介绍这两个命令。索引里的
    info 和 show 下的列表列出了所有的子命令。参见[索引],407 页。
    info 这个命令(缩写 i)可以描述程序的状态。例如,你可以用 info args 显示传递给函
    数的参数,用 info registers 来列出
    寄存器数据,用 info breakpoints 列出你设置的断点。你可以用 help info 来取得 info 的
    所有子命令。
    set 你可以用 set 命令把一个表达式的值来设置一个环境变量。例如,你可以用 set prompt
    来设置 G D B 提示符。 s h o w 和 i n f o 不同, s h o w 描述的 G D B 本身的状态。你可以用 s e t 命令改变大多数你可以用 s h o w 显示的内容。例如,你可以用 s e t r a d i x 来设置显示的数值进制系统,或者用 s h o w r a d i x 来显示数值进制。你可以用不带参数的 s h o w 命令来显示所有可以设置的参数和它们的值;你也可以用 i n f o s e t 。这两个命令是一样的。还有其余 3 种 s h o w 子命令,这 3 中命令缺乏对应的 s e t 命令: s h o w v e r s i o n 显示当前 G D B 的版本。你应该在 G D B b u g 报告中包含版本信息。如果你的机器上有多个版本的 G D B , 你可能需要知道哪个版本是你正在运行的;随着 G D B 的发展,新的命令会引入,而一些旧的将废弃。同时,许多系统供应商移植了不同版本的 G D B , 在 G N U / L i n u x 发行版也存在着多种版本的 G D B . 版本号和你启动时显示一样。 s h o w c o p y i n g i n f o c o p y i n g 显示 G D B 版权信息。 s h o w w a r r a n t y i n f o w a r r a n t y 显示 G N U 免责声明,或者保证(如果你的 G D B 版本有的话)。第四章在 G D B 里运行程序在你开始在 G D B 里运行程序前,你需要在编译的时候产生调试信息。你可以在你选定的环境里带参数(如果有的话)的启动 G D B 。如果你是在本地调试,你可以重定向输入输出,调试一个已运行的进程,或者结束一个进程。 4.1 为调试而编译为了有效的调试程序,你需要在编译的时候产生调试信息。调试信息存储在目标文件里;调试信息描述了数据和函数的类型,源代码和可执行代码的对应关系。编译时指定编译器的’ − g ’选项可以产生调试信息。在编译给你的客户发布的程序时,可以用’ − O ’选项指定编译器进行优化。然而,许多编译器不能同时处理’ − g ’和’ − o ’选项。如果用的是这些编译器,你得不到带有调试信息的优化过的可执行程序。 G C C , G N U C / C + + 编译器,带有或不带’ − O ’选项都可以用’ − g ’选项,因此可以让 G D B 调试优化过的代码。我们推荐你在编译程序时总是用’ − g ’。也许你认为你的程序是正确的,但决不要去碰运气。请记住在你调试一个用’ − g − o ’编译的程序时,优化器已经重排了你的代码;调试器显示的是真正编译成的代码。在执行路径和你的源代码不一致时,不要太惊讶!一个极端例子:如果定义了一个变量,但从来也没用过, G D B 发现不了它— − 因为优化器已经把它优化掉了。用’ − g − o ’和只用’ − g ’编译的程序有时候不大一样,特别是在有些具有指令调度的机器上。如有疑问,再用只带’ − g ’编译,如果这个版本修正了问题,请给我们发送一个报告(包含一个测试用例)。更多调试优化过的代码,参见 8.2 节 [ 变量 ] , 76 页。较早前的 G N U C 编译器允许一个’ − g g ’变体选项来产生调试信息。 G D B 不再支持这个格式;如果你的 G N U C 编译器有这个选项,别再用了。 G D B 知道预编译宏,可以显示宏的展开式(参见第九章 [ 宏 ] , 101 页)。由于’ − g ’选项产生的调试信息过多,大多数编译器不会产生与编译宏的信息。 G C C 3.1 版本以及此后的可以提供宏信息,如果在编译的时候指定了’ − g d w a r f − 2 ′和’ − g 3 ′选项 ; 前一个选项产生 D w a r f 2 格式的调试信息,后者产生”额外信息”。我们希望在将来能够找到一个精简的方式来表示宏信息,这样只要一个’ − g ’选项就可以了。 4.2 开始程序 r u n r 在 G D B 里用 r u n 命令开始你的程序。你在启动 G D B 时指定要调试的程序名( V x W o r k s 例外)(参见第二章 [ 进入和离开 G D B ] ),或者用 f i l e 或者 e x e c − f i l e 命令(参见 15.1 节 [ 指定文件的命令 ] , 155 页)。如果你是在一个支持多进程的环境里运行 G D B 的话, r u n 命令创建一个子进程来运行你的程序。在某系不支持多进程的环境下, r u n 跳到被调试程序的开头。其他的目标,比如’ r e m o t e ’ , 总是在运行的。如果你的到一个类似如下的错误信息: T h e “ r e m o t e ” t a r g e t d o e s n o t s u p p o r t “ r u n ” . T r y “ h e l p t a r g e t ” o r “ c o n t i n u e ” . 接着用’ c o n t i n u e ’继续运行你的程序。你可能需要先 l o a d (参见 [ 加载 ] , 169 页)。程序的执行总会受某些从它的上级那里得到的信息的影响。 G D B 可以指定那些虚啊哟你在启动程序之前就要设置的信息(你可以在启动程序之后改变,但是只有在下一次运行的时候才起效)。这些信息可以划分为 4 类: T h e a r g u m e n t s . 将你的程序的参数作为 r u n 命令的参数。如果在你的环境里有 s h e l l , s h e l l 可以用来传递参数,那样的话你就可以用普通的方式(比如 w i l d c a r d 展开和变量替换)来描述参数了。在 U n i x 系统里,你可以用环境变量 S H E L L 来控制使用那个 s h e l l 。参见 4.3 节 [ 程序参数 ] , 27 页。 T h e e n v i r o n m e n t . 你的程序会从 G D B 里继承环境变量,而你也可以用 G D B 命令 s e t e n v i r o n m e n t 和 u n s e t e n v i r o n m e n t 改变某些影响你的程序的环境变量。参见 4.4 节 [ 程序的环境 ] , 28 页。 T h e w o r k i n g d i r e c t o r y . 你的程序从 G D B 里继承工作目录。你可以用 G D B 命令 c d 来改变工作目录。参见 4.5 节 [ 程序的工作目录 ] , 29 页。 T h e s t a n d a r d i n p u t a n d o u t p u t . 你的程序使用和 G D B 一样的标准输入输出。你可以在 r u n 命令行里重定向输入和输出,或者你可以用 t t y 命令来为你的程序设置不同的设备。参见 4.6 节 [ 程序的输入输出 ] , 29 页。警告:输入输出重定向起效之后,你不能再用管道将程序的产生的输出传递到另外一个程序里去;如果你要这样试的话, G D B 很有可能结束调试这个错误的程序。在你执行 r u n 命令后,你的程序马上就开始执行。参见第五章 [ 停止和继续 ] , 39 页,那里讨论了如何筹划中断你的程序。一旦你的程序中断下来,你就可以在你的程序里调用函数,用 p r i n t 或者 c a l l 命令。参见第八章 [ 检验数据 ] , 75 页。如果在最近一次 G D B 读入符号表之后符号文件的修改时间发生了改变, G D B 会丢弃现有的符号表然后重新读入。重新读入符号表的时候, G D B 会试图保留你当前的断点设置。 s t a r t 不同的语言可能有不同的主函数的名称。对于 C / C + + 来说,主函数的名称一直都是” m a i n ” , 而有些语言例如 A d a 就不需要为他们的主函数指定一个特定的名称。依赖于编程语言,调试器可以方便的开始执行程序并且在主函数的入口点中断。’ s t a r t ’命令的功能和在主函数入口点里设置一个临时断点后执行’ r u n ’命令相当。有些程序包含一个在主程序之前执行的加工期,加工期会执行一些启动代码。这依赖于你使用哪种语言写程序。例如, C + + , 构造函数会在 m a i n 之前为静态和全局变量调用。因此调试器有可能在主函数之前就中断程序。而临时断点还会保留来中断程序的执行。’ s t a r t ’的参数将会传递给你的程序。这些参数会以字符形式给’ r u n ’命令。注意要是下次不带参数调用’ s t a r t ’或‘ r u n ’的时候,这些参数将会被重用。有些时候必须在加工期调试程序。这样的话, s t a r t 命令在主函数入口点的中断就太迟了,唯一此时已经过了加工期。在这种情况下,在运行情在加工期代码上设置断点就可以了。 4.3 程序参数参数可以藉由 r u n 命令的参数来指定。参数由 s h e l l 传递给你的程序, s h e l l 会扩展通配符和执行重定向。你的 S H E L L 环境变量(如果有的话)会决定 G D B 使用哪种 s h e l l . 如果你没有定义 S H E L L 的话, G D B 使用默认的 s h e l l ( U n i x 下’ b i n / s h ’ ) 。在非 U n i x 系统里,程序通常都是由 G D B 直接调用的, G D B 会用相应的系统调用来模拟 I / O 重定向,通配符的宽展由程序的加工期代码完成,不是由 s h e l l 来做的。不带参数的 r u n 命令使用前一次的 r u n 命令的参数,或者由 s e t a r g s 命令设置的参数。 s e t a r g s 为你的下一次执行程序设置参数。如果 s e t a r g s 不带参数的话, r u n 就不带参数的执行程序。一旦你带参数的 r u n 程序的话,要想下一次不带参数的执行程序就只有用不带参数的 s e t a r g s 命令了。 s h o w a r g s 显示在启动的时候传递给程序的参数。 4.4 程序的环境环境由一系列的环境变量和环境变量的值的组合组成。环境变量可以很方便地记录一些东西,例如你的用户名, h o m e 目录,终端类型,程序执行的搜索路径等等。通常通过 s h e l l 来设置环境变量,这些变量就可以被别的程序继承了。这在调试的时候很有用, G D B 就不必重新启动来试验一个改变了的环境。 p a t h d i r e c t o r y 在 P A T H 环境变量的前头加上 d i r e t o r y ( 可执行程序的搜索路径 ) 。 G D B 用的 P A T H 将不会改变。你可以指定多个路径名称,用空格符或者系统依赖( U n i x ’ : ′ , M S D O S 、 M S − W i n d o w s ’ ; ′ )的分隔符类分割如果 d i r e c t o r y 已经在 P A T H 里了,这个 d i r e c t o r y 会移到 P A T H 的前面以加快搜索。在 G D B 搜索路径的时候,你可以用’ 来设置 GDB 提示符。 show 和 info 不同,show 描述的 GDB 本身的状态。你可以用 set 命令改变大多数你可以 用 show 显示的内容。例如,你可以用 set radix 来设置显示的数值进制系统,或者用 show radix 来显示数值进制。 你可以用不带参数的 show 命令来显示所有可以设置的参数和它们的值;你也可以用 info set。这两个命令是一样的。 还有其余 3 种 show 子命令,这 3 中命令缺乏对应的 set 命令: show version 显示当前 GDB 的版本。你应该在 GDB bug 报告中包含版本信息。如果你的机器上有多 个版本的 GDB,你可能需要知道哪个版 本是你正在运行的;随着 GDB 的发展,新的命令会引入,而一些旧的将废弃。同时, 许多系统供应商移植了不同版本的 GDB,在 GNU/Linux 发行版也存在着多种版本的 GDB.版本号和你启动时显示一样。 show copying info copying 显示 GDB 版权信息。 show warranty info warranty 显示 GNU 免责声明,或者保证(如果你的 GDB 版本有的话)。 第四章 在 GDB 里运行程序 在你开始在 GDB 里运行程序前,你需要在编译的时候产生调试信息。 你可以在你选定的环境里带参数(如果有的话)的启动 GDB。如果你是在本地调试,你 可以重定向输入输出,调试一个已运行 的进程,或者结束一个进程。 4.1 为调试而编译 为了有效的调试程序,你需要在编译的时候产生调试信息。调试信息存储在目标文件里; 调试信息描述了数据和函数的类型, 源代码和可执行代码的对应关系。 编译时指定编译器的’-g’选项可以产生调试信息。 在编译给你的客户发布的程序时,可以用’-O’选项指定编译器进行优化。然而,许多 编译器不能同时处理’-g’和’-o’选项。如果用的 是这些编译器,你得不到带有调试信息的优化过的可执行程序。 GCC,GNU C/C++编译器,带有或不带’-O’选项都可以用’-g’选项,因此可以让 GDB 调试优化过的代码。我们推荐你在编译程序时总 是用’-g’。也许你认为你的程序是正确的,但决不要去碰运气。 请记住在你调试一个用’-g -o’编译的程序时,优化器已经重排了你的代码;调试器显示 的是真正编译成的代码。在执行路径和你 的源代码不一致时,不要太惊讶!一个极端例子:如果定义了一个变量,但从来也没用过, GDB 发现不了它—-因为优化器已经把 它优化掉了。 用’-g -o’和只用’-g’编译的程序有时候不大一样,特别是在有些具有指令调度的机器 上。如有疑问,再用只带’-g’编译,如果这个版 本修正了问题,请给我们发送一个报告(包含一个测试用例)。更多调试优化过的代码,参 见 8.2 节[变量],76 页。 较早前的 GNU C 编译器允许一个’-gg’变体选项来产生调试信息。GDB 不再支持这个 格式;如果你的 GNU C 编译器有这个选项,别 再用了。 GDB 知道预编译宏,可以显示宏的展开式(参见第九章[宏],101 页)。由于’-g’选项产 生的调试信息过多,大多数编译器不会产生 与编译宏的信息。GCC3.1 版本以及此后的可以提供宏信息,如果在编译的时候指定了’ -gdwarf-2′和’-g3′选项;前一个选项产生 Dwarf 2 格式的调试信息,后者产生”额外信息”。我们希望在将来能够找到一个精简的方式来表 示宏信息,这样只要一个’-g’选项就可以了 。 4.2 开始程序 run r 在 GDB 里用 run 命令开始你的程序。你在启动 GDB 时指定要调试的程序名(VxWorks 例外)(参见第二章[进入和离开 GDB]), 或者用 file 或者 exec-file 命令(参见 15.1 节[指定文件的命令],155 页)。 如果你是在一个支持多进程的环境里运行 GDB 的话,run 命令创建一个子进程来运行你 的程序。在某系不支持多进程的环境下, run 跳到被调试程序的开头。其他的目标,比如’remote’,总是在运行的。如果你的到一个 类似如下的错误信息: The “remote” target does not support “run”. Try “help target” or “continue”. 接着用’continue’继续运行你的程序。你可能需要先 load(参见[加载],169 页)。 程序的执行总会受某些从它的上级那里得到的信息的影响。GDB 可以指定那些虚啊哟你 在启动程序之前就要设置的信息(你 可以在启动程序之后改变,但是只有在下一次运行的时候才起效)。这些信息可以划分为 4 类: The arguments. 将你的程序的参数作为 run 命令的参数。如果在你的环境里有 shell,shell 可以用来传 递参数,那样的话你就可以用普通的方式 (比如 wildcard 展开和变量替换)来描述参数了。在 Unix 系统里,你可以用环境变量 SHELL 来控制使用那个 shell。参见 4.3 节[程序 参数],27 页。 The environment. 你的程序会从 GDB 里继承环境变量,而你也可以用 GDB 命令 set environment 和 unset environment 改变某些影响你的程序的 环境变量。参见 4.4 节[程序的环境],28 页。 The working directory. 你的程序从 GDB 里继承工作目录。你可以用 GDB 命令 cd 来改变工作目录。参见 4.5 节[程序的工作目录],29 页。 The standard input and output. 你的程序使用和 GDB 一样的标准输入输出。你可以在 run 命令行里重定向输入和输出, 或者你可以用 tty 命令来为你的程序 设置不同的设备。参见 4.6 节[程序的输入输出],29 页。 警告:输入输出重定向起效之后,你不能再用管道将程序的产生的输出传递到另外一个 程序里去;如果你要这样试的话, GDB 很有可能结束调试这个错误的程序。 在你执行 run 命令后,你的程序马上就开始执行。参见第五章[停止和继续],39 页,那 里讨论了如何筹划中断你的程序。一 旦你的程序中断下来,你就可以在你的程序里调用函数,用 print 或者 call 命令。参见第 八章[检验数据],75 页。 如果在最近一次 GDB 读入符号表之后符号文件的修改时间发生了改变,GDB 会丢弃现 有的符号表然后重新读入。重新读入 符号表的时候,GDB 会试图保留你当前的断点设置。 start 不同的语言可能有不同的主函数的名称。对于 C/C++来说,主函数的名称一直都是” main”,而有些语言例如 Ada 就不需 要为他们的主函数指定一个特定的名称。依赖于编程语言,调试器可以方便的开始执行 程序并且在主函数的入口点中 断。 ’start’命令的功能和在主函数入口点里设置一个临时断点后执行’run’命令相当。 有些程序包含一个在主程序之前执 行的加工期,加工期会执行一些启动代码。这依赖于你使用哪种语言写程序。例如, C++,构造函数会在 main 之前为静 态和全局变量调用。因此调试器有可能在主函数之前就中断程序。而临时断点还会保留 来中断程序的执行。 ’start’的参数将会传递给你的程序。这些参数会以字符形式给’run’命令。注意要 是下次不带参数调用’start’或 ‘run’的时候,这些参数将会被重用。 有些时候必须在加工期调试程序。这样的话,start 命令在主函数入口点的中断就太迟了, 唯一此时已经过了加工期 。在这种情况下,在运行情在加工期代码上设置断点就可以了。 4.3 程序参数 参数可以藉由 run 命令的参数来指定。参数由 shell 传递给你的程序,shell 会扩展通配符 和执行重定向。你的 SHELL 环境变量 (如果有的话)会决定 GDB 使用哪种 shell.如果你没有定义 SHELL 的话,GDB 使用默认的 shell(Unix 下’bin/sh’)。 在非 Unix 系统里,程序通常都是由 GDB 直接调用的,GDB 会用相应的系统调用来模拟 I/O 重定向,通配符的宽展由程序的加 工期代码完成,不是由 shell 来做的。 不带参数的 run 命令使用前一次的 run 命令的参数,或者由 set args 命令设置的参数。 set args 为你的下一次执行程序设置参数。如果 set args 不带参数的话,run 就不带参数的执行 程序。一旦你带参数的 run 程序 的话,要想下一次不带参数的执行程序就只有用不带参数的 set args 命令了。 show args 显示在启动的时候传递给程序的参数。 4.4 程序的环境 环境由一系列的环境变量和环境变量的值的组合组成。环境变量可以很方便地记录一些东 西,例如你的用户名, home 目录,终端类型,程序执行的搜索路径等等。通常通过 shell 来设置环境变量,这些变 量就可以被别的程序继 承了。这在调试的时候很有用,GDB 就不必重新启动来试验一个改变了的环境。 path directory 在 PATH 环境变量的前头加上 diretory(可执行程序的搜索路径)。GDB 用的 PATH 将不 会改变。你可以指定多个路径名 称,用空格符或者系统依赖(Unix’:',MS_DOS、MS-Windows’;')的分隔符类分割如 果 directory 已经在 PATH 里了, 这个 directory 会移到 PATH 的前面以加快搜索。 在 GDB 搜索路径的时候,你可以用’ 来设置GDB提示符。showinfo不同,show描述的GDB本身的状态。你可以用set命令改变大多数你可以用show显示的内容。例如,你可以用setradix来设置显示的数值进制系统,或者用showradix来显示数值进制。你可以用不带参数的show命令来显示所有可以设置的参数和它们的值;你也可以用infoset。这两个命令是一样的。还有其余3show子命令,这3中命令缺乏对应的set命令:showversion显示当前GDB的版本。你应该在GDBbug报告中包含版本信息。如果你的机器上有多个版本的GDB,你可能需要知道哪个版本是你正在运行的;随着GDB的发展,新的命令会引入,而一些旧的将废弃。同时,许多系统供应商移植了不同版本的GDB,GNU/Linux发行版也存在着多种版本的GDB.版本号和你启动时显示一样。showcopyinginfocopying显示GDB版权信息。showwarrantyinfowarranty显示GNU免责声明,或者保证(如果你的GDB版本有的话)。第四章在GDB里运行程序在你开始在GDB里运行程序前,你需要在编译的时候产生调试信息。你可以在你选定的环境里带参数(如果有的话)的启动GDB。如果你是在本地调试,你可以重定向输入输出,调试一个已运行的进程,或者结束一个进程。4.1为调试而编译为了有效的调试程序,你需要在编译的时候产生调试信息。调试信息存储在目标文件里;调试信息描述了数据和函数的类型,源代码和可执行代码的对应关系。编译时指定编译器的g选项可以产生调试信息。在编译给你的客户发布的程序时,可以用O选项指定编译器进行优化。然而,许多编译器不能同时处理go选项。如果用的是这些编译器,你得不到带有调试信息的优化过的可执行程序。GCC,GNUC/C++编译器,带有或不带O选项都可以用g选项,因此可以让GDB调试优化过的代码。我们推荐你在编译程序时总是用g。也许你认为你的程序是正确的,但决不要去碰运气。请记住在你调试一个用go编译的程序时,优化器已经重排了你的代码;调试器显示的是真正编译成的代码。在执行路径和你的源代码不一致时,不要太惊讶!一个极端例子:如果定义了一个变量,但从来也没用过,GDB发现不了它因为优化器已经把它优化掉了。用go和只用g编译的程序有时候不大一样,特别是在有些具有指令调度的机器上。如有疑问,再用只带g编译,如果这个版本修正了问题,请给我们发送一个报告(包含一个测试用例)。更多调试优化过的代码,参见8.2[变量],76页。较早前的GNUC编译器允许一个gg变体选项来产生调试信息。GDB不再支持这个格式;如果你的GNUC编译器有这个选项,别再用了。GDB知道预编译宏,可以显示宏的展开式(参见第九章[],101页)。由于g选项产生的调试信息过多,大多数编译器不会产生与编译宏的信息。GCC3.1版本以及此后的可以提供宏信息,如果在编译的时候指定了gdwarf2′g3′选项;前一个选项产生Dwarf2格式的调试信息,后者产生额外信息。我们希望在将来能够找到一个精简的方式来表示宏信息,这样只要一个g选项就可以了。4.2开始程序runrGDB里用run命令开始你的程序。你在启动GDB时指定要调试的程序名(VxWorks例外)(参见第二章[进入和离开GDB]),或者用file或者execfile命令(参见15.1[指定文件的命令]155页)。如果你是在一个支持多进程的环境里运行GDB的话,run命令创建一个子进程来运行你的程序。在某系不支持多进程的环境下,run跳到被调试程序的开头。其他的目标,比如remote,总是在运行的。如果你的到一个类似如下的错误信息:Theremotetargetdoesnotsupportrun”.Tryhelptargetorcontinue”.接着用continue继续运行你的程序。你可能需要先load(参见[加载]169页)。程序的执行总会受某些从它的上级那里得到的信息的影响。GDB可以指定那些虚啊哟你在启动程序之前就要设置的信息(你可以在启动程序之后改变,但是只有在下一次运行的时候才起效)。这些信息可以划分为4类:Thearguments.将你的程序的参数作为run命令的参数。如果在你的环境里有shellshell可以用来传递参数,那样的话你就可以用普通的方式(比如wildcard展开和变量替换)来描述参数了。在Unix系统里,你可以用环境变量SHELL来控制使用那个shell。参见4.3[程序参数]27页。Theenvironment.你的程序会从GDB里继承环境变量,而你也可以用GDB命令setenvironmentunsetenvironment改变某些影响你的程序的环境变量。参见4.4[程序的环境]28页。Theworkingdirectory.你的程序从GDB里继承工作目录。你可以用GDB命令cd来改变工作目录。参见4.5[程序的工作目录]29页。Thestandardinputandoutput.你的程序使用和GDB一样的标准输入输出。你可以在run命令行里重定向输入和输出,或者你可以用tty命令来为你的程序设置不同的设备。参见4.6[程序的输入输出]29页。警告:输入输出重定向起效之后,你不能再用管道将程序的产生的输出传递到另外一个程序里去;如果你要这样试的话,GDB很有可能结束调试这个错误的程序。在你执行run命令后,你的程序马上就开始执行。参见第五章[停止和继续]39页,那里讨论了如何筹划中断你的程序。一旦你的程序中断下来,你就可以在你的程序里调用函数,用print或者call命令。参见第八章[检验数据]75页。如果在最近一次GDB读入符号表之后符号文件的修改时间发生了改变,GDB会丢弃现有的符号表然后重新读入。重新读入符号表的时候,GDB会试图保留你当前的断点设置。start不同的语言可能有不同的主函数的名称。对于C/C++来说,主函数的名称一直都是main,而有些语言例如Ada就不需要为他们的主函数指定一个特定的名称。依赖于编程语言,调试器可以方便的开始执行程序并且在主函数的入口点中断。start命令的功能和在主函数入口点里设置一个临时断点后执行run命令相当。有些程序包含一个在主程序之前执行的加工期,加工期会执行一些启动代码。这依赖于你使用哪种语言写程序。例如,C++,构造函数会在main之前为静态和全局变量调用。因此调试器有可能在主函数之前就中断程序。而临时断点还会保留来中断程序的执行。start的参数将会传递给你的程序。这些参数会以字符形式给run命令。注意要是下次不带参数调用startrun的时候,这些参数将会被重用。有些时候必须在加工期调试程序。这样的话,start命令在主函数入口点的中断就太迟了,唯一此时已经过了加工期。在这种情况下,在运行情在加工期代码上设置断点就可以了。4.3程序参数参数可以藉由run命令的参数来指定。参数由shell传递给你的程序,shell会扩展通配符和执行重定向。你的SHELL环境变量(如果有的话)会决定GDB使用哪种shell.如果你没有定义SHELL的话,GDB使用默认的shell(Unixbin/sh)。在非Unix系统里,程序通常都是由GDB直接调用的,GDB会用相应的系统调用来模拟I/O重定向,通配符的宽展由程序的加工期代码完成,不是由shell来做的。不带参数的run命令使用前一次的run命令的参数,或者由setargs命令设置的参数。setargs为你的下一次执行程序设置参数。如果setargs不带参数的话,run就不带参数的执行程序。一旦你带参数的run程序的话,要想下一次不带参数的执行程序就只有用不带参数的setargs命令了。showargs显示在启动的时候传递给程序的参数。4.4程序的环境环境由一系列的环境变量和环境变量的值的组合组成。环境变量可以很方便地记录一些东西,例如你的用户名,home目录,终端类型,程序执行的搜索路径等等。通常通过shell来设置环境变量,这些变量就可以被别的程序继承了。这在调试的时候很有用,GDB就不必重新启动来试验一个改变了的环境。pathdirectoryPATH环境变量的前头加上diretory(可执行程序的搜索路径)GDB用的PATH将不会改变。你可以指定多个路径名称,用空格符或者系统依赖(Unix:,MSDOSMSWindows;)的分隔符类分割如果directory已经在PATH里了,这个directory会移到PATH的前面以加快搜索。在GDB搜索路径的时候,你可以用cwd’来代指当前工作目录。如果你用’.'代替,
    它代表在你执行 path 命令时的目
    录。GDB 在把路径加入 PATH 前用当前路径替代’.’.
    show paths
    显示可执行程序的搜索路径列表(PATH 环境变量)。
    show environment [varname]
    打印名为 varname 的环境变量的值。如果你没提供 varname,打印所有的环境变量的名
    称和值。你可以缩写
    environment 为 env.
    set environment varname [=value]
    设置环境变量 varname 的为 value.这个值只为你的程序改变,GDB 本身不改变。value
    可以使任意字符串;环境变量
    都是字符串,你的程序负责转译;如果被删除了,变量的值就被设置为 null 值。例如,
    命令:
    set env USER = foo
    告诉被调试的程序,下一次执行 run 的时候,它的用户是’foo’.(‘=’附近的空格是
    为了清楚些;空格不是必须的)
    unset environment varname
    删除传递给程序的环境变量。和‘set env varname =’不同,unset environment 是从环境
    变量里删除变量,而不是
    为它设置一个空值。
    警告 :在 Unix 系统,GDB 用 shell 执行程序,shell 由 SHELL 决定(如果有的话,否则用’
    /bin/sh’)。如 果 SHELL 环境变
    量指定的 shell 有初始化文件的话–例如 C-shell 的’.cshrc’,BASH 的’.bashrc’—这些文
    件里定义的变量都将影响你的程序。
    你也可以将那些只在登录时用到的变量移到别的文件里,例如’.login’或者’.profile’。
    4.5 程序的工作目录
    每次你用 run 启动程序时,程序都从 GDB 的当前工作目录继承工作目录。GDB 从它的父
    进程(通常是 shell)那里继承工作
    目录,而你可以用 cd 在 GDB 里指定一个新的工作目录。
    GDB 的工作目录是 GDB 操作文件时的默认目录。参加按 15.1 节[文件命令],155 页。
    cd directory
    指定 GDB 的工作目录为 directory
    pwd 打印 GDB 的工作目录
    通常很难找到被调试程序的当前工作路径(因为它可以在运行的时候改变)。如果你是在
    支持’/proc’文件系统的系统下
    运行 GDB 的,你可以用 info proc 命令来查找当前被调试程序的工作目录。(参见 18.1.3 节,
    [SVR4 进程信息],183 页)
    4.6 程序的输入输出
    缺省情况下,GDB 里运行的程序在与 GDB 相同的终端上输入输出。GDB 会在和你交互
    的时候切换到它自己的终端模式,
    不过它会记住你的程序的终端模式然后在继续运行程序切换到那个模式上。
    info terminal
    显示 GDB 记录的你的程序使用的终端模式。
    你可以用 run 命令重定向程序的输入/输出。例如:
    run > outfile
    开始运行程序,将打印输出到文件’outfile’。
    另外一个指定程序输入输出的命令是 tty 命令。这个命令接受一个文件名作为参数,然后
    将这个文件作为接下来的
    run 命令的缺省值。它也可以为子进程重置控制终端。例如:
    tty /dev/ttyb
    将接下来 run 命令运行的进程的输入输出定向到’/dev/ttyb’,并将此作为控制终端。
    run 命令将改变 tty 命令对于输入输出的设备的设置,但不改变其控制终端。
    用 tty 命令或者在 run 命令里重定向输入只会影响你调试的程序。GDB 的输入仍然来自于
    你的终端。tty 是 set inferior-tty 的别名。
    你可以用 show inferior-tty 命令来趟 GDB 显示程序将要使用终端名。
    set inferior-tty /dev/ttyb
    将被调试程序的 tty 设备设置为/dev/ttyb
    show inferior-tty
    显示被调试程序目前的 tty 设备名
    4.7 调试一个已经在运行的进程
    attach process-id
    这个命令 attach 到一个从 GDB 外启动的进程上。(info files 显示你当前活跃的目标)这
    个命令需要一个进
    程 id 作为参数。通常用 ps 工具来找到一个 Unix 进程的 ID,或者用’jobs -l’shell 命令。
    在你执行 attach 命令之后,按下回车键 attach 将不会再次执行。
    只有在支持进程的环境下,attach 命令才有效;例如,attach 在没有操作系统的裸机上市
    无效的。你必须有发给
    进程送信号的权限。
    在你执行 attach 命令的时候,调试器首先在当前工作目录下查找进程的可执行程序,如果
    没有找到,接着会用源
    代码文件搜索路径(参见 7.5 节[指定源代码目录],70 页)。你也可以用 file 命令来加载可执
    行文件。参见 15.1 节[
    指定文件的命令],155 页。
    GDB 在准备好要调试的进程后第一件事就是中断这个进程。可以在 run 启动的进程上的
    使用的命令也可以用在你
    attach 的进程上,你可以检查,修改这个进程。你可以插入一个断点;你可以 step 和 continue;
    你可以修改存储器。
    如果你希望进程继续执行,你可以在 attach 之后用 continue 命令来继续。
    detach
    在完成了调试之后,可以用 detach 来释放 GDB 对进程的控制。detach 进程后,进程继
    续执行。detach 命令之
    后,进程和 GDB 就没有关系了,你还可以 attach 到另外一个进程或者用 run 启动一个
    程序。detach 执行之后
    ,按下回车键不会再重复。
    如果你 attach 过一个进程,退出 GDB 会 detach 这个进程。如果你是用 run 命令启动的话,
    你将 kill 这个进程。缺省
    的,GDB 会要求得到你的确认;你可以用 set confirm 命令来控制是否需要确认(参见 19.7
    节[可选的警告和消息],213 页)。
    4.8 杀死子进程
    kill 杀死在 GDB 里运行的子进程
    在你希望调试一个 core dump 而不是进程的时候,这个命令很有用。在程序运行期间的时
    候,GDB 会忽略 core dump

    在某系操作系统,如果你在 GDB 里为这个程序设置了断点,这个程序就不能在 GDB 外
    运行了。你可以用 kill 命令来
    让程序在 GDB 外运行。
    在你运行程序的时候,kill 命令也有助于重编和重新连接程序,而有些系统是不可能做到
    这个的。在这种情况下
    ,在下次执行 run 命令的时候,GDB 可以知道程序已经发生变化了,就会重新读取符号表
    (同时也会保留你目前的
    断点设置)。
    4.9 调试多线程进程
    在某些操作系统里,例如 HP-UX 和 Solaris,一个程序可能有多个线程。线程精确概念随
    着各个操作系统而不一样
    ,但大体上,一个有多个线程的进程和多进程相似,除了多线程共享一个地址空间(就是说,
    他们可以检查和修改
    同一个变量)。另一方面,每个线程有它自己的寄存器和执行栈,也可能有自己私有的存储
    空间。
    GDB 提供了多个调试多线程的工具:
    新线程的自动通知
    ‘thread threadno’,切换线程
    ‘info threads’,查询线程
    ‘thread apply [threadno] [all] args’,对线程列表执行命令
    线程特定断点
    ’set print thread-events’,控制线程开始和结束时打印消息
    警告:在各个支持线程的操作系统里,不是所有的 GDB 配置都支持这些工具的。如果你
    的 GDB 不支持线程,这个命
    令就无效。例如,不支持线程的系统里’info threads’命令就不能输出信息,也会拒绝 thread
    命令,如下:
    (gdb) info threads
    (gdb) thread 1
    Thread ID 1 not known. Use the “info threads” command to
    see the IDs of currently known threads.
    GDB 线程调试工具可以观察进程的所有线程,而一旦 GDB 控制线程的话,这个线程就总
    是调试的焦点了。这个线程
    称为当前线程。调试命令从当前线程的角度来显示进程的信息。
    一旦 GDB 察觉到进程的新线程,GDB 就会 用‘[New systag]’的方式显示目标系统的标识。
    systag 是线程的标识,各
    个系统不一样。例如,当 GDB 发现一个新线程的时候,在 GNU/Linux 你可能看到
    [New Thread 46912507313328 (LWP 25582)]。
    而在 SGI 系统里,systag 就简单的形如‘process 368’,没有更多信息。
    出于调试的目的,GDB 自己会给线程一个编号–总是一个整数。
    info threads
    显示当前进程里的线程的总概要。GDB 显示每个线程(以此为序):
    1.GDB 分配的线程号
    2.目标系统的线程标识(systag)
    3.线程当前栈的概要
    线程号左边的星号’*'代表此线程是当前线程。例如:
    (gdb) info threads
    3 process 35 thread 27 0×34e5 in sigpause ()
    2 process 35 thread 23 0×34e5 in sigpause ()
  • 1 process 35 thread 13 main (argc=1, argv=0×7ffffff8)
    at threadtest.c:68
    在 HP-UX 系统里:
    出于调试目的, GDB 为进程里每个线程分配一个线程号(以线程创建顺序分配小整数)。
    无论何时 GDB 察觉到一个新线程,它会用‘[New systag]’的形式显示 GDB 自己的线程
    号和目标系统的线程标志 。
    systag 是线程标识,各个系统下可能不同。例如,GDB 察觉到新线程,在 HP-UX,你能看

    [New thread 2 (system thread 26594)]。
    info threads
    显示所有线程的概要。GDB 显示每一个线程(以此为序):
    1.GDB 分配的线程 号
    2.目标系统的线程标识(systag)
    3.线程当前栈的概要
    线程号左边的星号’*'代表此线程是当前线程。例如:
    (gdb) info threads
  • 3 system thread 26607 worker (wptr=0×7b09c318 “@”) \
    at quicksort.c:137
    2 system thread 26606 0×7b0030d8 in __ksleep () \
    from /usr/lib/libc.2
    1 system thread 27905 0×7b003498 in _brk () \
    from /usr/lib/libc.2
    在 Solaris 系统,那你可以用一个 Solaris 特有的命令来显示更多的信息:
    maint info sol-threads
    显示 Solaris 用户线程的信息。
    thread threadno
    将 threadno 指向的线程设置为当前线程。这个命令的参数 threadno 是 GDB 内部的线程
    号,就是’info threads’
    命令显示第一列。
    GDB 会显示你选择的线程的系统标识和它当前栈的概要:
    (gdb) thread 2
    [Switching to process 35 thread 23]
    0×34e5 in sigpause ()
    伴随着’[New…]‘消息,’Switching to’之后的文本形式由你的系统线程标识表示方式
    决定。
    thread apply [threadno] [all] command
    thread apply命令可以让你在一个或多个线程上执行名为command命令。用参数threadno
    指定你希望操作的
    线程数目。可以是单个线程号,’info threads’显示的第一列;或者可以是线程范围,
    像 2-4.要操作所有
    线程,敲入 thread apply all command。
    set print thread-events
    set print thread-events on
    set print thread-events off
    GDB 察觉到新线程启动或线程结束的时候,set print thread-events 命令可以开启或关闭
    打印信息。缺省
    下,如果目标系统支持的话,这些事件发生的时候,这些信息会打印出来。注意,这些
    信息不一定在所有目
    标系统里都可以关闭的。
    show print thread-events
    显示是否在 GDB 察觉线程启动或结束时打印信息。
    由断点或者信号决定,无论何时 GDB 停止程序,它都会选择断点或信号发生的线程。GDB
    会用‘[Switching to syst
    ag]’形式标识线程提示线程上下文的切换。
    更多关于GDB在停止启动多线程程序的行为的信息,参见5.4节[停止核启动多线程程序],
    59 页。
    更多多线程程序观察点的信息,参见 5.1.2[设置观察点],44 页。
    4.10 调试多个程序
    在多数系统下,GDB 没有为能用 fork 调用创建附加进程的程序提供特殊的支持。程序创
    建子进程时,GDB 会继续调试
    父进程,而子进程则不受影响。如果你此前在子进程的代码上设置了一个断点,则子进程会
    被 SIGTRAP 信号结束。
    不过,如果你想调试子进程的话,有一个不那么麻烦的替代方案。在 fork 调用之后的子
    进程代码里调用 sleep 调
    用。如果 sleep 代码的调用由某些环境变量或者某个文件的存在与否来决定,那就很方便了:
    如果你不想调试子进
    程,不设置这些变量或者删除文件就可以了。在子进程休眠的时候,用 ps 程序来得到进程
    id.接着用 GDB attach 到这
    个子进程上,如果你正在调试父进程,你需要新启动一个 GDB 实例,参见 4.7[Attach],30
    页。你就可以如同 attach 到别的
    进程那样开始调试子进程了。
    在某些系统上,GDB 提供调试用 fork 或 vfork 调用创建子进程的程序的支持。目前,只
    有 HP-UX(11.x 和以后版本)和
    GNU/Linux(2.5.60 内核版本及后续)提供这个功能的支持。
    缺省的,在创建子进程的时候,GDB 会继续调试父进程,而子进程不受影响。
    如果你要调试子进程,用命令
    set follow-fork-mode.
    set follow-fork-mode mode
    设置调试器对于 fork 或 vfork 调用的反应。fork 或 vfork 创建一个子进程。mode 参数可
    以是:
    parent fork 之后调试原进程。子进程不受影响。这是缺省方式。
    child fork 之后调试新的进程。父进程不受影响。
    show follow-fork-mode
    显示当前调试器对于 fork/vfork 调用的反应。
    在 Linux 下,如果你要调试父进程和子进程,用命令
    set detach-on-fork.
    set detach-on-fork mode
    设置 GDB 在 fork 之后是否 detach 进程中的其中一个,或者继续保留控制这两个进程。
    on 子进程(或者父进程,依赖于 follow-fork-mode 的值)会被 detach 然后独立运行。
    这是缺省 mode。
    off 两个进程都由 GDB 控制。一个进程(子进程或者父进程,依赖于
    follow-fork-mode)被调试,另外
    一个则被挂起。
    show detach-on-fork
    显示 detach-on-fork mode
    如果你选择了设置‘detach-on-fork’为 off,那么 GDB 会保持控制所有被创建的子程序(包
    括被嵌套创建的)。你
    可以用 info forks 命令来显示在 GDB 里创建的子进程,然后用 fork 命令来从一个进程切换
    到另一个。
    info forks
    打印在 GDB 控制下被创建的子进程列表。这个表包括 fork id, 进程 id 和当前进程的位
    置(程序计数器)。
    fork fork-id
    切换到 fork-id 指定的进程。参数 fork-id 是 GDB 内部为 fork 分配的,如命令’info forks’
    所显示列表的第一
    列。
    process process-id
    切换到 process-id 指定的进程。参数 process-id 必须是’info forks’输出的。
    要想结束调试一个被创建的进程,可以用 detach fork 命令(允许这个进程独立的运行),
    或者用删除(也杀死)
    的方法 delete fork 命令。
    detach fork fork-id
    detach 一个由 GDB 标识的 fork-id 指定的进程,然后从 fork 列表里删除。这个进程会被
    允许继续独立运行。
    delete fork fork-id
    杀死一个由 GDB 标识的 fork-id 指定的进程,然后从 fork 列表里删除。
    如果你要调试一个 vfork 创建接着 exec 的进程的话,GDB 会在这个新的目标上执行到底
    一个断点。如果你在原来程序
    的主函数上设置了一个断点,子进程上的主函数上也有一个同样的断点。
    如果子进程正在执行 vfork 调用,你不能调试子进程或者父进程。
    如果你在 exec 调用执行之后运行 GDB 的 run 命令,新目标会重新启动。要重启父进程,
    运行 file 命令,父进程可执行程序
    名作参数。
    你可以用 catch 命令来在 fork,vfork 或者 exec 调用的时候让 GDB 中断。
    4.11 为跳转设置书签
    在某些操作系统(目前只在 GNU/Linux 上),GDB 可以保存一个程序状态的快照,称为
    检查点,以后可以跳回。
    跳回到检查点会撤销所有在检查点之后的变化。这些变化包括内存,寄存器,甚至系统状
    态(有些限制)。这样可以
    有效的及时回到在检查点设置的状态。
    因此,如果你单步调试到你认为你接近到快要发生错误的地方,你就可以保存一个检查点。
    接着,如果你不经意的走的
    太远错过了关键的状态,你可以回到检查点后再从那里开始,而不需要从头启动程序。
    检查点对于需要很长时间或者单步调试里 bug 发生地方很远的情况下很有帮助。
    用 checkpoint/restart 方法调试:
    checkpoint
    保存被调试程序当前执行状态的快照。checkpoint 命令不需要参数,但每个检查点都分
    配一个小整数标识,如同
    breakpoint 标识一样。
    info checkpoints
    列出在当前被调试会话的检查点。对于每个检查点,信息显示如下:
    Checkpoint ID
    Process ID
    Code Address
    Source line, or label
    restart checkpoint-id
    在检查点号的状态上重新启动。所有程序变量,寄存器,栈帧等等都恢复到检查点上保
    存的状态。本质上将,gdb 会
    把时钟回拨到检查点所记录的时间。
    注意,断点,GDB 变量,命令历史等不受检查点重置的影响。通常,检查点只重置被
    调试程序内部的状态,不影响调试器
    本省的状态。
    delete checkpoint checkpoint-id
    删除以前保存的检查点
    回到以前保存的检查点,会重置程序的用户状态,加上相当数量的系统状态,包括文件指
    针。重置不会撤销已写入文件的数据,但
    是会把文件指针指向以前的文职,因此以前写入的数据就可以被覆盖。对于以只读模式打开
    的文件,指针也会回到以前的位置,因此
    可以重新读取数据。
    当然,送到打印机(或者其它外设)的字符不能收回,而从别的设备(比如串口设备)里
    读取的数据可以从内部程序缓冲里撤销,
    但是不能被塞回到串行管道里去,然后再读取他们。相似的是文件的内如如果被改变了,也
    不能被重置。
    然而,在这些约束条件下,你可以重新回到以前保存的程序状态去,重新调试–然后你可
    以改变事件的过程来执行一个不同的路径
    调试。
    最后,在你回到检查点的时候,有些内部程序状态会不一样—程序的进程 id。每个检查点
    会有一个独立的进程 id(pid),每个都和原
    来的 pid 不一样。如果你的程序保存了一个进程 id 的本地副本,这会有一个潜在的问题。
    4.11.1 使用检查点的隐含好处
    在某些系统里(例如 GNU/Linux),出于安全考虑,每个新进程的地址空间都要随机确定。
    这就很难或者说不可能在一个绝对地址上
    设置一个断点或者观察点,因为一个符号的绝对位置每次执行都不一样。
    然而,一个检查点是一个进程的相同的副本。因此如果你在主函数的入口点创建了一个检
    查点,你可以避开地址空间的随机化的影
    响,而且符号也会呆在相同的位置。
    第五章 中断和继续
    使用调试器的主要目的是在程序结束之前可以中断它;或者是在程序出现问题的时候,你
    可以调查为什么出问题。
    在 GDB 里,有多个原因可以让程序中断,例如信号,断点或者一个 GDB 命令之后(例
    如 step)执行新一行代码前。你可以检查和改变变量,设置一个新的断 点,或者删除一个
    旧的断点,再接着执行。通常 GDB 提供的消息可以显示程序大量的状态—但你也可以在任
    何时候显式的请求这些信息。
    info program
    显示程序状态信息:是否在执行,是什么进程,为什么中断。
    5.1 断点,监视点,捕获点
    断点可以让程序在执行到某个点上停止下来。对于每个断点,你可以加上条件来更详细地
    控制程序是否中断。你可以用 break 命令(带变量)来设置断点(参见 5.1.1 节[设置断点],
    40 页),变量用来指定程序在什么地方中断(以行号,函数名或者程序的绝对地址的方式)。
    在某些系统里,你可以在可执行程序运行前,在共享库里设置断点。在 HP-UX 系统里有
    些小小的限制:你必须等到程序运行才能在那些被程序间接调用的共享库例程上设置断点,
    例如,例程是 pthread_create 调用的参数。
    监视点是特殊的断点,在表达式的值改变的时候中断程序。表达式可以是是一个变量的值,
    或者是由操作符绑定的一个或多个变量,例如’a+b’.有时这种断点 也称为数据断点。你
    必须用一个不同的命令来设置监视点(参见 5.1.2 节[设置监视点],44 页),除此之外,你看
    原因两断点一样管理监视点:用相同的命 令激活,禁用,删除断点和监视点。
    在任何 GDB 中断程序的时候,你可以安排自动显示程序的数值。参见 8.6 节[自动显示],
    81 页。
    捕获点是另一种特殊类型的断点,用来在某些事件发生时中断程序,例如在抛出 C++异常
    或者加载库的时候。和监视点一样,你需要用不同的命令来设置捕获点 (参见 5.1.3 节[设
    置捕获点],47 页),除此之外,你可以类似断点来管理捕获点。(在程序接到一个信号时停
    止程序,用 handle 命令;参见 5.3 节[信号],57 页)
    GDB 会在你创建断点,监视点,捕获点的时候分配一个数字给它们;这些数字是从 1 开
    始的连续整数。在很多用来控制断点多种功能的命令里,你可以用断点号来指明是操作哪一
    个断点。每个断点可以激活或者禁用;如果被禁用了,他就不再影响程序的运行,除非你再
    激活它。
    5.1.1 设置断点
    断点设置用 break 命令(缩写 b). 调试器用’ b p n u m ’变量记录你最近设置的断点号;关于便利变量用途的讨论,参见 8.9 节 [ 便利变量 ] , 89 页。 b r e a k l o c a t i o n 在给定的位置 ( l o c a t i o n ) 设置断点,位置可以是函数名,行号,或者是一个指令的地址。 ( 参见 7.2 节 [ 指定位置 ] , 68 页,所有可能指定位置的方式 ) 。断点可能在程序执行指定位置前的代码前中断程序。在可以重载符号的源代码语言里,例如 C + + ,一个函数名可以涉及到多于一个可能中断的位置。参见 5.1.8 节 [ 断点菜单 ] , 52 页,讨论了这种情况。 b r e a k 在不带参数的情况下, b r e a k 命令在当前栈里的下一条指令里设置断点(参见第六章 [ 检查栈 ] , 61 页)。在当前栈的最低端,这可以让程序在控制返回到帧的时候立即中断。这和一个栈帧里的 f i n i s h 命令的效果相似–除了 f i n i s h 不留下一个有效的断点。如果你在栈帧的最低端用 b r e a k 而不带参数, G D B 在下次到达当前位置时中断程序;这在循环内很有帮助。 G D B 通常在继续执行时忽略断点,直到最少一条指令执行为止。如果没有这样做,你不禁用断点,你将不能通过断点。这个股则在程序中断的时候,不论断点是否存在,都可以生效。 b r e a k … i f c o n d 带参数设置断点;在每次断点到达时计算 c o n d 表达式,并且当且仅当表达式的值不为零的时候中断—就是说,如果 c o n d 表达式为真。‘ … ’代表可能的指定中断位置的参数(上面描述过的)。更多中断条件的信息,参见 5.1.6 节 [ 中断条件, 50 页 ] 。 t b r e a k a r g s 设置一个只中断一次的断点。 a r g s 和 b r e a k 命令里的参数一样,断点设置也一样,但断点在第一次程序中断后自动删除。参见 5.1.5 节 [ 关闭断点 ] , 49 页。 h b r e a k a r g s 设置一个硬件支持的断点。 a r g s 和 b r e a k 命令的一样,设置也一样,但断点需要硬件支持,某些目标硬件可能不支持。这个命令的主要目的是为了调试 E P R O M / R O M 代码,所以你可以不改变指令而在这个指令上设置一个断点。这个指令可以用在 S P A R C l i t e D S U 支持的新的陷阱 − 产生和多数基于 X 86 的目标。这些目标可以在程序访问某些数据或指令地址的时候产生陷阱,这些陷阱是设计用来调试寄存器的。然而硬件断点寄存器有断点数的限制。例如,在 D S U 上,一次只可以设置两个数据断点,如果多于两个的话 G D B 会拒绝的。在设置新的断点前删除或禁用不用的硬件断点(参见 5.1.5 节 [ 禁用断点 ] , 49 页)。参见 5.1.6 节 [ 中断条件 ] , 50 页。更多远程目标,你可以限制硬件断点的数量,见 [ 设置远程硬件断点限制 ] , 177 页。 t h b r e a k a r g s 设置一个只中断一次的硬件支持断点。 a r g s 和 h b r e a k 的参数一样,设置方式也一样。不过,和 t b r e a k 命令相似,断点会在程序第一次中断后自动删除。和 h b r e a k 命令相似,断点需要硬件支持,某些硬件可能不支持。参见 5.1.5 [ 禁用断点 ] , 49 页。参见 5.1.6 节 [ 中断条件 ] , 50 页。 r b r e a k r e g e x 在所有匹配正则表达式 r e g e x 的函数上设置断点。这个命令会在所有匹配的函数上设置无条件的断点,也打印设置的断点列表。一旦这些断点被设置上,它们就和用 b r e a k 命令设置的一样了。你可以删除,禁用它们,或者可以和别的断点一样为他们设置条件。正则表达式的语法是标准的,就如’ g r e p ’工具用的一样。注意,和 s h e l l s 用的不一样,例如 f o o ∗ 匹配开头是 f o ,接下来有 0 或者多个 o 的函数。在你的正则表达式的开头和结尾有个隐含的 . ∗ , 所以要想只匹配 f o o 开头的函数, 用 f o o . 在调试 C + + 程序,在非特定类的成员函数的重载函数的设置断点上, r b r e a k 很有用。可以用 r b r e a k 命令在一个程序里的所有函数上设置断点,如下: ( g d b ) r b r e a k . i n f o b r e a k p o i n t s [ n ] i n f o b r e a k [ n ] i n f o w a t c h p o i n t s [ n ] 打印断点,监视点和捕获点表。可选参数 n 代表打印特定断点的信息(或者监视点,捕获点)。对于每个断点,打印下列信息: B r e a k p o i n t N u m b e r s T y p e 断点,监视点,或捕获点 D i s p o s i t i o n 断点是否标记为禁用或删除 E n a b l e d o r D i s a b l e d 用’ y ′ 标记激活断点,用’ n ′ 标记断点禁用。 A d d r e s s 程序里的断点位置,内存里的位置。对于一个挂起的断点,它的位置是未知的,这个域会包含’ < P E N D I N G > ’。这类断点在共享库没被加载前世不会起作用的。详细说明见下面。一个断点对应多个位置的话,这个域会包含‘ < M U L T P L E > ’–详细说明见下面。 W h a t 断点位置在程序源代码,文件和行号。对于一个挂起的断点,由于在对应的共享库未被加载前不能解释,此时断点命令会显示一个初始的字符串。如果断点是有条件的, i n f o b r e a k 会显示被断点影响的行上的条件;断点命令,如果有的话,会在这行后显示。一个挂起的断点可以有条件的指定。这个条件会在共享库加载后分析有效性,以此来确定一个有效的位置。 i n f o b r e a k 带有一个断点号 n 的参数将只显示此断点。变量 bpnum’变量记录你最近设置的断点号;关 于便利变量用途的讨论,参见 8.9 节[便利变量],89 页。 break location 在给定的位置(location)设置断点,位置可以是函数名,行号,或者是一个指令的地址。 (参见 7.2 节[指定位置],68 页,所有可能指定 位置的方式)。断点可能在程序执行指定位置前的代码前中断程序。 在可以重载符号的源代码语言里,例如 C++,一个函数名可以涉及到多于一个可能中断 的位置。参见 5.1.8 节[断点菜单],52 页,讨论 了这种情况。 break 在不带参数的情况下,break 命令在当前栈里的下一条指令里设置断点(参见第六章[检 查栈],61 页)。在当前栈的最低端,这可以让 程序在控制返回到帧的时候立即中断。这和一个栈帧里的 finish 命令的效果相似–除了 finish 不留下一个有效的断点。如果你在栈帧的最 低端用 break 而不带参数,GDB 在下次到达当前位置时中断程序;这在循环内很有帮 助。 GDB 通常在继续执行时忽略断点,直到最少一条指令执行为止。如果没有这样做,你 不禁用断点,你将不能通过断点。这个股则在程 序中断的时候,不论断点是否存在,都可以生效。 break … if cond 带参数设置断点;在每次断点到达时计算 cond 表达式,并且当且仅当表达式的值不为 零的时候中断—就是说,如果 cond 表达式为真。 ‘…’代表可能的指定中断位置的参数(上面描述过的)。更多中断条件的信息,参见 5.1.6 节[中断条件,50 页]。 tbreak args 设置一个只中断一次的断点。args 和 break 命令里的参数一样,断点设置也一样,但断 点在第一次程序中断后自动删除。参见 5.1.5 节[关 闭断点],49 页。 hbreak args 设置一个硬件支持的断点。args 和 break 命令的一样,设置也一样,但断点需要硬件支 持,某些目标硬件可能不支持。这个命令的主 要目的是为了调试 EPROM/ROM 代码,所以你可以不改变指令而在这个指令上设置一 个断点。这个指令可以用在 SPARClite DSU 支持的新 的陷阱-产生和多数基于 X86 的目标。这些目标可以在程序访问某些数据或指令地址的 时候产生陷阱,这些陷阱是设计用来调试寄存器 的。然而硬件断点寄存器有断点数的限制。例如,在 DSU 上,一次只可以设置两个数 据断点,如果多于两个的话 GDB 会拒绝的。在设置 新的断点前删除或禁用不用的硬件断点(参见 5.1.5 节[禁用断点],49 页)。参见 5.1.6 节[中断条件],50 页。更多远程目标,你可以 限制硬件断点的数量,见[设置远程硬件断点限制],177 页。 thbreak args 设置一个只中断一次的硬件支持断点。args 和 hbreak 的参数一样,设置方式也一样。 不过,和 tbreak 命令相似,断点会在程序第一次 中断后自动删除。和 hbreak 命令相似,断点需要硬件支持,某些硬件可能不支持。参 见 5.1.5[禁用断点],49 页。参见 5.1.6 节[中断 条件],50 页。 rbreak regex 在所有匹配正则表达式 regex 的函数上设置断点。这个命令会在所有匹配的函数上设置 无条件的断点,也打印设置的断点列表。一旦 这些断点被设置上,它们就和用 break 命令设置的一样了。你可以删除,禁用它们,或 者可以和别的断点一样为他们设置条件。 正则表达式的语法是标准的,就如’grep’工具用的一样。注意,和 shells 用的不一样, 例如 foo*匹配开头是 fo,接下来有 0 或者多个 o 的函数。在你的正则表达式的开头和结尾有个隐含的.*,所以要想只匹配 foo 开头的函 数,用^foo. 在调试 C++程序,在非特定类的成员函数的重载函数的设置断点上,rbreak 很有用。 可以用 rbreak 命令在一个程序里的所有函数上设置断点,如下: (gdb) rbreak . info breakpoints [n] info break [n] info watchpoints [n] 打印断点,监视点和捕获点表。可选参数 n 代表打印特定断点的信息(或者监视点,捕 获点)。对于每个断点,打印下列信息: Breakpoint Numbers Type 断点,监视点,或捕获点 Disposition 断点是否标记为禁用或删除 Enabled or Disabled 用’y'标记激活断点,用’n'标记断点禁用。 Address 程序里的断点位置,内存里的位置。对于一个挂起的断点,它的位置是未 知的,这个域会包含’<PENDING>’。这类断点在共享库没被加载前世不会 起作用的。详细说明见下面。一个断点对应多个位置的话,这个域会包含 ‘<MULTPLE>’–详细说明见下面。 What 断点位置在程序源代码,文件和行号。对于一个挂起的断点,由于在对应 的共享库未被加载前不能解释,此时断点命令会显示一个初始的字符串。 如果断点是有条件的,info break 会显示被断点影响的行上的条件;断点命令,如果有 的话, 会在这行后显示。一个挂起的断点可以有条件的指定。这个条件会在共享库加载后分析 有效性, 以此来确定一个有效的位置。 info break 带有一个断点号 n 的参数将只显示此断点。变量 bpnum变量记录你最近设置的断点号;关于便利变量用途的讨论,参见8.9[便利变量]89页。breaklocation在给定的位置(location)设置断点,位置可以是函数名,行号,或者是一个指令的地址。(参见7.2[指定位置]68页,所有可能指定位置的方式)。断点可能在程序执行指定位置前的代码前中断程序。在可以重载符号的源代码语言里,例如C++,一个函数名可以涉及到多于一个可能中断的位置。参见5.1.8[断点菜单]52页,讨论了这种情况。break在不带参数的情况下,break命令在当前栈里的下一条指令里设置断点(参见第六章[检查栈]61页)。在当前栈的最低端,这可以让程序在控制返回到帧的时候立即中断。这和一个栈帧里的finish命令的效果相似除了finish不留下一个有效的断点。如果你在栈帧的最低端用break而不带参数,GDB在下次到达当前位置时中断程序;这在循环内很有帮助。GDB通常在继续执行时忽略断点,直到最少一条指令执行为止。如果没有这样做,你不禁用断点,你将不能通过断点。这个股则在程序中断的时候,不论断点是否存在,都可以生效。breakifcond带参数设置断点;在每次断点到达时计算cond表达式,并且当且仅当表达式的值不为零的时候中断就是说,如果cond表达式为真。代表可能的指定中断位置的参数(上面描述过的)。更多中断条件的信息,参见5.1.6[中断条件,50]tbreakargs设置一个只中断一次的断点。argsbreak命令里的参数一样,断点设置也一样,但断点在第一次程序中断后自动删除。参见5.1.5[关闭断点]49页。hbreakargs设置一个硬件支持的断点。argsbreak命令的一样,设置也一样,但断点需要硬件支持,某些目标硬件可能不支持。这个命令的主要目的是为了调试EPROM/ROM代码,所以你可以不改变指令而在这个指令上设置一个断点。这个指令可以用在SPARCliteDSU支持的新的陷阱产生和多数基于X86的目标。这些目标可以在程序访问某些数据或指令地址的时候产生陷阱,这些陷阱是设计用来调试寄存器的。然而硬件断点寄存器有断点数的限制。例如,在DSU上,一次只可以设置两个数据断点,如果多于两个的话GDB会拒绝的。在设置新的断点前删除或禁用不用的硬件断点(参见5.1.5[禁用断点]49页)。参见5.1.6[中断条件]50页。更多远程目标,你可以限制硬件断点的数量,见[设置远程硬件断点限制]177页。thbreakargs设置一个只中断一次的硬件支持断点。argshbreak的参数一样,设置方式也一样。不过,和tbreak命令相似,断点会在程序第一次中断后自动删除。和hbreak命令相似,断点需要硬件支持,某些硬件可能不支持。参见5.1.5[禁用断点]49页。参见5.1.6[中断条件]50页。rbreakregex在所有匹配正则表达式regex的函数上设置断点。这个命令会在所有匹配的函数上设置无条件的断点,也打印设置的断点列表。一旦这些断点被设置上,它们就和用break命令设置的一样了。你可以删除,禁用它们,或者可以和别的断点一样为他们设置条件。正则表达式的语法是标准的,就如grep工具用的一样。注意,和shells用的不一样,例如foo匹配开头是fo,接下来有0或者多个o的函数。在你的正则表达式的开头和结尾有个隐含的.,所以要想只匹配foo开头的函数,foo.在调试C++程序,在非特定类的成员函数的重载函数的设置断点上,rbreak很有用。可以用rbreak命令在一个程序里的所有函数上设置断点,如下:(gdb)rbreak.infobreakpoints[n]infobreak[n]infowatchpoints[n]打印断点,监视点和捕获点表。可选参数n代表打印特定断点的信息(或者监视点,捕获点)。对于每个断点,打印下列信息:BreakpointNumbersType断点,监视点,或捕获点Disposition断点是否标记为禁用或删除EnabledorDisabledy标记激活断点,用n标记断点禁用。Address程序里的断点位置,内存里的位置。对于一个挂起的断点,它的位置是未知的,这个域会包含<PENDING>。这类断点在共享库没被加载前世不会起作用的。详细说明见下面。一个断点对应多个位置的话,这个域会包含<MULTPLE>’–详细说明见下面。What断点位置在程序源代码,文件和行号。对于一个挂起的断点,由于在对应的共享库未被加载前不能解释,此时断点命令会显示一个初始的字符串。如果断点是有条件的,infobreak会显示被断点影响的行上的条件;断点命令,如果有的话,会在这行后显示。一个挂起的断点可以有条件的指定。这个条件会在共享库加载后分析有效性,以此来确定一个有效的位置。infobreak带有一个断点号n的参数将只显示此断点。变量_和 x 命令缺省的检查地址
    用来显示
    最近位置的断点(参见 8.5 节[查看内存],79 页)。
    info break 断点被执行过的次数。这个命令在和 ignore 命令和用的时候特别有用。你可
    以忽略
    大部分的断点执行,查看断点信息来看断点总共有多少次执行,然后再次运行,忽略这
    个总数
    少一次的端点执行。这将可以让你快速的到达断点的最后一次执行。
    GDB 可以在程序的同一位置设置任意数量的断点。这不是愚蠢或毫无意义的。特别是在
    断点是条件性的
    情况下,更为有用(参见 5.1.6 节[断点条件],50 页)。
    一个断点可能对应于多个位置。这种情况的例子如下:
    对于 C++构造函数,GCC 编译器产生函数体多个的实例,用于不同的重载场景。
    对于 C++模板函数,函数里一个给定的行可以对应于任意数量的实例。
    对于内联函数,一个给定的源代码行可以对应于多个内联的地址。
    在这些情况下,GDB 会在这些相关的位置插入断点。
    一个对应于多个位置的断点可能会用多行来显示断点信息表–一个表头行,接下来是每一
    行对应于每一
    断点位置。表头行在地址列里有’’。每个位置有单独的行,这一行包含位置
    的实际地址,和那
    个位置对应的函数名。断点号列的形式是断点号.位置号。
    例如:
    Num Type Disp Enb Address What
    1 breakpoint keep y
    stop only if i==1
    breakpoint already hit 1 time
    1.1 y 0×080486a2 in void foo() at t.cc:8
    1.2 y 0×080486ca in void foo() at t.cc:8
    将断点号.位置号作为参数传递给 enable 何 disable 命令,每个位置就可以被单独的激活或者
    禁用。注意,不能
    从列表里删除一个单独的位置,只能删除从属于父断点的整个位置列表(用 delete num 命令,
    num 是父断点的编号
    ,上面例子里是 1).禁用或者激活父断点(参见 5.1.5[禁用],49 页)影响所有属于这个断
    点的位置。
    在共享库里设置断点是很平常的事。程序运行的时候共享库可以显式加载/卸载,还可以
    多次重复。为了支持这
    个用例,GDB 会在共享库加载/卸载的时候更新断点位置。典型地,在库尚未加载或库的符
    号还不可用的时候,你可
    以在调试会话的开始在库里设置一个断点。在设置此类断点的时,GDB 会问你是否想要设
    置一个所谓的挂起断点–
    断点的地址还不能解释。
    程序运行后,当一个新共享库加载以后,GDB 会重新计算所有断点。当一个新加载的共
    享库包含挂起断点引用到
    的符号或者行时,这个断点就变为已解析和普通断点了。在共享库卸载时,所有引用到它的
    符号或行的断点都成为
    挂起断点。
    这个逻辑也适用于对应于多个位置的断点。例如,如果你在一个 C++模板函数里设置了一
    个断点,一个新加载的
    共享库有此模板的一个实例,一个新的位置会加到断点的位置列表里。
    除了位置未解析外,挂起断点和常规断点没有区别。你可以设置条件或者命令,激活或者
    禁用和执行别的断点操
    作。
    在’break’命令不能解析断点地址时,GDB 提供了附加的命令来控制解析此地址:
    set breakpoint pending auto
    这个命令是缺省行为。GDB 不能找到断点位置时,它会向你询问是否该创建一个刮起
    断点。
    set breakpoint pending on
    设置未识别断点位置应该自动的创建一个刮起断点。
    set breakpoint pending off
    挂起断点不创建。任何未识别断点位置都将导致一个错误。这个设置不影响此前创建的

    起断点。
    show breakpoint pending
    显示目前关于创建挂起断点的行为模式。
    上面的设置只影响 break 命令和它的参数。一旦断点被设置了,共享库加载/卸载时会被自
    动的更新。
    上面的设置只影响 break 命令和它的参数。一旦断点被设置了,共享库加载/卸载时会被自
    动的更新。
    对于某些目标,断点所在的位置可以在只读或是可读写的,GDB 可以根据断点位置来自
    行决定用硬件或者软件断点。这
    个规则应用于用 break 命令来设置的断点,也用于那些用 next 和 finish 之类的命令设置的内
    部断点。对于用 hbreak 命令设置
    的断点,GDB 总会用硬件断点。
    用下列命令控制这个自动行为:
    set breakpoint auto-hw on
    这是缺省行为。GDB 设置断点时,它会尝试用目标内存映射来决定是用软件还是用硬
    件断点。
    set breakpoint auto-hw off
    设置 GDB 不自动选择断点类型。如果目标提供了内存映射,GDB 会在试图在只读地址
    上设置软件断点的时候警告。
    GDB 本身会在程序里为某些特殊目的设置断点,例如为了恰当的处理 longjmp(在 C 程
    序里)。这些内部断点分配负值编号,
    从-1 开始;’info breakpoints’不显示它们。可以用 GDB 维持命令’maint info breakpoints’
    来查看它们(参见[maint info
    breakpoints],331 页)。
    5.1.2 设置监视点
    在一个表达式改变时,可以用监视点来中断程序运行,而不需要预先知道表达式在哪里发
    生变化。(这类断点有时称为数
    据断点)。表达式可以简单如单个变量的值,也可以复杂如多个变量用操作符结合起来。例
    如:
    1.单个变量值的引用
    2.一个地址转换为一个恰当的数据类型。比如,‘*(int )0×12345678’会在指定的地址监视
    一个 4 字节长的区域(认为是一个
    整形)。
    3.任意复杂的表达式,例如‘a
    b + c/d’。表达式可以程序语言的任何正确的操作符(参见
    12 章[语言],119 页)。
    可以在一个表达式上设置一个断点,即使这个表达式尚不能解析。例如,可以在
    ‘*global_ptr’初始化前设置一个监视点。
    在程序设置了‘*global_ptr’和表达式用一个有效值时,GDB 会中断程序。如果表达式通过
    其他方式变有效,而不是通过改变
    变量的方式的话(例如,malloc 调用而使‘global_ptr’指向的内存可用),GDB 会在下次
    表达式改变时中断程序。
    监视点可以用硬件或硬件的方式实现,这依赖于你的系统。如果是软件断点的话,GDB
    单步跟踪程序并且每一次都测试变量的
    值,这将使得比普通的执行慢几百倍。(但这是值得的,在你没有线索去哪里找 bug 的时候)
    在某些系统里,比如 HP-UX, PowerPC, gnu/Linux 和大多数基于 x86 的系统里,GDB 支持
    硬件监视点,而这不会降低程序执行速度。
    watch expr [thread threadnum
    设置一个表达式监视点。在表达式 expr 被被改写和值改变时 GDB 会中断程序。最简单
    (也是最常用的)的用法是监视一个变
    量:
    (gdb) watch foo
    如果命令包含[thread threadnum]参数,GDB 只会在 threadnum 标识的线程改变表达式
    expr 的值时中断。注意,只在硬件
    监视点上 GDB 才起作用。
    rwatch expr [thread threadnum]
    程序读表达式的值时中断。
    awatch expr [thread threadnum]
    读或写表达式时中断。
    info watchpoints
    打印监视点,断点和捕获点列表;和 info break 相同(参见 5.1.1 节[设置断点],40 页)
    如果可能,GDB 就设置一个硬件监视点。硬件监视点执行的非常快,调试器会在指令产生
    监视值改变的时候报告这个变化。如果 GDB
    不能设置一个硬件监视点,那么它会设置一个软件监视点,软件监视点会相对慢得多,而且
    是在变化发生之后的下一个指令才报告,
    并不是在发生改变的时候就报告。
    用 set can-use-hw-watchpoints 0 命令可以迫使 GDB 只设置软件监视点。由于参数设置为 0
    了,GDB 就再也不会试图去
    设置硬件监视点了,即使这个系统支持硬件监视点(注意,此前设置的硬件-协助的监视点
    仍将使用硬件机制来监视
    表达式的值)。
    set can-use-hw-watchpoints
    设置是否用硬件监视点。
    show can-use-hw-watchpoints
    显示硬件监视点的当前模式。
    对于远程系统,你可是限制硬件监视点的数量,参见[设置远程 硬件-断点-限制],177 页。
    在执行 watch 命令时,GDB 报告:
    Hardware watchpoint num: expr
    如果成功设置了一个硬件监视点的话。
    目前,awatch 和 rwatch 命令只能设置硬件监视点,因为不改变被监视的表达式的值的数
    据的访问不在每个执行到它
    的指令上都加以检查的话,就没法探测到;目前 GDB 也没有那样做。如果 GDB 发现用 awatch
    或者 rwatch 不能设置硬件监视
    点的话,会打印类似如下信息:
    Expression cannot be implemented with read/access watchpoint.
    有时候,由于被监视的表达式的数据类型长度超出目标系统上支持的硬件监视点,GDB
    可能不能设置硬件监视点。例
    如,某些系统只能监视最多 4 个字节宽的硬件监视点;在这些系统里,不能为带有双精度的
    浮点指针数据(通常是 8 字节
    长)的表达式设置一个硬件断点。一个可能的替代方案是,把一个长区域分成一系列小的监
    视点。
    如果设置了太多的硬件监视点,GDB 有可能不能再继续执行程序时插入所有的监视点。
    由于准确的有效监视点的数量
    在程序重新执行后才能知道,GDB 可能不能在设置监视点的时候给出警告,警告信息会在
    程序重新执行后发出:
    Hardware watchpoint num: Could not insert watchpoint
    如果发出了这个警告,需要删除或禁用一些监视点。
    监视复杂的引用了大量的变量的表达式可能会消耗光硬件断点可用的资源。这是因为
    GDB 需要分别为监视表达式里
    引用的每个变量分配资源。
    如果调用了一个交互使用打印或者调用的函数,任何监视点都只能等到 GDB 遇到另一种
    断点或调用结束之后才有效。
    GDB 会在离开生存区的时候,自动删除监视本地(自动)变量的监视点,或者是引用到
    这种变量的表达式的监视点;
    也就是说在离开这些变量定义区的时候。尤其是,在调试程序要结束时,所有的本地变量都
    要离开生存区的时候,只
    有全局变量的监视点才保留。如果重新执行程序,需要重设这些监视点。一个方法是在 main
    函数的入口点设置代码断
    点,在执行断点的时候设置这些监视点。
    在多线程程序里,监视点会从每个线程里发现被监视的表达式值的变化。
    警告:多线程程序里,软件监视点只有有限的用处。如果 GDB 创建了一个软件监视点,
    它只能在一个线程里监
    视这个表达式的值。如果你你确信只有当前的线程活动会导致表达式的值的改变的话(并且
    你确信没有别的线程会变成当前线程的话),那么你可以像通常那样使用软件监视点。然而,
    GDB 不能察觉非当前线程改变表达式值。(于此相反
    ,硬件监视点能从所有线程中监视断点)
    参见[设置远程硬件-监视点-限制],177 页。
    5.1.3 设置捕获点
    用捕获点可以让调试器为某些程序事件中断程序执行,例如 C++异常或者是共享库的加
    载。用 catch 命令来设置
    一个捕获点。
    catch event
    在 event 发生时中断。事件可以是下列的:
    throw C++异常的抛出。
    catch C++异常的捕获。
    execption
    Ada 异常。如果在命令的结尾指定了异常名(比如 catch execption Program_Error),
    调试器
    就只会在这个异常发生时中断。否则,调试器会在任意的 Ada 异常发生时中断。
    exception unhandled
    没有被程序处理的异常。
    assert 一个失败的 Ada 断言。
    exec exec 调用。目前只在 HP-UX 和 GNU/Linux 上可用。
    fork fork 调用。目前只在 HP-UX 和 GNU/Linux 上可用。
    vfork vfork 调用。目前只在 HP-UX 和 GNU/Linux 上可用。
    load
    load libname
    动态加载共享库,或者加载库 libname。目前只在 HP-UX 上可用。
    unload
    unload libname
    卸载动态加载的共享库,或者卸载库 libname。目前只在 HP-UX 上可用。
    tcatch event
    设置只中断一次的捕获点。捕获点会在事件第一次捕获之后自动删除。
    用 info break 命令列出目前的捕获点。
    目前 GDB 对 C++异常处理有一些限制(catch throw 和 catch catch):
    1.如果交互调用函数,GDB 通常会在这个函数结束时将控制权交还给你。如果这个调用产

    了一个异常,这个调用可能越过交还控制权的机制,让程序结束或仅仅只是继续执行,直到
    它碰到一断点,捕获到一个 GDB 检测的信号,或者退出。即使你为异常设置了捕获点也是

    样的;异常的捕获点在交互式的调用里是禁用的。
    2.不能交互的产生一个异常。
    3.不能交互的注册异常处理函数。
    某些时候 catch 并不是最好的异常处理调试技术:如果你需要异常发生的精确位置的话,

    异常处理函数前中断是更好的选择,因为那样的话可以看到还未退绕的栈。相反,在异常处

    函数里设置断点的话,就不太容易找出在哪里发生了异常。
    要在异常处理函数调用前中断程序,你需要了解一些实现的知识。就 GNU C++而言,异常
    是调
    用一个名为__raise_exception 的库函数而引发的,这个函数有下面的 ANSI C 接口:
    /
    addr is where the exception identifier is stored.
    id is the exception identifier. */
    void __raise_exception (void **addr, void *id);
    要让调试器在栈没有退绕前捕获异常,在函数__raise_exception 上设置断点就可以了(参见
    5.1
    节[断点;监视点;和异常],39 页)。
    用一个条件断点(参见 5.1.6 节[断点条件],50 页),可以在一个指定异常发生时中断。可
    以用
    多个条件断点来在任何一个异常发生的时候中断程序。
    5.1.4 删除断点
    常常有必要在断点完成任务之后删除断点,监视点或者捕获点,因为不再需要它们再次中
    断了。这
    就是删除断点。一个断点删除之后就不存在了;它被遗忘了。
    用 clear 命令可以从程序中删除所有断点。用 delete 命令指定断点号可以删除单独的断点,
    监视点或
    者捕获点。
    用删除断点来继续执行并不是必须的。只要不改变执行地址的执行程序,GDB 会自动忽
    略在将要执行
    的第一个指令上的断点。
    clear 在选定的栈帧上的下一个指令上删除所有的断点(参见 6.3 节[选择一个帧],64 页)。
    只要选择
    最内层的帧的话,这是一个在程序中断地方删除断点的好方法。
    clear location
    在指定的位置删除所有的断点。参见 7.2 节[制定位置],68 页,更多关于位置的形式;
    最有用的形
    式如下表:
    clear function
    clear filename:function
    删除在名为 function 入口点上的断点。
    clear linenum
    clear filename:linenum
    删除在指定的行或文件名上的断点。
    delete [breakpoints] [range…]
    在指定的范围内删除断点,监视点或者捕获点。如果没有制定参数,就删除所有的断点
    (GDB 会要求确定,
    除非你设置了 set confirm off)。delete 命令可以缩写为 d.
    5.1.5 禁用断点
    相比删除断点,监视点或者捕获点,你也许更愿意禁用它们。这将让断点不起作用就如它
    被删除了一样,但会记住
    断点的信息,可以在以后激活。
    可以用 enable 和 disable 命令禁用和激活断点,监视点和捕获点,可选择指定一个或多个
    断点号作为参数。如果不知道
    用哪个断点号,用 info break 或 info watch 来打印断点,监视点,捕获点的信息。
    禁用和激活有多个位置的断点会影响其所有的位置。
    断点,监视点,捕获点有四种激活状态中的任意一种:
    1.已激活的(Enabled)。断点会中断程序。这个状态是用 break 命令发起的。
    2.已禁用的(Disabled)。断点不再影响程序。
    3.激活一次(Enabled once)。断点会中断程序,但会变成 disabled 状态。
    4.激活并删除(Enabled for deletion)。断点中断程序,但中断后立即就永久删除。这个状态
    是用 tbreak 命令发起的。
    可以用下列命令来激活或禁用断点,监视点,捕获点:
    disable [breakpoints] [range…]
    禁用指定断点–或者所有的断点,如果没有参数的话。已禁用的断点不再起效,但没有
    被删除。所有的选项如
    ignore-counts,conditions 和 commands 会记住,以便将来再次激活。disable 缩写 dis。
    enable [breakpoints] [range…]
    激活指定的断点(或者是所有的断点)。重新可以中断程序。
    enable [breakpoints] once range…
    临时激活指定的断点。GDB 会在这个断点中断程序之后立即禁用它。
    enable [breakpoints] delete range…
    激活断点中断一次,然后删除之。GDB 在程序中断后立即删除这类断点。这类断点用
    tbreak 命令发起。
    除了用 tbreak 命令设置的断点(参见 5.1.1[设置断点],40 页),断点会自动激活;因此,
    它们用上述的命令转换状态。
    (命令 util 可以设置和删除它自己的断点,但不改变其它的断点的状态;参见[继续和单步
    跟踪],54 页)
    5.1.6 中断条件
    最简单的断点会在程序执行到指定位置时中断程序。也可以为断点指定条件。条件只是程序
    语言的一个 Boolean
    型的表达式(参见 8.1 节[表达式],75 页)。带条件的断点会在程序执行到底时候计算表达
    式的值,只有在条件为
    真的时候才中断。
    这是和程序断言的用法是相反的;这这个情况里,你希望在断言失败是中断–就是说,条
    件为假。在 C 语言里,如
    果要测试由叫天 assert 表示的断言,应该在相应的断点上设置条件为‘! assert’。
    监视点也可以用条件;其实监视点不太需要条件,因为监视点总在检测表达式的值–但用
    条件可能更简单,在一个
    变量名上设置一个监视点,并制定一个条件来测试新值是否是你期望的。
    断点条件可能有边际效应,甚至可能调用程序里的函数。这可能很有用,比如,在激活程
    序进程日志函数,或者用
    你自己的打印函数来格式化特殊数据结构。除非在相同的位置还有另外已激活断点,否则效
    应是完全可预测的。(
    假使那样的话,GDB 会首先检查另外的断点来中断程序,不检查此断点的条件)注意,要
    在断点到达时实现边际效应
    ,断点命令通常比断点条件更方便更灵活。(参见 5.1.7[断点命令列表],51 页)
    在 break 命令里用’if’可以在设置断点时指定断点条件。参见 5.1.1 节[设置断点],40 页。
    它们也可以在任何时候用
    condition 命令改变。
    你也可以在用 watch 时带 if 关键字。catch 命令不识别 if 关键字。只有 condition 可以用来
    对捕获点进一步的设置。
    condition bnum expression
    为断点,监视点,捕获点指定断点条件表达式。一旦你设置了条件,断点 bnum 就只在
    表达式为真中断程序(
    非零,在 C 里)。当用 condition,GDB 立即检查语义的正确性,然后判断它用到的符
    号是否用在了断点上下
    文里。如果表达式用到的符号不在断点上下文里,GDB 会打印一个错误消息:
    No symbol “foo” in current context.
    然而,GDB 不会真的在用 condition 命令的同时就计算表达式(或者在一个命令带条件
    的设置断点,像 break
    if…).参见 8.1 节[表达式],75 页。
    condition bnum
    从断点 bnum 里删除条件。这个断点就成为普通的无条件的断点。
    一个特殊的条件断点的例子是在断点达到一定次数时才中断。这非常有用,因此有一个特
    别的方式来实现:用断点
    的忽略计数。每个断点都有一个忽略计数,忽略计数是个整型数。大多数情况下,忽略计数
    是 0,因此没有什么效
    用。但是程序执行到一个忽略计数为正数的的断点的时候,那么就会中断执行,忽略计数减
    1,接着执行程序。结果是
    ,如果忽略计数是 n,断点不会在接下来的 n 次中断程序。
    ignore bnum count
    将断点 bnum 的忽略计数设置为 count。接下来 的 count 次碰到断点,程序执行不会中断;
    除了忽略计数减 1,
    GDB 不做别的。
    要在下一次断点执行到的时候中断程序,设置忽略计数为 0。在用 continue 在中断后来
    重新执行,可以直接
    指定忽略计数为 continue 的参数,而不需要用 ignore.参见 5.2 节[继续和单步跟踪],54
    页。如果一个断点有
    一个正当忽略计数和条件,不会检查条件。一旦忽略计数变为 0,GDB 会重新检查条
    件。
    要达到和忽略计数一样的效果,可以用每次自减 1 的调试器方便变量作为断点条件来
    做,比如‘$foo– <= 0
    ’。
    忽略计数可以用于断点,监视点和捕获点。
    5.1.7 断点命令列表
    可以为断点(或监视点,捕获点)设置一系列命令来让断点中断时执行之。例如,你可能
    想要打印某些表达式的值
    ,或者激活别的断点。
    commands [bnum]
    … command-list …
    end 为断点 bnum 指定命令列表。commands 后面接命令行。最后输入一行 end 阿里结束
    命令。
    要删除一个断点的命令,输入 commands 后接一行 end;就是说,不带命令。
    不带 bnum 参数的话,commands 指定最后的断点,监视点或者捕获点(不是最近执行
    到的断点)。
    在命令列表里,按下回车键意味着重复上一个命令的功能将被禁用。
    可以用断点命令来重新启动程序。仅仅只用 continue 命令,或者 step,或者是其它重新执
    行的命令就可以了。
    在重新执行的命令之后的命令将会被忽略不执行。这是因为每次重新执行的时候(即使只
    是 next 或 step),可能会碰
    到别的断点–这个断点可能有自己的命令列表,会产生执行哪一个命令列表的歧义 。
    如果你指定的命令列表的的哥命令是 silent,通常断点中断的所产生的消息就不会打印。
    这个在打印一个特定的消
    息,然后接着继续执行的中断而言是可取的。如果剩下的命令都不打印消息,你将看不到什
    么中断的迹象。silent 只
    在断点命令开头有意义。
    命令 echo,output 和 printf 可以打印精确控制的输出,并常常在哑断点里很有用。参见 20.4
    节[控制输出的命令],
    222 页。
    举个例子,下面的例子可以让你用断点命令来在 foo 的入口点,当 x>0 时打印 x 的值。
    break foo if x>0
    commands
    silent
    printf “x is %d\n”,x
    cont
    end
    断点命令的一个应用是应付一个 bug(就是说先放下此 bug)来测试另一个。在出错行后
    的代码上设置一个断点,设
    置条件来检查出错情况,用命令来给需要用到的变量设置正确的值。用 continue 命令来结束
    命令列表,因此程序就不
    会停止,用 silent 命令来禁止输出信息。下面是一个例子:
    break 403
    commands
    silent
    set x = y + 4
    cont
    end
    5.1.8 断点菜单
    某些编程语言(特别是 C++和 Objective-C)允许一个函数名可以定义好几次,应用于不
    同的上下文,称之为重载。
    当一个函数名重载时,‘break function’就不足与让 GDB 明白你需要在哪里设置断点。可以
    用函数的准确特征来指
    明要设置的是哪个函数版本,例如用‘break function(types)’。否则,GDB 会提供一个数字
    标识的菜单供你选择
    不同的断点,并用提示符’>’等待你的选择。开头的两个选项是‘[0] cancel’和‘[1] all’。
    输入 1 会在每个函数
    上设置一个断点,输入 0 取消 break 命令设置新的断点。
    例如,下面的会话摘录显示的是在重载的符号 String::after 上设置断点。选择了 3 个特别
    的函数定义体:
    (gdb) b String::after
    [0] cancel
    [1] all
    [2] file:String.cc; line number:867
    [3] file:String.cc; line number:860
    [4] file:String.cc; line number:875
    [5] file:String.cc; line number:853
    [6] file:String.cc; line number:846
    [7] file:String.cc; line number:735

2 4 6
Breakpoint 1 at 0xb26c: file String.cc, line 867.
Breakpoint 2 at 0xb344: file String.cc, line 875.
Breakpoint 3 at 0xafcc: file String.cc, line 846.
Multiple breakpoints were set.
Use the “delete” command to delete unwanted
breakpoints.
(gdb)
5.1.9 “不能插入断点”
在某些操作系统里, 如果有其它的进程运行了这个程序的话,断点就不能在程序里设置
了。在此情况下,用一个断
点试图运行或继续执行程序会引起输出一个错误信息:
Cannot insert breakpoints.
The same program may be running in another process.
这个情况发生时,有三种继续的方式:
1.删除或关闭断点,接着继续。
2.挂起 GDB,复制程序文件并命名为另一个名字。重新执行 GDB,用 exec-file 命令来指定
GDB 要执行的程序文件名。接
着重启程序。
3.用链接器选项’-N’重新链接程序,以使文本段是非共享的.操作系统的限制可能不应用
于非共享模式的可执行程序。
如果你要求设置太多有效的硬件支持的断点和监视点的话,类似的信息也会输出:
Stopped; cannot insert breakpoints.
You may have requested too many hardware breakpoints and watchpoints.
这个消息在你试图继续执行程序是打印,因为 GDB 只有在此时才能准确的知道有多少硬件
断点和监视点需要插入。
在这个消息打印的时候,你需要禁用或者删除一些硬件支持断点和监视点,然后接着继续
执行。
5.1.10 “断点地址已调整…”
某些处理器架构对断点所在的地址有限制。对于那些有限制的架构,GDB 会试图调整断
点的地址来符合它的限制。
这类架构的一个例子是富士的 FR-V。FR-V 是 VLIW 架构,有着类 RISC 的指令集,它的
一组指令可能集合在一起以便于
并行运算。FR-V 架构限制断点指令的位置在一组指令集的最低的地址空间。GDB 会主动调
整断点位置,将断点指令设置
在这组指令集的开头来适应 FR-V 的限制。
由于优化代码的原因,得到一组指令集常常和源代码的表述是不一样的,因此断点位置可
能被调整到和源代码不一
致的地方。由于这样的调整使得 GDB 断点的行为可能和用户的预期有很大的不同,在设置
断点的时候会打印出一个警告
信息,并且断点执行到时也会打印这样的信息。
断点在受到调整时,类似下面的警告信息会打印出来:
warning: Breakpoint address adjusted from 0×00010414 to 0×00010410.
对于用户可设置的和 GDB 内部的断点,这样的警告都可能出现。如果你看到一个这样的
警告,你应该确认调整后的位置
上设置的断点是否具有你所期望的效果。如果不是,问题断点要删除,应该设置一个有效的
断点。例如,可能在后面的指
令上设置断点就够了。在某些情况下,条件断点在防治断点出发调整方面也很有用。
GDB 也会在调整后的断点上中断时打印一个警告:
warning: Breakpoint 1 address previously adjusted from 0×00010414
to 0×00010410.
遇到这个警告时,很可能要采取补救措施已经晚了,除非断点中断早于所期望的或者中断
的频率比预期的高。
5.2 继续和单步跟踪
继续意味着重新执行程序知道程序正常结束。相反,单步跟踪意味着只执行程序“一步”,
“一步”可能代表着一
行源代码,或者一条机器指令(依赖于你用的特定命令)。不论继续或是单步跟踪,程序可
能很快就由于断点
或信号中断执行。(如果由于信号中断,你可能希望用 handle,或者用’signal 0′来继续执行。
参见 5.3 节[信号],57 页)
continue [ignore-count]
c [ignore-count]
fg [ignore-count]
在程序最近中断的地方重新执行;任何设置在这个地址上的断点都会被绕过。可选参数
ignore-count 可以让
你进一步指定在这个位置上忽略断点次数;它的效果就如 ignore(参见 5.1.6[断点条件],
50 页)。
参数 ignore-count 只有在程序由于断点中断时才有意义。其他时候,continue 的
ignore-count 参数会被忽略

同义词 c 和 fg(表示 foreground,因为被调试的程序总被认为是前台程序)只是为方便
而提供的,其行为就如
continue 一样。
要在不同的位置重新执行程序,可以用 return(参见 14.4 节[从函数里返回])来回到调用
函数;或者 jump(参见
14.2[在不同的地址继续],150 页)到程序里的绝对地址。
使用单步跟踪的典型的技术是在函数的入口点设置一个断点,或者在可能发生错误的程序
段上设置,运行程
序知道它在断点上中断,再在这个嫌疑区域单步执行,检测感兴趣的变量,直到你发现错误
发生。
step 继续执行程序直到另一行代码,再中断,把控制权交换 GDB。可以缩写为 s。
警告:如果在没有调试信息的函数里使用 step 命令,程序执行会继续执行直到有
调试信息的一个函
数。类似的,不会单步进一个没有调试信息的函数。要跳过没有调试信息的函数,
用 stepi 命令,下
面会介绍。
step 命令只在一行源代码的第一个指令上中断。这就阻止了在类似 switch 语句,for 循
环上的多次中断。如果
在这行代码上调用的函数有调试信息,step 会继续中断。换句话说是,step 会进入这行
代码所调用的所有函
数。
如果函数有行号信息,step 命令也只进入这个函数。否则 step 命令就如 next 命令。这
个规则可以在 MIPS 机器
上用 cc -gl 编译时避免错误。在以前,step 会进入有调试信息的子函数。
step count
继续单步执行,不过执行 count 次。如果执行到一个断点,或者一个与单步跟踪无关的
信号在 count 次单步执
行发生时,单步执行会立即中断。
next [count]
在当前栈帧(最内层)上继续执行到下一行源码行。和 step 相似,但在这行代码上调
用的函数将不会中断。
程序执行到在原栈层里的另一行代码时会中断。缩写为 n。
参数 count 是重复次数,和 step 的一样。
命令 next 只在一行源代码的第一个指令上中断。这就阻止了在类似 switch 语句,for 循
环上的多次中断。如果
在这行代码上调用的函数有调试信息,step 会继续中断。
set step-mode
set step-mode on
set step-mode on 命令会导致 step 命令在没有调试行信息的函数的入口点中断,而不是
越过这个函数。
如果你对一个不带符号信息的函数的机器指令很感兴趣,又不想 GDB 越个这个函数的
话,那么这个命令就很有用了。
set step-mode off
导致 step 命令越过不带调试信息的函数。这是缺省的。
show step-mode
显示 GDB 是否中断或越过不带源代码行调试信息的函数。
finish
继续执行直到当前选定栈帧上的函数返回。打印返回值(如果有)。
和 return 命令相反(参见 14.4 节[从一个函数里返回],151 页)。
until
u 在当前栈帧上继续执行直到越过当前行的源代码行。这个命令用来避免多次单步执行
一个循环。和 next 命令,除了
until 在遇到一个跳转时,until 会自动继续执行直到程序计数器比跳转地址大的时候。
这就意味着在你用单步执行达到循环结束的时候,until 会使程序继续执行直到循环结
束。相反,在循环结束时,
next 命令只是简单的又从循环开始单步执行,这就使得你要进行下次单步执行循环。
until 总是在程序试图从当前栈帧中退出的时候中断执行。
如果机器指令的顺序和源代码的不相符合的话,until 可能会产生和预期不大一样的结
果。例如,下列调试会话摘录
里,f(frame)命令显示了程序执行在杭 206 中断;但用 until,得到行 195:
(gdb) f
#0 main (argc=4, argv=0xf7fffae8) at m4.c:206
206 expand_input();
(gdb) until
195 for ( ; argc > 0; NEXTARG) {
这是因为执行效率的缘故,编译器在循环尾产生结束测试的代码,而不是在循环开头–
即使 C for 循环的测试实在循
环体前。until 命令看上去就像会单步执行回循环的开头;然而,它不是真的要回到前
面的代码里去–不是依照实际
的机器代码。
不带参数的 until 命令将使用单个指令的单步执行方式,因此比带参数的 until 要慢。
until location
u location
继续执行直到程序执行到指定的位置,或者从当前栈帧返回。location 可以是在 7.2 节[指
定位置]里讨论的任意一种
形式。这个命令形式使用临时断点,因此比不带参数的 until 命令要快。只有在这个指
定的位置在当前帧上时,它才
会真的被执行到。这就意味着 until 可以用来跳过函数嵌套调用。如下面函数所示,如
果当前位置是 96 行,执行命令
until 99会执行程序到 99 行,在内从调用返回的时候。
94 int factorial (int value)
95 {
96 if (value > 1) {
97 value = factorial (value – 1);
98 }
99 return (value);
100 }
advance location
继续执行程序直到给定的位置 location。参数是必须的,可以是可以是在 7.2 节[指定位
置]里讨论的任意一种形式。
执行会在从当前栈帧上退出时中断。advance 和 until 相似,但 advance 不会跳出函数嵌
套调用,而且目标位置不必要
在当前帧上。
stepi
stepi arg
si 执行一个极其指令,接着中断然后返回到调试器。
在单步执行一个机器指令的时候,执行‘display/i KaTeX parse error: Expected 'EOF', got '#' at position 5129: …显示最内 3 层的堆栈帧。 #̲0 m4_traceon (o…cdir’来指代编译目录(如果已记录的话),’ c w d ’指代当前工作目录。’ cwd’指代当前工作目 录。’ cwd指代当前工作目录。cwd’和’.'不相
同—前者记录在 GDB 调试会话期间变动的当前工作目录,后者则在你将一个目录添加
进源代码路径时立即
展开为当前目录。
directory
将源代码路径重置为默认值(Unix 系统下是’ c d i r : cdir: cdir:cwd’)。这个命令要求确认。
show directories
打印源代码路径:显示包含那个目录。
set substitute-path from to
定义一个源代码路径替换规则,并将其填加到当前替换规则列表的尾部。如果有相同的
from 规则存在的话
,那么旧的规则就会被删除。
例如,如果文件’/foo/bar/baz.c’移动到’/mnt/cross/baz.c’,那么命令
(gdb) set substitute-path /usr/src /mnt/cross
告诉 GDB 用’/mnt/cross’替换’/usr/src’,这就可以让 GDB 查找到’baz.c’,即使它已
经移走了。
如果定义了多个替换规则,那么 GDB 会以规则定义的顺序一个接一个的计算它们。如
果有的话,第一个匹
配项就会进行替换。
例如,如果我们输入了下列命令:
(gdb) set substitute-path /usr/src/include /mnt/include
(gdb) set substitute-path /usr/src /mnt/src
GDB 会用第一个规则将’/usr/src/include/defs.h’用’/mnt/include/defs.h’替换。不过,
它会用第二个
规则将’/usr/src/lib/foo.c’用’/mnt/src/lib/foo.c’替换。
unset substitute-path [path]
如果指定了 path 的话,在当前替换规则列表里搜索要重置的规则。如果找到的话就删
除之。如果没有找到
的话,调试器会输出一个警告信息。
如果没有指定 path 的话,那么所有的替换规则都将被删除。
show substitute-path [path]
如果指定了 path,那么打印打印源代码路径替换规则,如果有的话。
如果没有指定 path,那么打印所有的替换规则。
如果源代码路径混杂着一些不再有用的目录的话,GDB 可能在某些情况下造成错误的代
码版本的混淆。可以用下
列命令来纠正这种错误:
1.用不带参数的 directory 命令来重置源代码路径为默认值。
2.用带正确参数的 directory 来添加你需要的目录。可以用一个命令将所有的路径添加。
7.6 源代码和机器代码
可以用命令 info line 将源代码映射到程序地址上(反过来一样),命令 disassemble 可以显
示一定范围的机器指
令。在 GNU Emacs 模式下运行时,info line 命令会引起箭头指向指定的行。而且,info line
打印符号形式的地址
,也打印 16 进制的地址。
info line linespec
打印指定的源代码行的编译代码,从开始到结尾的地址。可以指定源代码行,7.2 节[指
定位置],68 页里
讨论的任意形式。
例如,我们可以用 info line 来查找函数 m4_chagequote 的第一行的目标代码:
(gdb) info line m4_changequote
Line 895 of “builtin.c” starts at pc 0×634c and ends at 0×6350.
我们也可以查询(用
addr 作为 linespec 的形式)哪一行源代码对应一个特定的地址:
(gdb) info line *0×63ff
Line 926 of “builtin.c” starts at pc 0×63e4 and ends at 0×6404.
在 info line 后,x 命令的缺省地址就变为这行的开头地址,所以’x/i’足以开始检查机器
代码(参见 8.5 节[检查
内存],79 页)。而且,这个地址也存储与变量$_里(参见 8.9 节[便利的变量],89 页)。
disassemble
这个命令将一段内存作为机器指令转储。缺省的内存范围是选定堆栈帧的程序计数器所
代表的函数。单个
参数的话是程序计数器的值;GDB 转储值个值附近的函数。两个参数指定要转储的地
址范围(第一个是开
始,第二个是结束)。
下面的例子显示了反汇编一个 HP PA-RISC 2.0 上的一段代码:
(gdb) disas 0×32c4 0×32e4
Dump of assembler code from 0×32c4 to 0×32e4:
0×32c4 <main+204>: addil 0,dp
0×32c8 <main+208>: ldw 0×22c(sr0,r1),r26
0×32cc <main+212>: ldil 0×3000,r31
0×32d0 <main+216>: ble 0×3f8(sr4,r31)
0×32d4 <main+220>: ldo 0(r31),rp
0×32d8 <main+224>: addil -0×800,dp
0×32dc <main+228>: ldo 0×588(r1),r26
0×32e0 <main+232>: ldil 0×3000,r31
End of assembler dump.
某些架构有多个通用的指令助记符或者同义词。
对于动态链接的和使用共享库的程序,调用函数或位于共享库的分支位置的指令可能显示
伪地址–这个地址是重
定位表的位置。在某些架构里,GDB 可以将这些伪地址映射到函数名上。
set disassembly-flavor instruction-set
用 disassemble 或 x/i 命令反汇编程序时,选择指令集。
目前这个命令只在 Intel x86 族平台上定义。可以设置指令集为 intel 或 att。默认是 att,
基于 x86 系统的
Unix 汇编器默认使用 AT&T 风格。
show disassembly-flavor
显示当前反汇编风格的设置。
第八章 查看数据
在程序里查看数据的常用方式是用 print 命令(缩写为 p),或者用它的同义命令 inspect。
print 命令会计算和打印用程序语言写的表达
式的值(参见 12 章[不同语言下使用 GDB],127 页)。
print expr
print /f expr
expr 是表达式(用程序语言写的)。缺省情况下,expr 的值会以它的数据类型相近的格
式打印;也可以用’/f’选择不同的格式,f 是
格式描述符;参见 8.5 节[输出形式],85 页。
print
print /f
如果省略了 expr,GDB 将显示最后一次的值(从值的历史记录里;参见 8.9 节[值历史
记录],96 页)。这个命令允许用户方便的换
一种格式查看相同值。
用 x 命令可以在更低层次上查看数据。x 命令检验一个指定位置上的数据,并且以指定格
式打印出来。参见 8.6 节[查看内存],87 页。
如果对数据类型感兴趣的话,或者想知道一个结构体或类里的一个域是如何声明的话,用
ptype expr 命令取代 print 命令就可以做到了。
参见 13 章[查看符号表],151 页。
8.1 表达式
print 很许多其他 GDB 命令一样接受一个表达式作为参数并且计算它的值。在 GDB 里,
程序里定义的任意类型的常量,变量或者操作符都是有效的表达式。包括条件表 达式,函
数调用,类型转换和字符串常量。也包括预定义宏,如果在编译程序时包含了的话;参见
4.1 节[编译],25 页。
GDB 支持用户输入的表达式里包含数组常量。语法是{element,element…}。例如,可以用
命令 print {1,2,3}来在内存里建立一个数组,
而这个数组是目标系统里在自由堆里分配的。
由于 C 应用的如此广泛,因此本手册里的大多数例子里的表达式都是用 C 写的。关于在
其它语言里如何使用表达式的信息,参见 12 章[不同语言下使用 GDB],119 页。
在这节里,讨论可用于 GDB 表达式的与编程语言无关的操作符。
GDB 支持下列操作符,另外还有其它编程语言可以通用的操作符:
@ ‘@’是二进制操作符,将一块内存作为数组。更多信息,参见 8.3 节[伪数组],77
页。
:: ‘::’可以指定一个文件或函数里定义的变量。参见 8.2 节[程序变量],76 页。
{type} addr
引用存储于 addr 位置上的 type 类型的对象。addr 可能是一个表达式,其值是一个整型
或是指针(但需要圆括号来进行类型转换)。
无论在 addr 地址上存储的数据是何种类型,这个用法都是允许的。
8.2 程序变量
在程序里,最常用的表达式类型是使用变量的名字。
表达式里的变量应理解为存储与一个特定的堆栈帧里(参见 6.3 节[选择一个帧],64 页);
变量可以是下列两种:
·全局变量(或是文件里的静态变量 file-static)
·根据编程语言的变量生存规则,在当前执行的堆栈帧上是可见的变量
这意味着在函数
foo (a)
int a;
{
bar (a);
{
int b = test ();
bar (b);
}
}
程序在函数 foo 里执行时,用户可以检查和使用变量 a,但只能在程序执行于 b 声明的块内
(函数 bar)使用或检查变量 b。
有一个例外:可以引用生存期是单个文件的变量或函数,即使当前执行点不在这个文件上。
不过,很有可能有多个变量
或函数有相同的名字(在不同的源文件)。如果那样的话,引用重名的变量可能会有预想不
到的效果。如果需要,可以
指定一个特定函数或文件的静态变量,用双冒号(::)标记:
file::variable
function::variable
这里的 file 或 function 是这个静态变量的上下文的名字。对于文件名,可以用两个单引号将
文件名包起以让 GDB 将其作为
一单个词分析–例如,要打印’f2.c’文件里定义的全局变量 x:
(gdb) p ’f2.c’::x
与在 C++相同符号的用法相比,C 的’::’ 的用法很少有冲突。GDB 也支持 C++生存期
操作符。
警告:偶尔的,一个本地变量可能在函数的某些点上显示错误的值–在进入一个新的生
存期后和在离开前。
在用机器指令单步跟踪程序的时候,可能碰到这个问题。这是因为,在大多数机器里,需
要多于一个的指令才能建立一个
堆栈帧(包括本定变量定义);如果你是用机器指令来单步跟踪的话,变量就可能显示错误
的值,直到堆栈帧完全建立为止。
在退出时,通常也需要多个机器指令才能销毁堆栈帧;在你开始单步执行通过这组指令的过
程中,本地变量的定义可能已经
消失了。
这个问题也可能在编译器做了显著优化的时候碰到。要确保总是得到精确的值的话,在编
译时关闭优化选项。
编译器优化结果所带来的另一个潜在影响是将没有用到的变量优化掉了,或者叫爱那个变
量存储于寄存器(而不是内存地址
上)。由于依赖于编译器提供的调试信息格式对于此类问题的支持,GDB 可能不能显示这些
本地变量的值。如果真的这样,GDB
会打印类似如下的消息:
No symbol “foo” in current context.
要解决这些问题,要么不带优化选项重新编译,要么使用一个不同的调试信息格式,如果
编译器支持多种格式的话。例如,
GCC,GNU C/C++编译器,通常支持’-gstabs+’选项。’-gstabs+’产生优于普通调格式(如
COFF)的调试信息。可以用 DWARF 2
(’-gdwarf-2′),这也是一个有效的调试信息格式。参见节”调试程序或 GCC 的选项”。
参见 12.4.1 节[C 和 C++],123 页,更多关于
最佳 C++程序调试信息格式的信息。
如果需要打印一个 GDB 未知其内容的对象,例如,由于调试信息没有完全说明它的数据
类型,GDB 会打印’’。
更多信息,参见 13 章[符号],143 页。
字符串是定义为无符号的 char 数组。signed char 或 unsigned char 会以 1 字节宽度的整形
数组打印。由于 GDB 定义字符串类型 type
‘char’为无符号类型,-fsigned-char 或-funsigned-charGCC 选项不会起效。对于程序代码
char var0[] = “A”;
signed char var1[] = “A”;
可以在调试时得到下列信息
(gdb) print var0
$1 = “A”
(gdb) print var1
$2 = {65 ’A’, 0 ’\0’}
8.3 伪数组
打印几个在内存里连续的相同类型的对象,常常是很有用的;数组的一节,或一个动态决
定大小的数组,在程序里只有一个指针存在。
用二进制操作符’@‘将一个连续的内存区域作为伪数组,可以达到这个目的。’@‘的左操作
数应该是目标数组的第一个元素,并且是一个
单独的对象。右操作数应该是目标数组的长度。结果是整个数组的值,其元素都是左操作数
的类型。第一个元素是左操作数;第二个元素
在内存中数紧邻着第一个元素,依此类推。下面是一个例子。如果程序
int *array = (int *) malloc (len * sizeof (int));
可以用下面命令打印数组的内容
p *array@len
‘@’的左操作数必须存在于内存中。用’@‘打印的数组值和用其它下标索引得到的值一
样,并且会在表达式里强制转换为指针。伪数组
常常通过值历史在表达式里出现(参见 8.8 节[值历史],88 页),在打印过后。
另一种创建伪数组的方法是使用强制转化。转化会将一个值作为一个数组对待。这个值不
一定在内存里:
(gdb) p/x (short[2])0×12345678
$1 = {0×1234, 0×5678}
为方便起见,如果不指定数组长度(如’type[]value’),GDB 会计算这个伪数组合适的长
度(如’sizeof(value)/sizeof(type)’):
(gdb) p/x (short[])0×12345678
$2 = {0×1234, 0×5678}
有时伪数组机制还不够;在相当复杂的数据结构里,结构体里的元素可能不是真的相邻–
例如,如果你需要的元素在结构体里声明为指
针。一个有用的变通方法(参见 8.9 节[惯用变量],89 页)是在表达式里使用惯用变量作为
计数器,记录第一个值,然后通过回车键重复执行表达式。例如,假设有一个结构体数组名
为 dtab,其结构体定义了一个指针 fv。下面是一个使用 dtab 的例子:
set i = 0 p d t a b [ i = 0 p dtab[ i=0pdtab[i++]->fv



8.4 输出格式
缺省的,GDB 根据数据类型打印数据值。不过,有时这种方式可能不是你想要的。例如,
你可能要以 16 进制打印一个数值,或者以 10 进
制打印一个指针。或者你想要查看内存中某个地址上的数据,作为字符串或者一个指令。要
做到这些,在打印值的时候指定输出格式就可
以了。
最简单的输出格式的用法是指定如何打印一个已计算过的值。要达到这个目的,在命令
print 后加上反斜杠’/‘和一个格式符号就可以。
格式符号如下:
x 将数值作为整型数据,并以 16 进制打印。
d 打印带符号整型数据
u 打印以无符号整型数据
o 以 8 进制打印整形数据
t 以 2 进制打印整形。’t’代表’two’(注一).
注一:’b’不能用,因为这个格式在 x 命令里也用到了,x 命令里’b’代表’byte’;参见 8.5
节[查看内存],79 页。
a 打印地址,打印 16 进制的绝对地址和最近符号的偏移量。可以用这个格式找出一个未
知地址的位于何处(在哪个函数里):
(gdb) p/a 0×54320
$3 = 0×54320 <_initialize_vx+396>
命令 info symbol 0×54320 也能得到相似的结果。参见 13 章[符号]143 页。
c 将一个整型以字符常量打印。会打印一个数值和它表示的字符。超出 7 位的 ASCII 的
数值(大于 127)的字符用 8 进制的数字替代打
印。
不用这个格式的话,GDB 将 char,unsigned char 和 signed char 数据作为字符常量打印。
单字节的向量成员以整型数据打印。
f 将数据以浮点类型打印。
s 如果可能的话,以字符串形式打印。用这个格式,指向单字节数据的指针将以 nll 结尾
的字符串打印,单字节数据的数组则会
以定长字符串打印。其它的值以它们原本类型打印。
不用这个格式的话,GDB 将指向 char,unsigned char 和 signed char 作为字符串打印,
这些类型的数组也同样处理。单字节向量
的成员以整型数组打印。
例如,要以 16 进制打印程序计数器(参见 8.10 节[寄存器],90 页),输入
p/x $pc
注意,在反斜杠前不需要空格;这是由于 GDB 里的命令名不能包含一个反斜杠。
要用其它格式打印最近值历史里的值,可以用 print 命令带一个格式就可以了,不用指定
表达式。例如,’p/x’会以 16 进制打印最近的
值。
8.5 查看内存
可以用命令 x(表示”examine”)以多种格式查看内存,而和程序数据类型无关。
x/nfu addr
x addr
x 用 x 命令查看内存。
n,f 和 u 都是可选的参数,指定打印多长的内存数据和以何种格式打印之;addr 是需要
打印的内存的地址表达式。如果用默认的 nfu,不
需要输入反斜杠’/’。有几个命令可以方便的设置 addr 的默认值。
n,重复次数
10 进制整数;默认是 1。指定显示多长的内存(需要和单元长度 u 一起计算得到)。
f,显示格式
显示格式和 print 命令的格式一样(’x’,’d’,’u’,’o’,’t’,’a’,’c’,’f’,’s’),外加 ’i’
(表示机器指令格式)。默认是’x’(16
进制)。默认格式在用 x 或 print 命令的时候都会改变。
u,单元大小
单元大小如下:
b 字节
h 2 节节
w 4 字节。默认值。
g 8 字节。
每次用 x 指定单元长度,这个长度就成为默认值,知道下一次用 x 再设置。(对于’s’
和’i’格式,单元长度会被忽略而不会改写)
addr,要打印的起始位置
addr 是要 GDB 开始打印的内存起始位置。表达式不需要指针值(虽然其可能是一个指
针值);这个表达式总会翻译为内存中一个
字节的整型地址。参见 8.1 节[表达式],75 页,更多关于表达式的信息。默认的 addr
通常是紧随最近查看地址之后–但其它几个
命令也可以设置默认地址:info breakpoints(设置为最近断点的地址),info line(设置
一行代码的起始地址),和 print(
如果用了 print 来显示内存中的一个值)。
例如 ,’x/3uh 0×54320′打印 3 个半字(6 字节)的内存数据,以 10 进制整型格式打印
(’u’),从地址 0×54320 开始。’x/4xw s p ’打印 4 个字(’ w ′ )的内存数据,以 16 进制从堆栈指针指向的内存开始(这里’ sp’打印 4 个字(’w')的内存数据,以 16 进制从堆栈指针指向的内存开始(这里’ sp打印4个字(w)的内存数据,以16进制从堆栈指针指向的内存开始(这里sp’;参见 8.10
节[寄存器],90 页)打印。
由于制定单元长度的字符和制定输出格式的字符是截然不同的,因此不需要记住单元长度
和格式字符的先后顺序;无论哪个在先都可以
。输出格式声明’4xw’和’4wx’是一样的。(不过,次数 n 必须在先;’wx4′就是无效
的。)
即使对于格式’s’和’i’来说单元长度 u 是忽略不计的,用户也可能要用一个计数 n;例
如,’3i’指定要看 3 个机器指令,包括操作数。为方便起见,特别 是在用 display 命令时,’
i’格式会超过计数所知定的长度打印延迟转移槽指令,如果有的话,这个转移指令就紧接在
计数长度的指令之后。 disassemble 命令提供了另一种查看机器指令的方式;参见 7.6 节[源
代码和机器代码],72 页。
x 命令的全部缺省参数都为方便的继续扫描内存而设计的,这样每次继续使用 x 命令的时
候就只需要很少的指定了。例如,用’x/3i addr’命令查看机器指令后,可以只用’x/7′来
查看下 7 个指令。如果用回车键来重复 x 命令的话,就会重复 n 次;其它参数就成为接下来
的 x 命令的缺省 值。
由于 x 命令打印的地址和内容通常是非常多而且可能会变成瓶颈,因此不会在值历史里保
留。 相反,GDB 将那些在后续表达式里用到的值形成惯用变量 和 _和 __。一个 x 命令之后,
最后被查看的地址可以用惯用变量 来 在表达式里引用。这个地址的内容,如前所查,可以用变量 _来在表达式里引用。这个地址的 内容,如前所查,可 以用变量 在表达式里引用。这个地址的内容,如前所查,可以用变量__来引用。如果 x 命令带有重复次数参数,地址和内容会保存最后打印的内存单
元;如果有多个单元在最后一行打印的话,就不是 记录最后打印的地址。
如果调试一个在远程机器上运行的程序(参见 17 章[远程调试],179 页),你可能希望确
认和下载到远程机器上的可执行文件相比的内存中的程序文件。compare-sections 命令提供
了这样的功能。
compare-sections [section-name]
用可执行文件里的名为 section-name 的可加载段数据和远程机器内存中相同的段数据
比较,并且打印不匹配的数据。如果不带参数,比
较所有的可加载段数据。这个命令的可用性依赖于系统对于”qCRC”远程请求的支持与
否。
8.6 自动显示
如果你觉得需要频繁打印一个表达式的值(来查看其如何改变的),可能要把它加到自动
显示列表里让 GDB 在每次程序中断时打印这个表达式的值。每个加入列表的表达式会有一
个编号来标识;要将一个表达式从列表里删除,可以用这个编号。自动显示如下所示:
2: foo = 38
3: bar[5] = (struct hack *) 0×3804
这个输出显示了条目编号,表达式和它们目前的值。如同你手工用 x 或 print 命令那样打印
输出那样,可以指定你喜欢的输出格式;事实上
display 命令决定是用 print 还是用 x 命令,这取决于你指定的格式–如果你指定了’i’或者’
s’格式的话,或者有一个单元长度的话,就用 x;
否则就用 print。
display expr
将表达式 expr 加入表达式列表,每次程序中断时自动显示。参见 8.1 节[表达式],75
页。
在时候此命令后再按回车键时,display 不会重复执行。
display/fmt expr
fmt 只指定显示格式,不指定大小和次数,将表达式 expr 加入自动显示列表;每次用指
定格式 fmt 输出。参见 8.4 节[输出格式],
78 页。
display/fmt addr
对于’i’或者’s’格式,或者包含一个单元长度或一个单元数量的话,将表达式 addr
作为一个要查看的内存地址加入列表,每次程
序中断的时候打印。”查看”用’x/fmt addr’命令来实现。参见 8.5 节[查看内存],79
页。
例如,要在每次执行中断时查看机器指令,’display/i p c ’就很有用的(’ pc’就很有用的(’ pc就很有用的(pc’是程序
计数器的通用名;参见 8.10 节[寄存器],90 页)。
undisplay dnums…
delete display dnums…
从显示列表中删除编号为 dnums 的显示项。
在执行 undisplay 后再回车的话,undisplay 不会重复。(否则你会得到’No display
number….’的错误信息)
disable display dnums
禁用编号为 dnums 的显示项。禁用的显示项不会自动输出,但系统仍会记录它。可以
再次激活。
enable display dnums…
激活编号为 dnums 的显示项。会再次自动显示其表达式的值,直到你禁用它。
display
显示当前列表中的变大是的值,就如同程序中断那样输出。
info display
打印此前设置的自动显示列表里的表达式,每个表达式带一个编号,但不显示其值。包
括禁用的表达式,这类表达式会标明为禁
用。也包括目前不能显示的表达式,这类表达式引用了当前不可用的自动变量。
如果显示表达式引用了本地变量,那么在其设置范围外是没有意义的。在执行到其变量无
定义的上下文时,这类表达式会被禁用。
例如,如果你在一个函数内执行了命令 display last_char,last_char 是此函数的参数,GDB
会在程序再次执行到这个函数并在此函数中断
时自动显示此参数。要是在别的位置中断的话–那里没有变量 last_char–就会将此显示项自
动禁用。下次程序在 last_char 有意义的位置中
断时,可以再次激活这个显示表达式。
8.7 打印设置
GDB 提供如下数组,结构体和符号的打印设置方法。在任何语言里下列设置对于调试都
是很有用的:
set print address
set print address on
GDB 打印内存地址,显示堆栈回溯的位置,结构体的值,指针值,断点等等,甚至在
其也显示哪些地址上的内容时。缺省是 on。
例如,下面是用 set print address on 来设置后,堆栈帧的输出:
(gdb) f
#0 set_quotes (lq=0×34c78 “<<”, rq=0×34c88 “>>”)
at input.c:530
530 if (lquote != def_lquote)
set print address off
在现实其值的时候不打印地址。例如,下面是用 set print address off 设置后相同的堆栈
帧的输出:
(gdb) set print addr off
(gdb) f
#0 set_quotes (lq=”<<”, rq=”>>”) at input.c:530
530 if (lquote != def_lquote)
可以用’set print address off’来从 GDB 接口中取消所有机器相关的显示。例如,使用
print address off,可以在所有机器上
得到相同的内容的回溯–不论时候牵涉到指针参数。
show print address
显示是否打印地址。
GDB 打印符号的地址时,通常会打印离此地址最近且位置靠前的符号,外加打印偏移。
如果符号不能指定位置的地址(例如,在一个文
件里的一个名字),你就需要确认它。一个确认的方法是用 info line,例如’info line *0×4537
′。另外一方法是,在打印一个符号的地
址时设置 GDB 要打印的源文件和行号:
set print symbol-filename on
设置 GDB 要打印的符号的源文件名和行号。
set print symbol-filename off
不打印符号的源文件名和行号。默认方式。
show print symbo-filename
显示是否打印符号的源文件名和行号。
显示符号文件名和行号的另外一种有用的情况是,在反汇编代码时打印文件名和行号。
GDB 会显示相应于每个指令的行号和源文件。
另外,你可能希望确认被打印地址的符号形式是否是离得最近且位置考前的符号:
set print max-symbolic-offset max-offset
设置 GDB 只显示地址的符号形式,如果偏移是在最近且靠前的符号和比 max-offset 低
的地址区间。缺省值是 0,GDB 总是打印比
此地址靠前的符号。
show print max-symbolic-offset
显示 GDB 打印一个符号地址的最大偏移量。
如果有一个指针但不能确定它指向何处,可以用’set print symbol-filename on’来尝试。
然后用’p/a pointer’来确认此指针指向的
名字和源文件的位置。这个命令可以将地址转换为符号形式。例如,下面是 GDB 显示的变
量 ptt 指向另一个变量 t,于’hi2.c’定义:
(gdb) set print symbol-filename on
(gdb) p/a ptt
$4 = 0xe008

第十章 跟踪点
在某些应用程序里,调试器不大可能因为开发者要了解此程序的行为,长时间的中断程序
的执行。如果程序的正确性依赖于实时行为,调试器造成的延迟会导致程序根本改变其行为,
甚至在代码本省正确的情况下也可能导致失败。不中断程序的执行来观察其行为是非常有用
的功能。
使用 GDB 的 trace 或者 collect 命令,可以指定程序里的位置,称为跟踪点,和在跟踪点
执行到的时候要计算的任意表达式。稍后在跟踪点
执行到的时候,可以用 tfind 命令来查看表达式的值。表达式也可以引用内存里的对象–结
构体或者数组,例如—GDB 应该记录的值;在访
问一个特殊的跟踪点时,可以查看那些对象,如果在这个时间点上这些对象在内存里的话。
不过,由于 GDB 不需要和用户交互就可以记录这些值,所以 GDB 可以迅速优雅的记录而
不干扰调试程序运行。
目前,跟踪点功能只在远程系统上实现。参见第 16 章[目标],167 页。另外,远程系统必
须了解如何收集跟踪数据。这个功能实现于远程存根;不过,到这个 手册编写为止,没有
存根支持 GDB 提供的跟踪点。实现跟踪点的远程数据包格式参见 D.6 节[跟踪点数据包],
356 页。
本章说明跟踪点命令和功能。
10.1 设置跟踪点的命令
在运行跟踪尝试前,可以设置任意跟踪点号都。如同断点那样(参见 5.1.1 节[设置断点],
40 页),跟踪点由 GDB 分配编号。和断点一样,跟踪点编号是从 1 开始连续的整数。跟踪
点相关的命令,大多需要跟踪点号作为其参数来指定跟踪点。
可以为各个跟踪点指定要系统收集的任意的数据集,此数据存储于跟踪缓冲区里。收集的
数据包括寄存器,本地变量和全局数据。接下来,可以用 GDB 命令来查看这些数据的值。
本节说明设置跟踪点的命令,并设置相关条件和操作。
10.1.1 创建和删除跟踪点
trace trace 命令和 break 命令非常相似。其参数可以是源代码行,函数名或者目标程序的
某个地址。参见 5.1.1 节[设置断点] ,40 页。
trace 命令创建跟踪点,程序在此点上短暂中断,收集数据,然后程序继续往下执行。
设置跟踪点或者改变跟踪点命令直到下个
tstart 命令才会生效;因此,不能在跟踪会话过程中改变跟踪点的属性。
下面是使用 trace 命令的一些例子:
(gdb) trace foo.c:121 // 源文件和行号
(gdb) trace +2 // 当前行的下两行
(gdb) trace my function // 函数的第一行代码
(gdb) trace *my function // 函数的真正开始的地方
(gdb) trace *0×2117c4 // 某个地址
trace 可以简写为 tr。
惯用变量$tpnum 记录最近设置的跟踪点号。
delete tracepoint [num]
永久删除一个或多个跟踪点。不带参数的话,默认删除所有的跟踪点。
例如:
(gdb) delete trace 1 2 3 // 删除三个跟踪点
(gdb) delete trace // 删除所有跟踪点
此命令可以简写为 del tr。
10.1.2 激活和禁用跟踪点
disable tracepoint [num]
禁用跟踪点 num,或者所有跟踪点,如果不指定参数的话。已禁用的跟踪点在下一次跟
踪会话期将不再有效,但不会被系统遗忘。
enable tracepoint 命令可以再次激活已禁用的跟踪点。
enable tracepoint [num]
激活跟踪点 num,或者所有的跟踪点。已激活的跟踪点将在下一次跟踪会话期起效。
10.1.3 跟踪点通过计数
passcount [n [num]]
设置跟踪点的通过计数。使用通过计数可以自动中止跟踪会话。如果跟踪点通过计数是
n,那么跟踪会话会在跟踪点第 n 次执行到的
时候自动中止。如果没有指定跟踪点号 num,通过计数命令将设置最近创建的跟踪点。
如果没有指定通过计数,那么跟踪会话会在
一直执行,直到用户手动终止为止。
例如:
(gdb) passcount 5 2 // 跟踪点 2 在第 5 次执行时中止
(gdb) passcount 12 // 最近创建的跟踪点,在第 12 次执行时中断
(gdb) trace foo
(gdb) pass 3
(gdb) trace bar
(gdb) pass 2
(gdb) trace baz
(gdb) pass 1 // 在 foo 执行过 3 次,或者 bar 执行过 2 次,或者 baz 执行过 1 次时,
中止跟踪
10.1.4 跟踪点操作列表
action [num]
此命令设置在跟踪点执行到时执行的操作。如果没有指定跟踪点号 num,此命令会为最
近创建的跟踪点设置操作(因此创建跟踪点
后执行 actions 命令就不必要再费事指定跟踪点号了)。接下来指明要执行的操作,每个
操作一行,只有包含 end 的最后一行用来结
束操作列表。到目前为止,只定义了 collect 和 while-stepping 操作。
要删除跟踪点的所有操作,输入’actions num’,接下来立即输入 end。
(gdb) collect data // 收集某些数据
(gdb) while-stepping 5 // 单步执行 5 次,收集数据
(gdb) end // 操作结束
下面的例子里,操作列表从 collect 命令开始,在跟踪点执行到时收集数据。那么,要
单步执行和收集额外的数据,就要在设置单
步执行时收集数据之后执行 while-stepping 命令。需要用 end 命令来终结 while-stepping
命令。最后,用 end 命令来终结操作列表。
(gdb) trace foo
(gdb) actions
Enter actions for tracepoint 1, one per line:

collect bar,baz
collect $regs
while-stepping 12
collect $fp, $sp
end
end
collect expr1, expr2, …
在跟踪点执行到时,收集指定表达式的结果。此命令可以接受以逗号分隔的任意有效表
达式作为参数。另外,全局,静态或本地变
量以外的,也支持下列特殊的参数:
$regs 收集所有寄存器
$args 收集函数的所有参数
$locals 收集所有本地变量
可以连续使用 collect 命令,每个 collect 都可以有单独的参数,或者一个 collect 命令带
多个参数,以逗号分隔:效果是相同的。
命令 info scope(参见 13 章[符号],143 页)非常适合查出哪些数据是要收集的。
while-stepping n
在跟踪点后执行 n 次单步跟踪,每执行一步都收集一次数据。while-stepping 命令后接
要收集的数据列表(最后再接 end 结束 while-
stepping 命令):
while-stepping 12
collect $regs, myglobal
end

while-stepping 可以缩写为 ws 或者 stepping。
10.1.5 跟踪点列表
info tracepoints [num]
打印跟踪点 num 的信息。如果不指定跟踪点号,将显示所有跟踪点的信息。每个跟踪
点都包含下列信息:
·跟踪点编号
·是否激活或禁用
·跟踪点地址
·用 passcount n 命令设置的通过计数
·用 while-stepping n 命令设置的单步执行次数
·跟踪点设置于源代码的何处
·用 actions 命令设置的操作列表
(gdb) info trace
Num Enb Address PassC StepC What
1 y 0×002117c4 0 0 <gdb_asm>
2 y 0×0020dc64 0 0 in g_test at g_test.c:1375
3 y 0×0020b1f4 0 0 in get_data at …/foo.c:41
(gdb)
本命令可缩写为 info tp。
10.1.6 开始和中止跟踪会话
tstart 此命令不需要参数。开始一次跟踪会话,并开始收集数据。如果不保留上一次跟踪
会话期间收集的数据的话,可能会带来一些副
作用。
tstop 此命令不需要参数。结束一次跟踪会话,并停止收集数据。
注意:一次跟踪会话和数据收集可能在达到跟踪点通过计数时自动终止(参见 10.1.3
节[跟踪点通过计数],106 页),或者在跟踪
缓冲区满的时候也可能导致终止。
tstatus 此命令显示当前跟踪数据收集的状态。
下面是关于目前为止我们介绍的命令的例子:
(gdb) trace gdb c test
(gdb) actions
Enter actions for tracepoint #1, one per line.

collect r e g s , regs, regs,locals,$args
while-stepping 11
collect r e g s e n d e n d ( g d b ) t s t a r t [ t i m e p a s s e s . . . ] ( g d b ) t s t o p 10.2 使用已收集的数据跟踪会话结束以后,可以使用 G D B 命令来检查跟踪数据。基本的概念是,在达到跟踪点的时候每次收集一个跟踪快照,此外每次单步跟踪的时候都收集一次快照。所有这些快照都保存与跟踪缓冲区里,并且是从 0 开始连续编号的,所以可以供用户在以后来查看。要查看这些快照,需要明确指定是哪一个。如果远程代理指定了某个跟踪快照的话,在接到 G D B 的请求时,它会从缓冲区里读取此快照相应的内存和寄存器,而不是从实际的内存或寄存器里读取内容,反馈给 G D B 。这就意味着 G D B 所有命令( p r i n t , i n f o r e g i s t e r s , b a c k t r a c e 等等)都会像正在调试程序期间一样工作,就如同在跟踪点发生时那样。如果请求的数据不在缓冲区里,此请求将会失败。 10.2.1 t f i n d n 从缓冲区里选择一个跟踪快照的基本命令是 t f i n d n , t f i n d 查找编号为 n 的跟踪快照,从 0 开始。如果没有指定参数,那么会选择下一个快照。下面是 t f i n d 命令各种形式。 t f i n d s t a r t 查找第一个快照。 t f i n d 0 的同义词(因为 0 是第一个快照的编号)。 t f i n d n o n e 停止调试跟踪快照,重新开始现场调试。 t f i n d e n d 和’ t f i n d n o n e ’相同。 t f i n d 不带参数代表查找下一个跟踪快照。 t f i n d − 查找当前快照的前调试过的快照。这就允许再次跟踪此前的步骤。 t f i n d t r a c e p o i n t n u m 查找跟踪点编号 n u m 相对应的下一个快照。搜索从最近查看的跟踪点快照开始。如果不带参数 n u m 的,查找当前跟踪点的下一个快照。 t f i n d p c a d d r 查找程序计数器地址 a d d r 对应的下一个快照。搜索从最近查看的跟踪点快照开始。如果不带参数的话,查找当前快照的 P C 对应的下一个快照。 t f i n d o u t s i d e a d d r 1 , a d d r 2 查找指定范围之外的 P C 对应的下一个快照。 t f i n d r a n g e a d d r 1 , a d d r 2 查找指定范围内的 P C 对应的下一个快照。 t f i n d l i n e [ f i l e : ] n 查找源代码行 n 对应的下一个快照。如果指定了可选参数 f i l e ,那么指定是此源文件的代码行 n 。搜索从最近查看的跟踪点快照开始。如果没有指定参数 n ,代表查找下一行,而不是当前检查的这一行;因此,重复 t f i n d l i n e 就好象在现场调试期间的单步跟踪一样。 t f i n d 命令的默认参数是特别设计的,使得它方便的在跟踪缓冲区里搜索。例如,不带参数的 t f i n d 命令选择下一个跟踪快照,而不带参数的 t f i n d − 命令选择前一个跟踪快照。所以,用一个 t f i n d 命令,再按回车键重复就可以依次检查所有的跟踪快照。或者,用 t f i n d − 再接着重复按回车键就可以反向检查快照了。不带参数的 t f i n d l i n e 命令选择下一行代码对应的快照。不带参数的 t f i n d p c 命令选择当前堆栈帧上保存程序计数器 P C 对应的下一个快照。不带参数的 t f i n d t r a c e p o i n t 命令选择当前跟踪点上收集的下一个快照。除了让用户可以手动在跟踪缓冲区里搜索之外,这些命令也能方便的构建 G D B 脚本,搜索跟踪缓冲区并打印用户感兴趣的数据。因此,如果我们想要检查缓冲区里的每个跟踪帧里的 P C , F P 和 S P 寄存器,我们可以用下面这些命令: ( g d b ) t f i n d s t a r t ( g d b ) w h i l e ( regs end end (gdb) tstart [time passes ...] (gdb) tstop 10.2 使用已收集的数据 跟踪会话结束以后,可以使用 GDB 命令来检查跟踪数据。基本的概念是,在达到跟踪点 的时候每次收集一个跟踪快照,此外每次单步跟踪 的时候都收集一次快照。所有这些快照都保存与跟踪缓冲区里,并且是从 0 开始连续编号的, 所以可以供用户在以后来查看。要查看这些快 照,需要明确指定是哪一个。如果远程代理指定了某个跟踪快照的话,在接到 GDB 的请求 时,它会从缓冲区里读取此快照相应的内存和寄存 器,而不是从实际的内存或寄存器里读取内容,反馈给 GDB。这就意味着 GDB 所有命令 (print,info registers,backtrace 等等)都会像正在 调试程序期间一样工作,就如同在跟踪点发生时那样。如果请求的数据不在缓冲区里,此请 求将会失败。 10.2.1 tfind n 从缓冲区里选择一个跟踪快照的基本命令是 tfind n,tfind 查找编号为 n 的跟踪快照,从 0 开始。如果没有指定参数,那么会选择下一个快 照。 下面是 tfind 命令各种形式。 tfind start 查找第一个快照。tfind 0 的同义词(因为 0 是第一个快照的编号)。 tfind none 停止调试跟踪快照,重新开始现场调试。 tfind end 和’tfind none’相同。 tfind 不带参数代表查找下一个跟踪快照。 tfind - 查找当前快照的前调试过的快照。这就允许再次跟踪此前的步骤。 tfind tracepoint num 查找跟踪点编号 num 相对应的下一个快照。搜索从最近查看的跟踪点快照开始。如果 不带参数 num 的,查找当前跟踪点的下一个快照 。 tfind pc addr 查找程序计数器地址 addr 对应的下一个快照。搜索从最近查看的跟踪点快照开始。如 果不带参数的话,查找当前快照的 PC 对应的下 一个快照。 tfind outside addr1,addr2 查找指定范围之外的 PC 对应的下一个快照。 tfind range addr1,addr2 查找指定范围内的 PC 对应的下一个快照。 tfind line [file:]n 查找源代码行 n 对应的下一个快照。如果指定了可选参数 file,那么指定是此源文件的 代码行 n。搜索从最近查看的跟踪点快照开始 。如果没有指定参数 n,代表查找下一行,而不是当前检查的这一行;因此,重复 tfind line 就好象在现场调试期间的单步跟踪一 样。 tfind 命令的默认参数是特别设计的,使得它方便的在跟踪缓冲区里搜索。例如,不带参 数的 tfind 命令选择下一个跟踪快照,而不带参数 的 tfind -命令选择前一个跟踪快照。所以,用一个 tfind 命令,再按回车键重复就可以依次 检查所有的跟踪快照。或者,用 tfind -再接着 重复按回车键就可以反向检查快照了。不带参数的 tfind line 命令选择下一行代码对应的快 照。不带参数的 tfind pc 命令选择当前堆栈帧上 保存程序计数器 PC 对应的下一个快照。不带参数的 tfind tracepoint 命令选择当前跟踪点上 收集的下一个快照。 除了让用户可以手动在跟踪缓冲区里搜索之外,这些命令也能方便的构建 GDB 脚本,搜 索跟踪缓冲区并打印用户感兴趣的数据。因此,如 果我们想要检查缓冲区里的每个跟踪帧里的 PC,FP 和 SP 寄存器,我们可以用下面这些命 令: (gdb) tfind start (gdb) while ( regsendend(gdb)tstart[timepasses...](gdb)tstop10.2使用已收集的数据跟踪会话结束以后,可以使用GDB命令来检查跟踪数据。基本的概念是,在达到跟踪点的时候每次收集一个跟踪快照,此外每次单步跟踪的时候都收集一次快照。所有这些快照都保存与跟踪缓冲区里,并且是从0开始连续编号的,所以可以供用户在以后来查看。要查看这些快照,需要明确指定是哪一个。如果远程代理指定了某个跟踪快照的话,在接到GDB的请求时,它会从缓冲区里读取此快照相应的内存和寄存器,而不是从实际的内存或寄存器里读取内容,反馈给GDB。这就意味着GDB所有命令(print,inforegisters,backtrace等等)都会像正在调试程序期间一样工作,就如同在跟踪点发生时那样。如果请求的数据不在缓冲区里,此请求将会失败。10.2.1tfindn从缓冲区里选择一个跟踪快照的基本命令是tfindntfind查找编号为n的跟踪快照,从0开始。如果没有指定参数,那么会选择下一个快照。下面是tfind命令各种形式。tfindstart查找第一个快照。tfind0的同义词(因为0是第一个快照的编号)。tfindnone停止调试跟踪快照,重新开始现场调试。tfindendtfindnone相同。tfind不带参数代表查找下一个跟踪快照。tfind查找当前快照的前调试过的快照。这就允许再次跟踪此前的步骤。tfindtracepointnum查找跟踪点编号num相对应的下一个快照。搜索从最近查看的跟踪点快照开始。如果不带参数num的,查找当前跟踪点的下一个快照。tfindpcaddr查找程序计数器地址addr对应的下一个快照。搜索从最近查看的跟踪点快照开始。如果不带参数的话,查找当前快照的PC对应的下一个快照。tfindoutsideaddr1,addr2查找指定范围之外的PC对应的下一个快照。tfindrangeaddr1,addr2查找指定范围内的PC对应的下一个快照。tfindline[file:]n查找源代码行n对应的下一个快照。如果指定了可选参数file,那么指定是此源文件的代码行n。搜索从最近查看的跟踪点快照开始。如果没有指定参数n,代表查找下一行,而不是当前检查的这一行;因此,重复tfindline就好象在现场调试期间的单步跟踪一样。tfind命令的默认参数是特别设计的,使得它方便的在跟踪缓冲区里搜索。例如,不带参数的tfind命令选择下一个跟踪快照,而不带参数的tfind命令选择前一个跟踪快照。所以,用一个tfind命令,再按回车键重复就可以依次检查所有的跟踪快照。或者,用tfind再接着重复按回车键就可以反向检查快照了。不带参数的tfindline命令选择下一行代码对应的快照。不带参数的tfindpc命令选择当前堆栈帧上保存程序计数器PC对应的下一个快照。不带参数的tfindtracepoint命令选择当前跟踪点上收集的下一个快照。除了让用户可以手动在跟踪缓冲区里搜索之外,这些命令也能方便的构建GDB脚本,搜索跟踪缓冲区并打印用户感兴趣的数据。因此,如果我们想要检查缓冲区里的每个跟踪帧里的PCFPSP寄存器,我们可以用下面这些命令:(gdb)tfindstart(gdb)while(trace frame != -1)
printf “Frame %d, PC = %08X, SP = %08X, FP = %08X\n”, \
$trace_frame, $pc, $sp, f p t f i n d e n d F r a m e 0 , P C = 0020 D C 64 , S P = 0030 B F 3 C , F P = 0030 B F 44 F r a m e 1 , P C = 0020 D C 6 C , S P = 0030 B F 38 , F P = 0030 B F 44 F r a m e 2 , P C = 0020 D C 70 , S P = 0030 B F 34 , F P = 0030 B F 44 F r a m e 3 , P C = 0020 D C 74 , S P = 0030 B F 30 , F P = 0030 B F 44 F r a m e 4 , P C = 0020 D C 78 , S P = 0030 B F 2 C , F P = 0030 B F 44 F r a m e 5 , P C = 0020 D C 7 C , S P = 0030 B F 28 , F P = 0030 B F 44 F r a m e 6 , P C = 0020 D C 80 , S P = 0030 B F 24 , F P = 0030 B F 44 F r a m e 7 , P C = 0020 D C 84 , S P = 0030 B F 20 , F P = 0030 B F 44 F r a m e 8 , P C = 0020 D C 88 , S P = 0030 B F 1 C , F P = 0030 B F 44 F r a m e 9 , P C = 0020 D C 8 E , S P = 0030 B F 18 , F P = 0030 B F 44 F r a m e 10 , P C = 00203 F 6 C , S P = 0030 B E 3 C , F P = 0030 B F 14 或者,如果想要检查缓冲区里每行代码里的变量 X : ( g d b ) t f i n d s t a r t ( g d b ) w h i l e ( fp tfind end Frame 0, PC = 0020DC64, SP = 0030BF3C, FP = 0030BF44 Frame 1, PC = 0020DC6C, SP = 0030BF38, FP = 0030BF44 Frame 2, PC = 0020DC70, SP = 0030BF34, FP = 0030BF44 Frame 3, PC = 0020DC74, SP = 0030BF30, FP = 0030BF44 Frame 4, PC = 0020DC78, SP = 0030BF2C, FP = 0030BF44 Frame 5, PC = 0020DC7C, SP = 0030BF28, FP = 0030BF44 Frame 6, PC = 0020DC80, SP = 0030BF24, FP = 0030BF44 Frame 7, PC = 0020DC84, SP = 0030BF20, FP = 0030BF44 Frame 8, PC = 0020DC88, SP = 0030BF1C, FP = 0030BF44 Frame 9, PC = 0020DC8E, SP = 0030BF18, FP = 0030BF44 Frame 10, PC = 00203F6C, SP = 0030BE3C, FP = 0030BF14 或者,如果想要检查缓冲区里每行代码里的变量 X: (gdb) tfind start (gdb) while ( fptfindendFrame0,PC=0020DC64,SP=0030BF3C,FP=0030BF44Frame1,PC=0020DC6C,SP=0030BF38,FP=0030BF44Frame2,PC=0020DC70,SP=0030BF34,FP=0030BF44Frame3,PC=0020DC74,SP=0030BF30,FP=0030BF44Frame4,PC=0020DC78,SP=0030BF2C,FP=0030BF44Frame5,PC=0020DC7C,SP=0030BF28,FP=0030BF44Frame6,PC=0020DC80,SP=0030BF24,FP=0030BF44Frame7,PC=0020DC84,SP=0030BF20,FP=0030BF44Frame8,PC=0020DC88,SP=0030BF1C,FP=0030BF44Frame9,PC=0020DC8E,SP=0030BF18,FP=0030BF44Frame10,PC=00203F6C,SP=0030BE3C,FP=0030BF14或者,如果想要检查缓冲区里每行代码里的变量X(gdb)tfindstart(gdb)while(trace frame != -1)
printf “Frame %d, X == %d\n”, $trace_frame, X
tfind line
end
Frame 0, X = 1
Frame 7, X = 2
Frame 13, X = 255
10.2.2 tdump
本命令没有参数。tdump 打印在当前跟踪快照里所有收集到的数据。
(gdb) trace 444
(gdb) actions
Enter actions for tracepoint #2, one per line:
collect $regs, $locals, $args, gdb_long_test
end
(gdb) tstart
(gdb) tfind line 444
#0 gdb_test (p1=0×11, p2=0×22, p3=0×33, p4=0×44, p5=0×55, p6=0×66)
at gdb_test.c:444
444 printp( “%s: arguments = 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n”, )
(gdb) tdump
Data collected at tracepoint 2, trace frame 1:
d0 0xc4aa0085 -995491707
d1 0×18 24
d2 0×80 128
d3 0×33 51
d4 0×71aea3d 119204413
d5 0×22 34
d6 0xe0 224
d7 0×380035 3670069
a0 0×19e24a 1696330
a1 0×3000668 50333288
a2 0×100 256
a3 0×322000 3284992
a4 0×3000698 50333336
a5 0×1ad3cc 1758156
fp 0×30bf3c 0×30bf3c
sp 0×30bf34 0×30bf34
ps 0×0 0
pc 0×20b2c8 0×20b2c8
fpcontrol 0×0 0
fpstatus 0×0 0
fpiaddr 0×0 0
p = 0×20e5b4 “gdb-test”
p1 = (void *) 0×11
p2 = (void *) 0×22
p3 = (void *) 0×33
p4 = (void *) 0×44
p5 = (void *) 0×55
p6 = (void *) 0×66
gdb_long_test = 17 ’\021’
(gdb)
10.2.3 save-tracepoints filename
本命令将当前所有跟踪点的定义以及它们的操作和通过计数保存到文件’filename’里, 以
便以后的调试会话里使用。要读取保存的跟踪
点定义,使用 sourcce 命令(参见 20.3 节[命令文件],221 页)。
10.3 跟踪点的惯用变量
(int) $trace_frame
当前跟踪快照(也称为帧)编号,或者-1,如果没有选择快照的话。
(int) $tracepoint
当前跟踪快照的跟踪点。
(int) $trace_line
当前跟踪快照的行号。
(char []) $trace_file
当前跟踪快照的源文件。
(char []) t r a c e f u n c 包含 trace_func 包含 tracefunc包含tracepoint 的函数名。
注意:$trace_file 不适于用 printf,用 output 打印。
下面是使用这些惯用变量的例子,单步执行跟踪快照并打印数据。
(gdb) tfind start
(gdb) while $trace frame != -1
output $trace_file
printf “, line %d (tracepoint #%d)\n”, $trace_line, $tracepoint
tfind
end
译注:
stub,翻译成”存根”还是”代理”,”存根”感觉有点拗口,对应于中文的感觉不是很舒服;”
代理”又觉得不能完整表达原文的意义。
第十一章 调试使用覆盖技术的程序
如果程序大到不能放到系统的内存里,你可以使用覆盖技术来规避这个问题。GDB 提供
了调试使用覆盖技术的程序的支持。
11.1 覆盖是如何工作的
假设你的计算机的指令地址空间只有 64KB 长,而有超过此长度的内存可供别的方式访
问:例如:特殊指令,段寄存器,或者内存管理硬件。进一步假设,你要将一个超过 64KB
长的程序加载到此计算机上运行。
一个解决方法是,将相对独立且相互之间没有直接调用的程序模块单独标识;这些模块称
为覆盖段。将这些覆盖段从主程序里隔离,将它们的机器码放在更大的内存中。将主程序放
在指令内存中,而同时保留至少足够大的空间来存放最大的覆盖段。
现在,要调用一个位于覆盖段上的函数,首先必须从大内存里将此覆盖段的机器代码复制
到指令内存中来,并跳转到其入口点。

覆盖代码
这个图(参见[覆盖代码],113 页)展示了将数据和指令地址空间分割的系统。要映射一
个覆盖段,程序需要从大地址空
间将代码复制到指令空间。由于上面介绍的所有复占位段使用相同的映射地址,所以在同一
时刻只有一个可以映射。对于数据和指令使用同一个地址空间的系统,原理是相同的,只是
程序变量,堆,主程序和覆盖段共享一个地址空间。
一个已经加载进指令内存且准备好使用的覆盖段称为已映射的覆盖段;其映射到地址是指
令内存中的地址。指令空间中未加载(或部分加载)的覆盖段称为未映射覆 盖段;其加载
地址是在大内存中的地址。已映射地址也称为虚拟内存地址,或 VMA;加载地址也称为加
载内存地址,或者 LMA。
不幸的是,对于将程序放置在有限指令内存的方法,覆盖段不完全是透明的方式。它们引
进了一些新的全局行限制,在设计程序时必须时刻牢记在心:
·在调用前或返回到一个覆盖段里的函数时,程序必须保证此段是已映射的。否则,调用或
返回会将控制交给此实际地址,但错误的段,程序很可能会崩溃。
·如果系统里映射覆盖段的处理很昂贵,那就需要仔细的选择覆盖段来减少其对程序性能的
影响。
· 加载进系统的可执行文件必须包含每个覆盖段的指令,就是说覆盖段的加载地址,不是
它们的已映射地址。不过,每个重复占位的指令必须重新重新定位,其符号就 如同段在它
的已映射地址上。可以用 GNU 连接器脚本来为程序的各个段指定不同的加载和重定位地址;
参见使用 ld:GNU 连接器,节”覆盖段描述”。
·系统加载可执行文件的程序不但要能够将可执行文件的内容加载进大地址空间,也要能加
载进指令和数据空间。
上面介绍的覆盖段系统是相对简单的,还可以有许多改进的方式:
·如果系统有合适的体开关寄存器或者存储器管理硬件的话,可以用这些工具来让覆盖段的
加载区域内容简单的映射到在经过映射后的指令地址空间里。这个方式很可能比复制覆盖段
的内容到映射区域去要快得多。
·如果覆盖段足够小,可以一次设置多个段,并且可以同时映射多个段。
· 可以用覆盖段来管理数据,就如同指令那样。通常来说,数据覆盖段比代码覆盖段更加
不透明:只有在调用或返回到代码覆盖段的时候,才需要关心,数据覆盖段却 时刻需要注
意你所访问的数据。而且,如果改变了一个数据覆盖段的内容,在复制其它的数据覆盖段到
此映射区域前,必须将其内容复制回其加载地址。
11.2 覆盖命令
要使用 GDB 对覆盖的支持,程序里每个覆盖必须对应可执行文件里的一个单独段。段虚
拟内存地址和加载内存地址必须是覆盖段的映射和加载地址。将覆盖段和程序段等价使得
GDB 可以判断函数或变量的恰当的地址,由此覆盖段是否映射就可以推测。
所有 GDB 覆盖段命令都以 overlay 开头;可以缩写为 ov 获 ovly。这些命令是:
overlay off
禁用 GDB 对覆盖段的支持。禁用覆盖的支持之后,GDB 会假设所有的函数和变量都位
于其映射地址上。缺省的,GDB 禁用
对覆盖的支持。
overlay manual
激活手动覆盖调试。在此模式下,GDB 由用户告知映射的是哪个段,哪个段未映射,
使用下面介绍的 overlay map-overlay
和 overlay unmap-overlay 命令。
overlay map-overlay overlay
overlay map overlay
通知 GDB 段已映射;overlay 必须是目标文件包含此覆盖段的名字。段映射后,GDB
假定可以在映射地址上查找到覆盖段的函
数和变量。GDB 假定其他会重叠在此映射地址上的段都未映射。
overlay unmap-overlay overlay
overlay unmap overlay
通知 GDB 段解除映射;overlay 必须是目标文件包含此覆盖段的名字。段解除映射之后,
GDB 假定段的函数和变量都位于其加载地
址上。
overlay auto
激活自动覆盖调试。在此模式下,GDB 会查询一个由内部覆盖管理器维护的数据结构,
以此得知映射的哪一个段。更多
细节,参见 11.3 节[自动覆盖调试],446 页。
overlay load-target
overlay load
从内部重读覆盖段表。通常,每次 GDB 在内部中断时都会自动重读此表,因此这个命
令只在手动该表段映射时才需要使用。此命
令只在使用自动段调试时才有用。
overlay list-overlays
overlay list
打印当前已映射段的列表,输出映射地址,加载地址和大小。
通常,GDB 在打印一个代码地址时,会包含函数名,和地址:
(gdb) print main
$3 = {int ()} 0×11a0
当覆盖段调试启用后,GDB 能够识别未映射段上的代码,并用符号来标识次未映射函数名。
例如,如果 foo 是位于未映射段上的函数,
GDB 会如下打印:
(gdb) overlay list
No sections are mapped.
(gdb) print foo
$5 = {int (int)} 0×100000 <foo>
foo 对应的段映射后,GDB 会正常方式打印函数名:
(gdb) overlay list
Section .ov.foo.text, loaded at 0×100000 – 0×100034,
mapped at 0×1016 – 0×104a
(gdb) print foo
$6 = {int (int)} 0×1016
覆盖调试启用后,GDB 就可以在覆盖段上正确地查找函数和变量的地址了。因此,大多
数 GDB 命令,如 break 何 disassemble,就可以正常使用了,即使是在未映射段上也可以。
然而,GDB 断点支持还是有一些限制:
·只要 GDB 可以往在加载地址上的段上写入数据,就可以在此未映射段上的函数里设置断
点。
·GDB 不能在未映射段上设置硬件和基于模拟器的断点。不过,如果在覆盖管理器的尾部
上设置断点的话(并告诉 GDB 映射的是哪个段,如果手动管理重覆盖段的话),GDB 恰当
地会重新设置其断点。
11.3 自动覆盖调试
只要覆盖管理器提供一些简单的协作,GDB 就可以自动跟踪段的映射情况。如果用 overlay
auto 命令启用了自动段调试(参见 11.2 节[覆盖命令],114 页),GDB 会从内部存储器里查
找某些变量来,就可以得到当前段的状态。
要支持 GDB 自动覆盖调试,下面是覆盖管理器必须定义的变量:
_ovly_table:
这个变量必须是下面结构体的数组:
struct
{
/
The overlay’s mapped address. /
unsigned long vma;
/
The size of the overlay, in bytes. /
unsigned long size;
/
The overlay’s load address. /
unsigned long lma;
/
Non-zero if the overlay is currently mapped;
zero otherwise. /
unsigned long mapped;
}
_novlys:
这个变量必须是一个 4 字节的有符号整数,存储_ovly_table 的数量。
要判断一个特殊段映射与否,GDB 会从_ovly_table 里查找一个节点,比较其 vma 和 lma
是否和此段在可执行文件上的 VMA 和 LMA 相等。GDB 查到
匹配的节点时,就会通过结构体成员 mapped 来判断此段是否已经映射了。
另外,段管理器也可能定义一个函数,函数名为_ovly_debug_event。如果定义了此函数,
GDB 可以悄悄地在此函数上设置一个断点。如果
段管理器在其改变了段表时调用这个函数,就可以使得 GDB 能够准确的跟踪段的映射情况,
并且更新其上设置的断点。即使段还在 ROM 或者其
它不可写内存上,还没有执行的时候,这个功能也能让断点能够执行。
11.4 覆盖示例程序
在链接使用了覆盖的程序时,必须将段置于其加载地址上,而在运行时再将其重定位于映
射地址上。要达到这个目的,必须写一个连接器脚本(参见节”覆盖介绍” 使用 ld:GNU
连接器)。不幸的是,由于连接器脚本是和宿主系统,目标架构和目标内存布局高度相关的,
本手册不能提供可移植的示例代码来演示 GDB 的覆 盖支持。
不过,作为 GDB 源代码的测试套件的一部分,GDB 源代码发布版包含了一个覆盖程序,
带有支持一些系统的连接器脚本。程序由’gdb/testsuite/gdb.base’目录下的文件组成:
‘overlays.c’
主程序文件。
‘ovlymgr.c’
覆盖管理器,由’overlays.c’调用。
‘foo.c’
‘bar.c’
‘baz.c’
‘grbx.c’
覆盖模块,由’overlays.c’加载和使用。
‘d10v.ld’
‘m32r.ld’
连接器脚本,链接生成测试程序的目标 d10v-els 和 m32r-elf 目标。
用 d10v-elf GCC 交叉编译器来生成测试程序:
$ d10v-elf-gcc -g -c overlays.c
$ d10v-elf-gcc -g -c ovlymgr.c
$ d10v-elf-gcc -g -c foo.c
$ d10v-elf-gcc -g -c bar.c
$ d10v-elf-gcc -g -c baz.c
$ d10v-elf-gcc -g -c grbx.c
$ d10v-elf-gcc -g overlays.o ovlymgr.o foo.o bar.o \
baz.o grbx.o -Wl,-Td10v.ld -o overlays
各个架构的编译的过程都一样,除了必须将相应的编译器和链接器替换为相应目标系统的
编译器和链接器。
第十二章 用 GDB 调试不同语言编写的程

虽然编程语言通常都有一些共同的东西,但是事实上它们却很少能够用相同的形式来表
述。例如,在 ANSI C 里,对指针 p 取值是用
p 来表示的,而在 Modula-2 里,用 p^。值的
表示(或者显示)也可能不相同。C 里的 16 进制数的表示形如”0×1ae”, 而在 Modula-2
里表示如”1AEH”。
GDB 里内置了某些语言的与其相关的信息,可以用程序原语言来执行类似上述的操作,
也使得 GDB 可以和程序元语言一致的形式输出值。用来建立表达式的语言成为工作语言。
12.1 切换源代码语言
有两种方式可以控制工作语言–一种是让 GDB 自动设置,一种是用户手动设置。可以用
set language 命令来达到意图。GDB 在启动时会自动设置语言。工作语言用来决定如何翻译
用户输入的表达式,如何打印这些结果,等等。
除了工作语言之外,GDB 能识别的源文件都有其自己的工作语言。对于某些目标文件格
式,编译器可能会提示此特定源文件是那种语言。不过,大多数时候 GDB 都是从文件名里
推断语言种类的。源文件的语言控制 C++名字是否去修饰符–这种方式是 backtrace 以其自
身语言来恰当地显示每一个帧。参见 12.2 节[显示语言],120 页。
使用程序时,例如 cfront 或 f2c,这类程序产生 C 的目标代码,但其本身是用其它语言编
写的,通常都会碰到问题。在此情况下,让程序在其 C 输出里使用#line 指令;这种方式可
以让 GDB 知道原程序代码正确的语言,并能显示源代码,而不是显示其所产生的 C 代码。
12.1.1 文件扩展名和语言列表
如果源文件的扩展名是下列名称之一,那么 GDB 就会推断其语言。
‘.ada’
‘.ads’
‘.adb’
‘.a’ Ada 源文件.
‘.c’ C 源文件
‘.C’
‘.cc’
‘.cp’
‘.cpp’
‘.cxx’
‘.c++’ C++ 源文件
‘.m’ Objective-C 源文件
‘.f’
‘.F’ Fortran 源文件
‘.mod’ Modula-2 源文件
‘.s’
‘.S’ 汇编源文件。和 C 一样执行,但在单步跟踪时 GDB 不会跳过函数的序言。
另外,可以设置和文件扩展名相关的语言。参见 12.2 节[显示语言],120 页。
12.1.2 设置工作语言
如果允许 GDB 自动设置语言的话,那么在程序和调试会话里表达式就以相同的方式转换。
如果用户期望的话,也可以手动设置。要手动设置,执行命令’set language lang’,lang
是语言的名称,例如 c 和 modula-3。要得到 GDB 支
持的语言列表,执行命令’set language’。
手动设置语言将妨碍 GDB 自动更新工作语言。在调试工作语言和源代码语言不想同时,
如果两种语言都可以接受表达式—但表示不同的事情的话,就可能导致一些困惑了。例如,
如果当前的源文件是用 C 写的,GDB 分析 Modula-2,如下的命令:
print a = b + c
可能不会得到你所期望的那个结果。在 C 里,这个命令表示得失将 b 和 c 相加,并将其结
果赋值给 a。结果是打印出 a 的值。在 Modula-2 里,这个命令表示比较 a 和 b+c 的结果,产
生一个布尔值。
12.1.3 让 GDB 推断源语言
要让 GDB 自动设置工作语言,用’set language local’或者’set language auto’。GDB 就
会推断工作语言了。也就是说,程序在一个堆栈帧里中断的时候(通常是遇到了一个断点),
GDB 就会为此栈上的函数设置已记录的工作语 言。如果此帧的语言未知(那就是说,此帧
上的函数或代码块定义于某个源文件,此文件的扩展名却是未知的),当前工作语言不会改
变,GDB 会发出一个警告。
这个命令对于多数只用同一种源代码语言编写的程序来说不是必须的。不过,用同一个源
代码语言编写的程序模块和库可以被不同源语言编写的主程序调用。使用’set language auto’
可以免除用户手动设置工作语言之苦。
12.2 显示语言
下面的命令可以助你找出何种工作语言,并找出程序是哪种源代码语言编写的。
show language
显示当前工作语言。可以用这个语言来执行命令(如 print)来建立和计算表达式,表
达式里可以引用程序里的变量。
info frame
显示此帧的源代码语言。如果从此帧上使用了一个标志符的话,这个语言就变成工作语
言。要识别列于此处的其他信息,参见 6.4 节
[堆栈帧的信息],65 页。
info source
显示此源代码的源代码语言。要识别列于此处的其他信息,参见 13 章[检验符号表],
143 页。
在某些异常情况下,源文件的扩展名可能不在标准列表里。可以明确将语言和扩展名关联
起来。
set extension-language ext language
设置 GDB 将扩展名 ext 的文件认为是由源代码语言 language 编写的。
info extensions
列出所有的文件扩展名和相关连的语言。
12.3 类型和域检查
警告:在本版手册里,包括了 GDB 类型和域检查命令,但还没有效用。本节文档介绍了
预期的功能。
通过编译期和运行时检查,某些语言的设计可以防止程序员犯一些常见的错误。这些检查
包括函数和操作符的参数的类型检查,保证在运行时捕获算术溢出。一旦通 过排除类型不
匹配的错误,程序编译好之后,检查这些有助于保证程序的正确性,并能在程序运行时提供
对域错误的实时检查。
GDB 可以检查如上述条件的检查。虽然不检查程序里的状态,GDB 可以检查直接传递给
它的表达式,例如 print 命令可以计算表达式的值。如同工作语言一样,GDB 也可以基于程
序的源语言来决定是否自动检查。关于缺省的语言支持设置,参见 12.4 节[语言支持]。
12.3.1 类型检查概述
某些语言,如 Modula-2,是强类型的,也就是说操作符和函数的参数必须是正确的类型,
否则就会发生错误。这个检查防止类型不匹配的错误引起运行时错误。例如,
1 + 2 => 3

[error] 1 + 2.3
第二个例子的错误是因为序数(CARDINAL)1 和实数(REAL)2.3 类型不相容。
对用于 GDB 命令的表达式,用户可以设置让 GDB 类型检查器不检查;将任意不匹配作
为错误并拒绝表达式;或者在遇到类型不匹配时只发出警告,但会计算表达式。如果选择了
最后一项,GDB 会计算如上述第二个例子的表达式,但会发出一个警告。
即使关闭了类型检查,也还有其他和类型有关的理由来防止 GDB 去对表达式进行计算。
例如,GDB 不知道如何将一个 int 型和 struct foo 型相加。这些特殊的类型错误和使用的语
言无关,常常在表达式里引起,例如前面介绍的那个例子,其错误是不知道如何计算。
每种语言都定义了其对类型检查的严格程度。例如,Modula-2 和 C 要求算数操作符的参
数都是数字。在 C 里,枚举类型和指针可以转换为数
值型,所以他们对于算数操作符也是有效的。关于特定语言的更多细节,参见 12.4 节[语言
支持]。
GDB 提供了附加命令来控制类型检查器:
set check type auto
设置自动类型检查,由当前工作语言决定。各种语言的缺省设置,参见 12.4 节[语言支
持]。
set check type on
set check type off
设置类型检查启用或关闭,覆盖掉当前工作语言的缺省设置。如果设置不匹配语言的缺
省值就会发出一个警告。如果启用了类型检
查,在计算表达式的时候发现类型不匹配的话,GDB 会打印警告信息并放弃计算表达
式。
set check range warn
在 GDB 域检查器发现域错误的时候输出警告信息,但会尝试计算表达式的值。由于其
他缘故,表达式计算可能会失败,例如访
问不属于进程的内存(许多 Unix 系统的典型例子)。
show range
显示当前域检查器的设置,以及 GDB 是否自动设置了域检查器。
12.4 语言支持
GDB 支持 C,C++,Objective-C,Fortran,Java,Pascal,汇编,Modula-2 和 Ada 语言。
GDB 的某些功能可以用 于和语言无关的表达式:GDB@,::操作符和’{type}addr’指令(参
见 8.1 节[表达式],75 页)可以和任何 GDB 支持的语言的指令一起 用。
接下来的章节详细的介绍了 GDB 对于每种语言的支持程度。这些章节不是语言导论和参
考,而只是 GDB 表达式解析器能接受何种表达式的参考指引,以及对于各种不同语言的输
入输出格式。有许多关于这些语言很好的书;请参考这些书的参考或导论。
12.4.1 C 和 C++
由于 C 和 C++关系很近,GDB 的很多功能都能在这两种语言上使用。在这些情况下,我
们一起讨论这两种语言。
C++调试工具是由 C++编译器和 GDB 共同实现的。因此,要有效调试 C++代码,必须用
支持 C++的编译器上编译 C++程序,例如 GNU g++,或 HP ANSI C++编译器(aCC)。
使用 GNU C++时要得到最佳效果,应使用 DWARF 2 调试格式;如果此格式不能在你的
系统里执行,请尝试用 stabs+调试格式。你可以用 g++ 命令行选项’-gdwarf-2′和’-gstabs+’
选择格式。“Options for Debugging Your Program or GCC” in Using the gnu Compiler
Collection (GCC)。
12.4.1.1 C 和 C++操作符
操作符必须定义于明确类型的值。例如,+定义于数值,但不是结构体。操作符常常定义
与类型组。
下列定义表述了 C 和 C++的目的:
·整型类型包括 int 和其存储类型限定符;char;enum;以及 C++的 bool。
·浮点类型包括 float,double 和 long double(如果目标平台支持的话)。
·指针类型包括所有定义为(type*)的类型。
·标量类型包括所有上述类型。
GDB 支持下列操作符。下面将以递增的次序列举:
, 逗号和顺序操作符。用逗号分隔的表达式列表将从左到右计算,最后计算整个表
达式的结果。
= 赋值。赋值表达式的值是被赋值的值。定义于标量类型。
op= 用于形如 a op=b 的表达式里,并转换到 a = a op b。op=和=具有相同的优先顺序。
op 是下面的任意操作符之一:|, ^, &, <<, >>,
+, -, *, /, %。
?: 三进制操作符。a?b:C 可以如此认为:如果 a 那么 b,否则 c。a 必须是整形类型。
|| 逻辑或。定义于整形类型。
&& 逻辑与。定义于整形类型。
| 位或。定义于整形类型。
^ 位异或。定义于整形类型。
& 位与。整形类型。
==,!= 相等和不相等。表达式的值 0 代表假,非 0 代表真。
<, >, <=, >=
小于,大于,小于等于,大于等于。定义与标量类型。表达式的值 0 代表假,
非 0 代表真。
<<, >> 左移,右移。定义于整形类型。
@ GDB”人工数组”操作符(参见 8.1 节[表达式],75 页)。
+,- 加和减。定义于整形类型,浮点类型和指针类型。
*, /, % 乘,除和求余。乘和除定义于整形和浮点类型。求余定义与整形类型。
++,– 自增和自减。位于变量前面,此操作符会于变量使用前执行;位于变量后面,
操作符会在变量使用后执行。

  • 对指针取值。定义于指针类型。和++的优先级相同。
    & 地址操作符。定义于变量。和++的优先级相同。
    对于调试 C++,除了在 C++语言里的用法,GDB 还实现了’&’的另一种用
    法:可以用’&(&ref)’来查看 C++引用变量(用’&ref’声
    明)的存储地址。
  • 负。定义于整形和浮点类型。和++的优先级相同。
    ! 逻辑非。定义于整形类型。和++的优先级相同。
    ~ 按位求补运算符。定义与整形类型。按位求补运算符。
    .,-> 结构体成员,结构体指针成员。为方便起见,GDB 视这两种操作符为等同,基
    于存储类型信息来选择是否对一个指针取值。定
    义于结构体和联合数据。
    .,-> 对指针指向的成员取值。
    [] 数组索引。a[i]定义如*(a+i)。和->的优先级相同。
    () 函数参数列表。和->的优先级相同。
    :: C++范围解析操作符。定义于结构体,联合和类。
    :: 双冒号也表示 GDB 范围解析操作符(参见 8.1 节[表达式],75 页)。和::的优
    先级相同,如上。
    如果在用户代码里重新定义了操作符,通常 GDB 会尝试用重新定义的版本来执行,而不
    用预定义的方式。
    12.4.1.2 C 和 C++常量
    GDB 允许用户用下列方式表示 C 和 C++的常量:
    ·整形常量是数字系列。8 进制常量的由’0′(即 0)开头,16 进制常量由’0x’或’0X’
    开头。常量也可以用字符’l’结尾,指明次常量应该视为
    long(长整型)类型。
    · 浮点指正常量是数字系列,接着一个十进制小数点,再接着数字系列,还可以接着一个
    指数。指数形如’e[[+]|-]nnn’,这里 nnn 是另一个
    数字系列。如是正的指数,’+'可选用。浮点常量也可以用字符’f’或者’F’结尾,指明此
    常量应视为 float(而不是默认的 double)类型;
    或者用字符’l’或者’L’来结尾,指明是 long double 常量。
    ·枚举常量有枚举变量,或者是枚举变量对应的整数组成。
    · 字符常量是由单引号(‘)括起来的单个字符,或者一个数字–字符对应的原始数值(通常
    是其 ASCII 值)组成。在引号里,单字符可以用一 个词或者转义序列表示,形如’\nnn’,
    这里 nnn 是字符序数的八进制表现形式;或者形如’\x’,这里’ x’是预定义的特殊字符—例
    如,’\n’
    表示新行(newline)。
    ·字符串常量由双引号(“)括起来的字符常量系列组成。任何有效的字符常量(如上所述)
    都可以。字符串里的双引号必须跟在反斜杠后面,例如’”a\”b’c”‘是 5 字符的字符串。
    ·指针常量是整形值。也可以用 C 操作符’&’将指针写道常量里。
    · 数组常量用花括号’{‘和’}'括起来,用逗号分隔;例如,’{1,2,3}’是 3 元素的整形
    数组,‘{{1,2}, {3,4}, {5,6}}’是 3 行 2 列的数组, 而‘{&”hi”, &”there”, &”fred”}’ 是 3 元素的指针数组。
    12.4.1.3 C++表达式
    GDB 可以处理大多数 C++表达式。
    注意:只有在使用正确的编译器和调试格式的情况下,GDB 才能调试 C++代码。目前,
    在用 GCC 2.95.3 或者 GCC 3.1 或者更新的版本上,使用选项‘-gdwarf-2’或者‘-gstabs+’ 的 C++代码上,GDB 工作的最好。DWARF 2 优于 stabs+。大多数 GCC 的配置选用 DWARF
    2 或者 stabs+作为其默认调试格式,所以通常不需要手动指定调试格式。用其它编译器编译
    的和/或者调试格式的代码,GDB 可能工作的不太好,也可能根 本就不起效。
    1.允许调用成员函数;调用表达式如下:
    count = aml->GetOriginal(x, y)
    2.当成员函数可用时(在选定的堆栈帧上),表达式和成语函数具有相同的可用的名字空间;
    那就是说,用 C++相同的规则,GDB 允许
    隐式引用类实例指针 this。
    3.可以调用重载函数;GDB 会在某些限制下解决函数调用中函数的正确的定义。涉及到用
    户定义类型的转换,构造函数的调用,不在程
    序里的模板实例,GDB 都不会去进行重载解析。GDB 也不能处理省略参数列表或者默认
    参数的情况。
    GDB 可以执行整形转换和提升(例如在函数参数 char 提升到 int),浮点提升,算术转换,
    指针转换,类对象转换到基类,以及标准转
    换如函数和数组到指针;GDB 需要精确匹配函数参数的数量。
    总是执行重载解析,除非指定了 set overload-resolution off。参见节 12.4.1.7[GDB 的 C++
    功能],128 页。
    要使用明确的函数签名来调用一个重载函数,必须指定 set overload-resolution off,如
    p ’foo(char,int)’(’x’, 13)
    GDB 命令补全功能可以简化这些;参见节 3.2[命令补全],19 页。
    4.GDB 能理解定义为 C++引用的变量;可以在表达式里使用这些变量,如同在 C++源代码
    里一样–它们是自动取值的。
    在 GDB 显示堆栈帧的时候,引用变量的值不睡显示在参数列表里(和其它类型的变量不
    同);这样能避免聚集,因为引用变量常常用
    于大的结构体。总是显示引用变量的地址,除非指定了’set print address off’。
    5.GDB 支持 C++名字解析操作符::–表达式可以使用此操作符就如同在程序里那样。由于一
    个范围可以定义于另外一个,如果需要可以
    重复使用::,例如表达式如’scope1::scope2::name’。
    在 C 和 C++调试里,GDB 也可以参考源文件来解决名字空间(参见 8.2 节[程序变量],76
    页)。
    另外,要是用 HP 的 C++编译器,GDB 支持虚函数调用,打印对象的虚基类,调用基类
    子对象的函数,对象转换,以及调用用户定义的
    操作符。
    12.4.1.4 C 和 C++缺省值
    如果允许 GDB 自动设置类型和域检查,工作语言转换到 C 或者 C++时,这两个设置的缺
    省设置都是 off。不论用户或 GDB 是否选择工
    作语言,GDB 都是这样处理的。
    如果允许 GDB 自动设置语言,GDB 会识别文件名以’.c’,’.C’或者’.cc’等等结尾的
    文件,在 GDB 进入以这些文件编译的代码时,GDB 会将工作语言设置为 C 或者 C++。更
    多细节,参加节 12.1.3[设置 GDB 推断源文件语言],120 页。
    12.4.1.5 C 和 C++类型和域检查
    在 GDB 分析 C 或者 C++表达式,缺省的,不使用类型检查。不过,如果你将类型检查打
    开,GDB 认为这两种变量类型相等,如果:
    ·这两种变量是结构化的且具有相同的结构,联合,或者枚举标签。
    ·这两种变量具有相同的类型名,或者用是用 typedef 声明的相等的类型。
    域检查,如果打开的话,用于数学操作。由于下标常常用于索引一个指针,而指针本身并
    不是数组,数组下标不检查。
    12.4.1.6 GDB 和 C
    set print union 和 show print union 命令应用于 union 类型。要是设置为’on’,任 何 struct
    或者 class 里的 union 都会打印。否则,会用
    ‘{…}’替代。
    @操作符有助于调试用指针和内存分配函数分配的动态数组。参见节 8.1[表达式],75 页。
    12.4.1.7 GDB 的 C++功能
    GDB 某些命令对于 C++特别有用,而有些是特殊设计用于 C++。下面是这些命令的概述:
    breakpoint menus
    要想在一个重载函数里设置断点,GDB 断点菜单可以助你指定哪个函数定义上设置。
    参见节 5.1.8[断点菜单],52 页。
    rbreak regex
    用正则表达式设置断点,正则表达式断点对于在不是任何特殊类的成员重载函数上设置
    断点,很有帮助。参见节 5.1.1[设置断点]
    ,40 页。
    catch throw
    catch catch
    使用这两个命令,调试 C++异常处理。参见节 5.1.3[设置捕获点],47 页。
    ptype typename
    打印继承关系和其它有关类型 typename 的信息。参见 13 章[检验符号表],143 页。
    set print demangle
    show print demangle
    set print asm-demangle
    show print asm-demangle
    控制是否以源代码形式显示 C++符号,在现实 C++源代码和汇编的时候。参见节 8.7[打
    印设置],82 页。
    set print object
    show print object
    选择是否打印派生(实际的)或者声明的对象类型。参见节 8.7[打印设置],82 页。
    set print vtbl
    show print vtbl
    控制打印虚函数表的格式。参见节 8.7[打印设置],82 页。(vtbl 命令在用 HP ANSI C++
    编译器编译(aCC)的程序上不起效。)
    set overload-resolution on
    激活 C++表达式计算的重载解析。缺省打开。对于重载函数,GDB 使用 C++转换规则
    计算参数并搜索签名匹配参数类型的函数(更多
    细节,参见节 12.4.1.3[C++表达式])。如果不能找到匹配项,将打印一个消息。
    set overload-resolution off
    关闭 C++表达式计算的重载解析。对于不是类成员函数的重载函数,GDB 选择在符号
    表里找到的此名字的第一个函数,不论其参数
    是否是正确的类型。对于是类成员函数的重载函数,GDB 搜索签名精确匹配参数类型
    的函数。
    show overload-resolution
    显示当前重载解析的设置。
    Overloaded symbol names
    使用相同的用于声明 C++符号的标识,可知指定重载符号的特殊定义:type symbol(types)
    而不仅仅是 symbol。也可以用 GDB 命令
    行字补全功能来列出可用的选择,或者来补全类型列表。参见节 3.2[命令补全],关于
    如何完成的更多细节。
    12.4.1.8 十进制浮点格式
    GDB可以十进制浮点格式来检验,设置和执行数字计算,在C语言里相应的用_Decimal32,
    _Decimal64 和 _Decimal128 类型的扩展支持十进制浮点算术。
    在实际应用中有两种编码方式,依赖于系统架构:x86 和 x86-64 平台下是 BID(Binary
    Integer Decimal),PowerPC 是 DPD(Densely Packed
    Decimal)。GDB 为已配置的系统使用恰当的编码格式。
    由于’libdecnumber’的限制,此库用于操作十进制浮点数,不能将宽度超过 32 位的整形
    转换到十进制浮点。
    另外,要模仿 GDB 的二进制浮点计算行为,十进制浮点操作里的错误检查将忽略下溢,
    上溢和 0 除异常。
    PowePC 架构,GDB 提供了伪寄存器集来检查存储于浮点寄存器里的_Decimal128 值。
    更多细节,参见节 18.4.7[PowerPC],207 页。
    12.4.2 Objective-C
    本节提供的命令和命令选项的信息有助于调试 Objective-C 代码。参见 13 章[符号],143
    页,和 13 章[符号],143 页,更多 Objective-C 相
    关的命令。
    12.4.2.1 命令里的方法名
    下面是接受 Objective-C 方法名的扩展命令,
    · clear
    · break
    · info line
    · jump
    · list
    完全合格的的 Objective-C 方法名应声明如下:
    -[Class methodName]
    这里减号用来表示对象实例方法,加号(没显示出来)用来表示一个类方法。类名 Class
    和方法名 methodName 用括号括起来,和 Objective-C 源代码里消息表示方法相似。例如,
    要在调试程序里的类 Fruit 的实例 create 方法上设置一个断点,输入:
    break -[Fruit create]
    要显示类方法 initialize 附近的 10 行程序代码,输入:
    list +[NSText initialize]
    在目前的 GDB 版本上,需要输入加号和减号。以后的 GDB 版本,加号和减号会是可选
    的,但可以用来缩小搜索范围。也可能只需要指定一
    个方法名:
    break create
    必须指定完整的方法名,包括冒号。如果程序源文件包含多个 create 方法的话,将以编号
    方式打印实现了此方法的类列表。用编号指定
    所选择的方法,或者输入’0′来退出,如果不选择的话。
    另一个例子是清除在 NSWindow 类的 makeKeyAndOrderFront:方法上的断点,输入:
    clear -[NSWindow makeKeyAndOrderFront:]
    12.4.2.2 和 Objective-C 协作的 Print 命令
    print 命令也扩展接受方法。例如:
    print -[object hash]
    让 GDB 发送 hash 消息给对象 object 并打印结果。而且,也增加了附加命令,print-object 或
    缩写为 po,此命令打印对象的描述。不过,此命
    令依赖于 Objective-C 库实现了特殊的钩子函数,_NSPrintForDebugger。
    12.4.3 Fortran
    GDB 可以调试用 Fortran 写的程序,但目前只支持 Fortran 77 语言的特征。
    某些 Fortran 编译器(GNU Fortran 77 和 Fortran 95 编译器)在变量和函数上添加一个下划
    线。在调试用这些编译器的编译的程序时,引
    用变量和函数时需要加上下划线。
    12.4.3.1 Fortran 操作符和表达式
    操作符必须定义与特定类型上。例如,+定义于数字,但不能定义于字符或者其它非算术
    类型。操作符通常定义于类型组。
    ** 求幂操作符。将第一个操作数作为第二个操作数的乘方。
    : 域操作符。常用于数组形式,表示数组的一部分。
    12.4.3.2 Fortran 的缺省值
    Fortran 符号通常是不区分大小写的,所以 GDB 缺省使用大小写不区分地匹配 Fortran 符
    号。用’set case-insensitive’命令改变此设置,
    更多细节,参见 13 章[符号],143 页。
    12.4.3.3 Fortran 的特殊命令
    GDB 实现了一些支持 Fortran 相关特征的命令,例如显示公共块。
    info common [common-name]
    此命令打印名为 common-name 的公共块里的数据值。不带参数的话,打印当前程序所
    有可访问的共块的名字。
    12.4.4 Pascal
    目前不能调试使用集合,子域,文件变量和嵌套函数的 Pascal 程序。GDB 不支持进入表
    达式,打印数值,或者相似的使用 Pascal 句法。
    Pascal 特定的命令 set print pascal_static-members 控制是否显示静态 Pascal 对象。参见 8.7
    节[打印设置],82 页。
    12.4.5 Modula-2
    GDB 对 Modula-2 的扩展支持智能支持由 GNU Modula-2 编译器(目前尚处于开发中)编
    译的程序。目前尚未支持其他 Modula-2 编译器,试图调试此类编译器产生的可执行程序的
    话,GDB 会在读入可执行程序的符号表时打印一个出错信息。
    12.4.5.1 操作符
    操作符必须定义域特定类型的数据上。例如,+定义于数字,但不能在结构体上。操作符
    通常定义于类型组。就 Modula-2 而言,有下列定义:
    ·整形类型由 INTEGER,CARDINAL,以及它们的子域组成。
    ·字符类型由 CHAR 和其子域组成。
    ·浮点类型由 REAL 组成。
    ·指针类型由任意声明为 POINTER TO 类型组成。
    ·向量类型由上述所有类型组成。
    ·集合类型由 SET 和 BITSET 类型组成。
    ·布尔类型由 BOOLEAN 组成。
    下面的介绍支持的操作符,以增序排列:
    , 函数参数或数组索引分割符。
    := 赋值。var := value 的值是 value。
    <, > 小于,大于,定义于整型,浮点型和枚举型。
    <=, >= 小于等于,大于等于,定义于整型,浮点型和枚举型,集包含和集合类型。和<
    优先顺序相同。
    =, <>, #
    相等和两种不相等的表示,向量类型也起效。和<优先顺序相同。在 GDB
    脚本里,只有<>可以表示不相等,因为#和脚本注释
    符冲突。
    IN 集合成员。定义与集合类型和其成员的类型。和<优先顺序相同。
    OR 布尔析取。定义于布尔型。
    AND,& 布尔合取。定义于布尔型。
    @ GDB“伪数组”操作符(参见 8.1 节[表达式],75 页)。
    +, - 加和减,定义于整型和浮点型,联合和集合差类型。
  • 乘,定义于整型和浮点型,或者集合交集。
    / 除,定义于浮点类型,或者集合类型的对称集合差。和优先顺序相同。
    DIV, MOD
    整型出和求余。定义于整型。和
    优先顺序相同。
  • 负。定义于 INTEGER 和 REAL 数据。
    ^ 对指针取值。定义于指针类型。
    NOT 布尔非。定义于布尔型。和^有限顺序相同。
    . RECORD(记录)域选择符。定义于 RECORD 数据。和^优先顺序相同。
    [] 数组索引。定义于 ARRAY 数据。和^优先顺序相同。
    () 过程参数列表。定义于 PROCEDURE 对象。和^优先顺序相同。
    ::, . GDB 和 Modula-2 域操作符。
    警告:设置表达式和它们的操作还不支持,GDB 在集合上使用操作符 IN,+, -, , /, =, ,, <>, #,<=,>=会引起错误。
    12.4.5.2 内建函数和过程
    Modula-2 也提供了一些内建的过程和函数。在介绍之前,先介绍下面用到的元变量:
    a 表示一个数组变量。
    c 表示一个 CHAR 常量或变量。
    i 表示一个变量或者整型常量。
    m 表示属于一个集合的标识符。通常和原变量 s 一起用于同一个函数。s 的类型英格是
    SET OF mtype(这里 mtype 是 m 的类型)。
    n 表示一个整型或浮点类型的变量或者常量
    r 表示一个浮点类型的变量或常量。
    t 表示一个类型。
    v 表示一个变量。
    x 表示一个变量或常量,其类型是多种类型中的其一。详细参见函数的解释。
    所有 Modula-2 内建过程都返回一个结果,见下。
    ABS(n) 返回 n 的绝对值。
    CAP© 如果 c 是小写字符,返回它对应的大写字符,否则返回 c。
    CHR(i) 返回值是 i 的字符。
    DEC(v) 将变量 v 的值减 1.返回新的值。
    DEC(v,i)
    将变量 v 的值减 i。返回新的值。
    EXCL(m,s)
    将元素 m 从集合 s 里删除。返回新的集合。
    FLOAT(i)
    返回整形变量 i 对应的浮点数。
    HIGH(a) 返回数组 a 的最后一个元素的索引。
    INC(v) 将变量 v 增加 1。返回新的值。
    INC(v,i)
    将变量 v 的值增加 i。返回新的值。
    INCL(m,s)
    如果 m 不在集合 s 的话,将元素 m 加入集合 s。返回新的集合。
    MAX(t) 返回类型 t 的最大值。
    MIN(t) 返回类型 t 的最小值。
    ODD(i) 如果 i 是一个奇数,返回 TRUE。
    ORD(x) 返回参数的原始值。例如,一个字符的原始值是其 ASCII 值(机器支持的 ASCII
    字符集)。x 必须是一个序数类型,
    包括整形,字符和枚举类型。
    SIZE(x) 返回参数的大小。x 可以是变量或类型。
    TRUNC®
    返回 r 的实数部分。
    TSIZE(x)
    返回参数的大小。x 可以是变量或类型。
    VAL(t,i)
    返回类型 t 的成员,其值是 i。
    警告:集合和它们的操作还未支持,所以使用过程 INCL 和 EXCL 的话,GDB 会视为错误。
    12.4.5.3 常量
    GDB 允许用下列方式表示 Modula-2 的常量:
    ·整型常量,此类型常量是数据序列。在用于表达式中时,常量会转换为和表达式其它部分
    相容的类型。16 进制整数应该后接’H’,二进
    制整数后接’B’。
    ·浮点常量也是数据序列,后接一个小数点和另外一个数据序列。可以指定一个可选的指数,
    其形式是’E[+|-]nnn’,这里’[+|-]nnn’表
    示指数。浮点常量的所有数据都必须是有效的 10 进制数据。
    ·字符常量由用一对单引号或双引号括起来的单个字符组成。用 C 风格的转义序列也是可
    以的。转义序列的简明介绍,参见节 12.4.1.2[C
    和 C++常量],125 页。
    ·字符串常量由一对单引号或双引号括起来的字符序列组成。用 C 风格的转义序列也是可
    以的。转义序列的简明介绍,参见节 12.4.1.2[C
    和 C++常量],125 页。
    ·枚举常量由由枚举标志符组成。
    ·布尔常量由标志符 TRUE 和 FALSE 表示。
    ·指针常量由整数值组成。
    ·集合常量目前尚未支持。
    12.4.5.4 Modula-2 类型
    目前,GDB 可以在 Modula-2 上下文中打印下列数据类型:数组类型,记录类型,集合类
    型,指针类型,过程类型,枚举类型,子域类型和基类。还可以打印用这些类型定义的变量
    内容。本节所示的 GDB 调试例子,包括几个源代码的样例。
    第一个例子包含下列的代码段:
    VAR
    s: SET OF CHAR ;
    r: [20…40] ;
    可以用 GDB 查询 r 和 s 的类型和值。
    (gdb) print s
    {’A’…’C’, ’Z’}
    (gdb) ptype s
    SET OF CHAR
    (gdb) print r
    21
    (gdb) ptype r
    [20…40]
    类似的,如果源代码声明 s 为:
    VAR
    s: SET [’A’…’Z’] ;
    那么可以查询 s 的类型:
    (gdb) ptype s
    type = SET [’A’…’Z’]
    注意,目前还不能用调试器交互的操作集合表达式。
    下面的例子演示在 Modula-2 里如何生命数组和怎样用 GDB 来打印其类型和内容:
    VAR
    s: ARRAY [-10…10] OF CHAR ;
    (gdb) ptype s
    ARRAY [-10…10] OF CHAR
    注意,数组处理尚未完全实现,虽然类型可以正确打印,表达式处理仍然会假设所有的数
    组的下标是 0,本例中将不会认为是-10。
    下面还有一些 Modula-2 类型相关的例子:
    TYPE
    colour = (blue, red, yellow, green) ;
    t = [blue…yellow] ;
    VAR
    s: t ;
    BEGIN
    s := blue ;
    GDB 交互显示如何查询数据类型和变量的值。
    (gdb) print s
    $1 = blue
    (gdb) ptype t
    type = [blue…yellow]
    在这个例子中声明了一个 Modula-2 的数组,打印其内容。观察到数组的内容以如同 C 对
    应的数据那样的相同方式写入。
    VAR
    s: ARRAY [1…5] OF CARDINAL ;
    BEGIN
    s[1] := 1 ;
    (gdb) print s
    KaTeX parse error: Expected 'EOF', got '#' at position 2122: …Modula-2 不相等操作符#̲会转译为注释的开始。用<>替代…‘开头的名字是惯用变量(参见节 8.9[惯用变量],89 页)或者是其寄存器(参见节
    8.10[寄存器],90 页)。
    另外,GDB 提供了一些和 Ada 相关的其他的快捷方式和直接的附加方式:
    ·赋值语句可以是一个表达式,返回其右边的操作数作为其值。因此,可以输入
    set x := y + 3
    print A(tmp := y + 1)
    ·可以使用分号作为操作符,返回右边操作数的值。例如,可以允许如下的复杂条件断点:
    break f
    condition 1 (report(i); k += 1; A(k) > 100)
    ·与其使用字符串连接和符号字符名称来将某些特殊的字符引入字符串,用户可以用特殊括
    号标注来替代,这个方法
    也用于打印字符串。假设有一些字符序列在字符串里或字符里,其形如”XX”,那么代表
    这个字符是以 XX 为编码的十
    六进制数值。在字符串中,字符序列’[“”]‘也代表单个引号。例如,
    “One line.[“0a”]Next line.[“0a”]“
    ·由于属性’Pos,’Min,’Max 的子类型是可选的(且在任何情况下都将被忽略)。例如,
    可以输入如下形式
    print ’max(x, y)
    ·打印数组时,如果数组的下界是 1,GDB 使用位置数值记数法, 否则使用修改过的技术
    命名。例如,一个具有 3 个整
    数的一维数组的下界是 3 的话,将打印如下
    (3 => 10, 17, 1)
    也就是说,和标准的 Ada 不一样,只有第一个元素具有=>前缀。
    ·可以用属性的独特的表达方式或者是其名字的子序列的多个字符(精确的匹配优先)来缩
    写属性。例如,可以用 a’
    len,a’gth,或者 a’lh 来替代 a’length。
    ·由于 Ada 是不区分大小写的,通常调试器将用户输入的标志符映射为小写。GNAT 编译
    器为其内部标志符使用大写字
    符,通常这根用户无关。对于少数用户需要检查的,用半角括号围起来避免小写映射。例
    如,
    gdb print <JMPBUF_SAVE>[0]
    ·打印一个全类范围类型的对象时和对一个访问到全类范围的变量取值时,会打印此对象相
    关类型的所有组件(如其
    运行时标签所示)。类似的,在此对象上的组件选择需要操作其的相关类型。
    12.4.6.4 在开头处停止
    某些时候有必要在阐述代码期和在主过程前调试代码。Ada 参考手册里定义次阐述代码由
    名为 adainit 的过程调用。
    要在阐述代码前中断执行,只需要简单的使用两个命令:tbreak adainit 和 run。
    12.4.6.5
    除了前述的遗漏外(参见节 12.4.6.2[Ada 的遗漏],138 页),在 GDB Ada 模式中有几个缺
    陷和限制,其中的一些会
    在今后计划发行的调试器发行版本和 GNU Ada 编译器里修复。
    ·目前,调试器不能提供足够的信息来判断某些指针是指向对象的指针还是指针就是对象本
    身。因此,要打印一个表
    达式,用户需要在此表达式后边附加额外的.all。
    ·编译器选择不实例化为对象的静态常量对于调试器是不可见的。
    ·函数参数列表里的命名参数联合将被忽略(参数列表是位置相关的)。
    ·目前很多有用的库文件包对于调试器不可见。
    ·定点算术运算,转换,输入和输出使用浮点算术运算,并将得到和主机相近的计算结果。
    ·属性’Address 可能不是 System.Address。 ·GNAT 编译器从不为 Ada 语言定义的标准符号产生标准前缀。GDB 明白:它会在你使用
    的时候将此前缀从名字里删除,
    且不会在本地符号里查找其名字,也不会再其他包和子程序里去匹配符号的。如果在程序
    的任意地方已定义了非参
    数和本地变量的符号,这些符号的名字匹配在“标准(Standard)”里的话,GNAT 缺少鉴
    定的条件而可能导致混淆
    。如果混淆发生了,可以将用 Standard 包里的名字来明确地鉴定这些有问题的名字。
    12.5 未支持的语言
    除了完全支持的编程语言,GDB 还支持称为 minimal 的伪语言。minimal 不是一种真正的
    编程语言,提供接近 C 和汇编
    语言所能提供的功能子集。在调试 GDB 尚未支持的语言编写的程序时,应该允许执行大多
    数简单的操作。
    如果将语言设置为 auto,当前架构要是未支持的语言,GDB 会自动选择语言
    第十三章 查看符号表
    本章介绍的命令可以查询在程序里定义的符号(变量名,函数名,类型名)。这些信息位
    于程序文本段,不会在
    程序执行时改变的。GDB 在程序符号表里查找这些符号,或者启动 GDB 时指示的文件(参
    见节 2.1.1[选择文件],12 页
    ),或者由文件管理命令指定的文件里查找(参见节 15.1[指定文件的命令],155 页)。
    有时可能需要引用含有奇怪字符的符号,通常 GDB 将这些符号视为字定义符。最常见
    的例子是引用到其他源文件里定
    义的静态变量(参见节 8.2[程序变量],76 页)。对象文件会记录文件名作为调试符号,不过
    GDB 可能解析文件名为 3 个
    部分,例如’foo.c’,会分解为’foo’,’.’,’c’三个字。要让 GDB 将’foo.c’视为单个符号,
    只要将其用单引号括起来;例如:
    p ’foo.c’::x
    在文件’foo.c’里查找 x 的值。
    set case-sensitive on
    set case-sensitive off
    set case-sensitive auto
    通常,GDB 在查找符号时,会用当前源代码语言决定的大小写相关与否来匹配它们的
    名字。偶尔用户也可能希望能自由选择。命令
    set case-sensitive 用 on 参数指定大小写相关匹配,off 参数指定大小写无关匹配。如果
    指定了 auto,大小写相关将由此源代码语
    言缺省的设置决定。除了 Fortran,对于大部分语言缺省的都是大小写相关的,Fortran
    是大小写无关的匹配。
    show case-sensitive
    此命令显示当前符号查找的大小写相关性的设置。
    info address symbol
    显示符号 symbol 数据在何处存储。对于寄存器变量,是说此变量在哪个寄存器里存储。
    对于非寄存器的本地变量,此命令打印此变
    量对于堆栈帧的偏移。
    注意’print &symbol’,不是在所有寄存器变量上都有效,且对于栈本地变量而言,会
    打印此变量的当前实例的实际地址。
    info symbol addr
    打印存储于地址 addr 上的符号名。如果在此地址上没有存储符号,GDB 打印最近的符
    号和偏移量:
    (gdb) info symbol 0×54320
    _initialize_vx + 396 in section .text
    这是 info address 命令的输出。可以用它来查找变量名或者用地址来查找函数名。
    whatis [arg]
    打印 arg 的数据类型,arg 可以是表达式或数据类型。如果不带参数,打印 的数据类型,值历史里最近的值。如果 a r g 是表达式,不会真的计算,且此表达式里的任何边际效应(例如赋值或函数调用)操作都不会执行。如果 a r g 是类型名,可能是一个类型的名字或者 t y p e d e f ,或者对于 C 代码而言,可以有’ c l a s s c l a s s − n a m e ’,’ s t r u c t s t r u c t − t a g ’,’ u n i o n u n i o n − t a g ’或者’ e n u m e n u m − t a g ’形式 . 参见节 8.1 [ 表达式 ] , 81 页。例如,对于下面变量声明: s t r u c t c o m p l e x d o u b l e r e a l ; d o u b l e i m a g ; v ; 这两个命令会输出如下: ( g d b ) w h a t i s v t y p e = s t r u c t c o m p l e x ( g d b ) p t y p e v t y p e = s t r u c t c o m p l e x d o u b l e r e a l ; d o u b l e i m a g ; 如同 w h a t i s 一样,使用 p t y p e 但不带参数的话会打印 的数据类型, 值历史里最近的值。如果 arg 是表达式,不 会真的计算,且此表达式里的任何边际效应(例如赋值或函数调用)操作都不会执行。 如果 arg 是类型名,可能是一个类型的名字或 者 typedef,或者对于 C 代码而言,可以有’class class-name’,’struct struct-tag’,’union union-tag’或者’enum enum-tag’形 式.参见节 8.1[表达式],81 页。 例如,对于下面变量声明: struct complex {double real; double imag;} v; 这两个命令会输出如下: (gdb) whatis v type = struct complex (gdb) ptype v type = struct complex { double real; double imag; } 如同 whatis 一样,使用 ptype 但不带参数的话会打印 的数据类型,值历史里最近的值。如果arg是表达式,不会真的计算,且此表达式里的任何边际效应(例如赋值或函数调用)操作都不会执行。如果arg是类型名,可能是一个类型的名字或者typedef,或者对于C代码而言,可以有classclassnamestructstructtagunionuniontag或者enumenumtag形式.参见节8.1[表达式]81页。例如,对于下面变量声明:structcomplexdoublereal;doubleimag;v;这两个命令会输出如下:(gdb)whatisvtype=structcomplex(gdb)ptypevtype=structcomplexdoublereal;doubleimag;如同whatis一样,使用ptype但不带参数的话会打印的类型,值历史里最近的值。
    有时候,程序使用模糊的数据类型或者不完整声明的复杂数据类型。如果程序里调试信
    息不足于让 GDB 显示数据类型的完整声明,GDB 会输
    出’’。例如,假设下面声明:
    struct foo;
    struct foo fooptr;
    但没有 struct foo 自身的定义,GDB 会输出:
    (gdb) ptype foo
    1 = < i n c o m p l e t e t y p e > “ I n c o m p l e t e t y p e ”是 C 描述没有完全声明的数据类型的术语。 i n f o t y p e s r e g e x p i n f o t y p e s 打印所有名字匹配正则表达式 r e g e x p (如果没有参数的话,打印程序里所有的类型)的的类型的简短介绍。会匹配每个完整类型名,如同这个命令是一个个单独完整的命令行;因此,’ i t y p e v a l u e ’显示程序里所有名字中包含字符串 v a l u e 的类型的信息,但’ i t y p e v a l u e 1 = <incomplete type> “Incomplete type”是 C 描述没有完全声明的数据类型的术语。 info types regexp info types 打印所有名字匹配正则表达式 regexp(如果没有参数的话,打印程序里所有的类型)的 的类型的简短介绍。会匹配每个完整类型 名,如同这个命令是一个个单独完整的命令行;因此,’i type value’显示程序里所有 名字中包含字符串 value 的类型的信息,但’i type ^value 1=<incompletetype>IncompletetypeC描述没有完全声明的数据类型的术语。infotypesregexpinfotypes打印所有名字匹配正则表达式regexp(如果没有参数的话,打印程序里所有的类型)的的类型的简短介绍。会匹配每个完整类型名,如同这个命令是一个个单独完整的命令行;因此,itypevalue显示程序里所有名字中包含字符串value的类型的信息,但itypevalue’显示那些完整名字是 value 的类型的信息。
    本命令有两个方面和 ptype 不同:首先,和 whatis 类似,它不会打印详细描述;第二,
    它列出所有定义了此类型的源代码文件。
    info scope location
    列出在指定范围内的所有变量。此命令那个接受位置参数-函数名,源代码行,前面一
    个’
    '的地址,且打印所有此范围内的变
    量。(更多关于支持位置形式的细节,参见节 7.2[指定位置],74 页。)例如:
    (gdb) info scope command line handler
    Scope for command_line_handler:
    Symbol rl is an argument at stack/frame offset 8, length 4.
    Symbol linebuffer is in static storage at address 0×150a18, length 4.
    Symbol linelength is in static storage at address 0×150a1c, length 4.
    Symbol p is a local variable in register $esi, length 4.
    Symbol p1 is a local variable in register $ebx, length 4.
    Symbol nline is a local variable in register $edx, length 4.
    Symbol repeat is a local variable at frame offset -8, length 4.
    此命令在跟踪实践中判断要收集何种数据的时候特别有用,参见节 10.1.4[跟踪点操作],
    115 页。
    info source
    显示当前源文件的信息–就是说,包含当前函数执行点的源文件:
    ·源文件名,包含此文件的目录,
    ·编译此文件的目录,
    ·文件长度,以行为计量单位,
    ·何种编程语言编写的,
    ·可执行程序中是否包含有此文件的调试信息,如果有的话,此信息是何种格式(例如,
    STABS,Dwarf 2),

    ·调试信息中是否包含预处理宏。
    info sources
    打印程序中所有有调试信息的源文件名,用两个列表打印:一个是其符号已经读过的,
    一个是在需要是将被度
    的。
    info functions
    打印所有已定义函数的名字和数据类型。
    info functions regexp
    打印所有名字包含匹配正则表达式 regexp 的函数的名字和数据类型。因此,’info fun
    step’查找所有名字中
    包含 step 的函数;’info fun ^step’查找名字以 step 开头的函数。如果函数名包含的字
    符和正则表达式语言冲
    突的话(例如’operator
    ()’),可以用反斜线符号括起来解决此问题。
    info variables
    打印所有声明于函数外的变量的名字和数据类型(例如,除了本地变量)。
    info variables regexp
    打印所有名字包含匹配正则表达式 regexp 的变量的名字和数据类型(除了本地变量)。
    info classes
    info classes regexp
    打印程序里所有 Objective-C 的类,或者(带参数 regexp)所有匹配正则表达式 regexp
    的类。
    info selectors
    info selectors regexp
    显示程序里所有 Objective-C 选择器,或者(有正则表达式参数的话)所有匹配某个正
    则表达式的选择器。
    有些系统允许替换程序里单独的目标文件,而不需要中断和重启程序。例如,在 VxWorks
    里,可以简单地重新编译
    一个有缺陷的目标文件,且同时保持程序运行。如果运行于这样的系统的话,可以让
    GDB 重新加载这些自动再链接
    的模块的符号:
    set symbol-reloading on
    在重新检视某个目标文件里的符号时,将此文件里符号定义用此替换。
    set symbol-reloading off
    再次检视目标文件里的相同名称的符号时,不替换其定义。缺省状态时不替换;如
    果在不允许自动模块
    链接的系统里运行的话,应该让 symbol-reloading 保持 off 状态,否则 DB 可能在
    链接大程序的时候丢弃符
    号;大程序可能包含几个具有相同符号的模块(在不同的目录或库里)。
    show symbol-reloading
    显示当前 on 或者 off 设置状态。
    set opaque-type-resolution on
    设置让 GDB 解决模糊类型问题。模糊类型是指声明为一个指向 struct,class,或者 union
    的指针类型—例如,
    struct MyType*–用于一个源文件,而 struct MyType 的完全声明在另一个文件里。缺省
    是 on。
    此子命令的设置改变会在下一次符号文件加载时生效。
    set opaque-type-resolution off
    设置让 GDB 不解决模糊类型问题。在这种情况下,打印这种数据类型如下:
    {}
    show opaque-type-resolution
    显示是否解决模糊类型。
    maint print symbols filename
    maint print psymbols filename
    maint print msymbols filename
    将调试符号数据转储到文件 filename。这几个命令能够用于调试 GDB 读符号的代码。
    只包括调试数据的符号。如
    果使用’maint print symbols’,GDB 会包含那些它已经得到完整信息的符号:也就是说,
    filename 反应的是那些
    GDB 已经读入符号的文件。 可以用命令 Info sources 查找文件在哪里。如果用’
    maint print psymbols’的话,转储的文件显示
    GDB 只部分了解的符号的信息。–也就是,符号定义于 GDB 略过的文件中,GDB 还
    没有完全读入。最后,对于目标文件需求的符号信
    息,’maint print msymbols’只从 GDB 已经读入的文件中转储最少的符号信息。关于
    GDB 如何读取符号的讨论在(在 symbolfile 介绍
    中),参见节 15.1[指定文件的命令],163 页。
    maint info symtabs [ regexp ]
    maint info psymtabs [ regexp ]
    列出名字匹配 regexp 的 struct symtab 和 struct partial_symtab 的结构体。如果不带 regexp
    的话,列出所有的结构体。输出包括可
    以拷贝进 GDB 里的表达式,调试这些表达式可以更详细的查看特定结构体。例如:
    (gdb) maint info psymtabs dwarf2read
    { objfile /home/gnu/build/gdb/gdb
    ((struct objfile *) 0×82e69d0)
    { psymtab /home/gnu/src/gdb/dwarf2read.c
    ((struct partial_symtab ) 0×8474b10)
    readin no
    fullname (null)
    text addresses 0×814d3c8 — 0×8158074
    globals (
    (struct partial_symbol *) 0×8507a08 @ 9)
    statics (
    (struct partial_symbol **) 0×40e95b78 @ 2882)
    dependencies (none)
    }
    }
    (gdb) maint info symtabs
    (gdb)
    可以看到,有一个部分符号表,其名称包含字符串’dwarf2read’,此表属于’gdb’可
    执行程序;并且,可以看到 GDB 根本没有读入任何
    符号表。如果在一个函数里设置断点,那么会让 GDB 读入包含此函数的编译单元的符
    号表:
    (gdb) break dwarf2_psymtab_to_symtab
    Breakpoint 1 at 0×814e5da: file /home/gnu/src/gdb/dwarf2read.c,
    line 1574.
    (gdb) maint info symtabs
    { objfile /home/gnu/build/gdb/gdb
    ((struct objfile *) 0×82e69d0)
    { symtab /home/gnu/src/gdb/dwarf2read.c
    ((struct symtab *) 0×86c1f38)
    dirname (null)
    fullname (null)
    blockvector ((struct blockvector *) 0×86c1bd0) (primary)
    linetable ((struct linetable *) 0×8370fa0)
    debugformat DWARF 2
    }
    }
    (gdb)
    第十四章 改变执行
    如果认为在程序中发现了错误,也许你想知道,纠正当前的错误是否能让剩余的运算得到
    正确的结果。 使用 GDB
    的执行中改变的功能,通过实践就可以找到答案。
    例如,将新值存入变量或内存地址,给程序一个信号,在不同的地址上重新执行程序,甚
    至从函数提前返回。
    14.1 给变量赋值
    通过赋值表达式可以改变变量的值。参见节 8.1[表达式],75 页。例如:
    print x=4
    将 4 赋值给变量 x,接着打印这个赋值表达式的值(此值是 4)。更多关于已支持语言的操作
    符信息,参见第 12 章 [用 GDB 调试不同语言编写的程序]。
    如果对赋值操作的结果不感兴趣的话,使用 set 命令替代 print 命令就可以了。set 和 print
    一样赋值,只是不打印赋
    值的结果,不将此值加入到值历史里(参见节 8.8[值历史],88 页)。此表达式只用于其作用
    效果中。
    如果 set 命令的参数字符串的开头和 set 子命令一样的话,使用 set variable 命令,而不要
    只用 set 命令。此命令和
    set 命令一样,除了它没有子命令外。例如,如果程序有个变量叫 width,如果试图用’set
    width=13′来设置一个新值
    的的话,就会导致一个错误,因为 GDB 有个命令叫’set width’:
    (gdb) whatis width
    type = double
    (gdb) p width
    $4 = 13
    (gdb) set width=47
    Invalid syntax in expression.
    当然,错误在于表达式’=47′。要想设置程序变量 width,用
    (gdb) set var width=47
    由于 set 命令有许多子命令,很容易就和程序变量名冲突,用 set variable 命令替代 set 命
    令就能很好的解决这个问
    题。例如,如果程序有个变量叫 g,由于 GDB 有个命令叫 set gnutarget,缩写为 set g:
    (gdb) whatis g
    type = double
    (gdb) p g
    $1 = 1
    (gdb) set g=4
    (gdb) p g
    2 = 1 ( g d b ) r T h e p r o g r a m b e i n g d e b u g g e d h a s b e e n s t a r t e d a l r e a d y . S t a r t i t f r o m t h e b e g i n n i n g ? ( y o r n ) y S t a r t i n g p r o g r a m : / h o m e / s m i t h / c c p r o g s / a . o u t “ / h o m e / s m i t h / c c p r o g s / a . o u t ” : c a n ’ t o p e n t o r e a d s y m b o l s : I n v a l i d b f d t a r g e t . ( g d b ) s h o w g T h e c u r r e n t B F D t a r g e t i s “ = 4 ″ . 在赋值时, G D B 允许比 C 更多的隐式转换;你可以方便地将一个整数存进一个指针变量,反过来也是一样地,并且可以具有相同长度的结构体转换到其它结构体,或者将其转换到长度短一些的结构体。要将值存进内存中的绝对地址,使用’ … ’指令在指定的地址上存入指定类型的值(参见节 8.1 [ 表达式 ] , 81 页)。例如, i n t 0 × 83040 把内存地址 0 × 8340 作为一个整型(这就意味着其在内存中有某种固定的大小和表示方式),且 s e t i n t 0 × 83040 = 4 将值 4 存入此内存位置。 14.2 在不同的位置上继续执行通常,继续执行程序时,用 c o n t i n u e 命令中断的地方上继续执行。不过,用下列命令,可以在自己选定的位置上继续执行程序: j u m p l i n e s p e c j u m p l o c a t i o n 在给定的行 l i n e s p e c 或者 l o c a t i o n 上继续执行。如果那个位置上有断点的话,执行会立即中断。关于 l i n e s p e c 和 l o c a t i o n 的不同形式,参见 7.2 节 [ 指定位置 ] , 74 页。 j u m p 命令惯常会和 t b r e a k 命令联合使用。参见 5.1.1 节 [ 设置断点 ] , 43 页。 j u m p 命令除了改变程序计数器之外,不会改变当前堆栈帧,堆栈指针,也不改变任何内存位置和任何寄存器。如果行 l i n e s p e c 是在当前执行的函数之外的函数里,如果这两个函数的参数模式或者本地变量不一样的话,那么结果就可能很诡异。由于这个原因,如果指定的行不在当前执行的函数里的话, j u m p 命令需要用户确认。不过,如果用户很熟悉程序的机器语言的话,这个诡异的结构也是可以预测的。在很多系统里,把一个新的值存入寄存器 2 = 1 (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/smith/cc_progs/a.out “/home/smith/cc_progs/a.out”: can’t open to read symbols: Invalid bfd target. (gdb) show g The current BFD target is “=4″. 在赋值时,GDB 允许比 C 更多的隐式转换;你可以方便地将一个整数存进一个指针变量, 反过来也是一样地,并且可以具有相同长度的结构体转换到其它结构体,或者将其转换到长 度短一些的结构体。 要将值存进内存中的绝对地址,使用’{…}’指令在指定的地址上存入指定类型的值(参 见节 8.1[表达式],81 页)。例如,{int}0×83040 把内存地址 0×8340 作为一个整型(这就 意味着其在内存中有某种固定的大小和表示方式),且 set {int}0×83040 = 4 将值 4 存入此内存位置。 14.2 在不同的位置上继续执行 通常,继续执行程序时,用 continue 命令中断的地方上继续执行。不过,用下列命令,可 以在自己选定的位置上继续执行程序: jump linespec jump location 在给定的行 linespec 或者 location 上继续执行。如果那个位置上有断点的话,执行会立 即中断。关于 linespec 和 location 的不同形 式,参见 7.2 节[指定位置],74 页。jump 命令惯常会和 tbreak 命令联合使用。参见 5.1.1 节[设置断点],43 页。 jump 命令除了改变程序计数器之外,不会改变当前堆栈帧,堆栈指针,也不改变任何 内存位置和任何寄存器。如果行 linespec 是在 当前执行的函数之外的函数里,如果这两个函数的参数模式或者本地变量不一样的话, 那么结果就可能很诡异。由于这个原因,如 果指定的行不在当前执行的函数里的话,jump 命令需要用户确认。不过,如果用户很 熟悉程序的机器语言的话,这个诡异的结构也 是可以预测的。 在很多系统里,把一个新的值存入寄存器 2=1(gdb)rTheprogrambeingdebuggedhasbeenstartedalready.Startitfromthebeginning?(yorn)yStartingprogram:/home/smith/ccprogs/a.out“/home/smith/ccprogs/a.out:cantopentoreadsymbols:Invalidbfdtarget.(gdb)showgThecurrentBFDtargetis=4″.在赋值时,GDB允许比C更多的隐式转换;你可以方便地将一个整数存进一个指针变量,反过来也是一样地,并且可以具有相同长度的结构体转换到其它结构体,或者将其转换到长度短一些的结构体。要将值存进内存中的绝对地址,使用指令在指定的地址上存入指定类型的值(参见节8.1[表达式]81页)。例如,int0×83040把内存地址0×8340作为一个整型(这就意味着其在内存中有某种固定的大小和表示方式),且setint0×83040=4将值4存入此内存位置。14.2在不同的位置上继续执行通常,继续执行程序时,用continue命令中断的地方上继续执行。不过,用下列命令,可以在自己选定的位置上继续执行程序:jumplinespecjumplocation在给定的行linespec或者location上继续执行。如果那个位置上有断点的话,执行会立即中断。关于linespeclocation的不同形式,参见7.2[指定位置]74页。jump命令惯常会和tbreak命令联合使用。参见5.1.1[设置断点]43页。jump命令除了改变程序计数器之外,不会改变当前堆栈帧,堆栈指针,也不改变任何内存位置和任何寄存器。如果行linespec是在当前执行的函数之外的函数里,如果这两个函数的参数模式或者本地变量不一样的话,那么结果就可能很诡异。由于这个原因,如果指定的行不在当前执行的函数里的话,jump命令需要用户确认。不过,如果用户很熟悉程序的机器语言的话,这个诡异的结构也是可以预测的。在很多系统里,把一个新的值存入寄存器pc 里和 jump 命令的效果一样。不同之处在于,
    这样做不会让程序开始执行;这样做只会改变
    程序继续执行的地址。例如,
    set KaTeX parse error: Undefined control sequence: \相 at position 6679: …文件设置相关的命令都能接受绝对\̲相̲对文件名作为参数。GDB 总是…@”结尾的 Unix shell 脚本也都可以做到这个。
    例如,可以用 env 来将环境变量传递给被调试程序,而不需要设置 gdbserver 的环境变量:
    $ gdbserver –wrapper env LD_PRELOAD=libtest.so — :2222 ./testprog
    17.3.2 连接 gdbserver
    在主机上执行 GDB.
    首先确认有必须的符号文件。在连接前,用 file 命令为应用程序加载符号。用 set sysroot
    来定位目标库(除非你的 GDB 是用–with-sysroot 和 sysroot 正确编译的)。
    符号文件和目标库和目标上的可执行程序和库必须是完全一致的,除了一个例外:主机系
    统上的文件不能是精简过的,而目标系统上的文件可以精简。不匹配或者文 件缺失的话,
    在调试过程中可能会导致让人迷惑的结果。在 GNU/Linux 目标上,不匹配或者文件缺失也
    可能使得 gdbserver 不能调试多线程程序。
    来接到目标(参见 17.1 节[连接到远程目标],179 页)。对于 TCP 连接,必须用 target remote
    命令先启动 gdbserver。否则会得到一个错误,此错误的文本信息依赖于主机系统,不过通
    常类似于”Connection refused”。当使用 gdbserver 的时候,不要在 GDB 里用 load 命令,因
    为此程序已经在目标系统上了。
    17.3.3 gdbserver 的监视命令
    在使用 gdbserver 的 GDB 会话期间,可以用 monitor 命令来给 gdbserver 发送特殊的请求。
    下面是可用的命令。
    monitor help
    列出可用监视命令。
    monitor set debug 0
    monitor set debug 1
    关闭或打开普通调试信息。
    monitor set remote-debug 0
    monitor set remote-debug 1
    关闭或打开和远程协议相关的调试信息(参见附录 D[远程协议],347 页)。
    monitor exit
    通知 gdbserver 立即退出。此命令后应该接着 disconnect 来结束调试会话。gdbserver 会
    将附加的进程剥离,并将其创建的进程杀死
    。在多进程调试会话的结尾,用 monitor exit 结束 gdbserver。
    17.4 远程配置
    本节介绍调试远程程序的可用配置选项。关于远程协议的文件 I/O 扩展相关的选项,参见
    [系统],381 页。
    set remoteaddresssize bits
    将存储器包里的地址最大值存储到指定数字的位中。为远程目标设置地址之后,GDB
    会将此数据上的地址位屏蔽掉。缺省值是目标
    地址里的位总数。
    show remoteaddresssize
    显示当前远程地址大小。
    set remotebaud n
    设置远程串口 I/O 波特率为 n 波特率。此值用于设置串口的速率。
    show remotebaud
    显示当前远程连接的速率。
    set remotebreak
    如果设置为on,在输入Ctrl-c来终止在远程目标上运行的程序时,GDB发送一个BREAK
    信号给远程目标。如果设置为 off,GDB 会发送
    一个’Ctrl-C’。缺省是 off,因为多数远程系统会认为’Ctrl-c’是中断信号。
    show remotebreak
    显示 GDB 是发送 BREAK 还是’Ctrl-C’来中断远程程序。
    set remoteflow on
    set remoteflow off
    打开或关闭和远程目标通讯的串口硬件流控(RTS/CTS)。
    show remoteflow
    显示当前硬件流控的设置。
    set remotelogbase base
    将登陆串口通讯协议的基数(也称为数制)设置为 base。支持的数制为:ascii,octal 和
    hex。缺省是 ascii。
    show remotelogbase
    显示当前登陆的远程串口协议的数制。
    set remotelogfile file
    将远程串口通讯记录于文件 file。缺省是不记录。
    show remotelogfile.
    显示当前存储串口通讯的文件名。
    set remotetimeout num
    设置等待远程目标回应的超时为 num 秒。缺省是 2 秒。
    show remotetimeout
    显示当前远程目标回应的超时。
    set remote hardware-watchpoint-limit limit
    set remote hardware-breakpoint-limit limit
    限制 GDB 使 u 用 limit 个远程硬件断点或监视点。缺省是-1,没有限制。
    set remote exec-file filename
    show remote exec-file
    选择可执行文件,用于在 target extended-remote 上 run。应该设置在目标系统上有效的
    文件名。如果没有设置,目标会使用缺省
    文件名(例如,最后一次运行的程序)。
    GDB 远程协议自动探测调试代理支持的数据包。如果要去除自动探测,可以使用命令打
    开或关闭单独的数据包。可以为每种包设置’on’(远程目标支持此 包),也可以设置为’
    off’(远程目标不支持此包),或者’auto’(探测远程目标支持此包与否)。这些设置的缺省
    值都是’auto’。更多关于每种 数据包的信息,参见附录 D[远程协议],347 页。
    通常的调试中,应该不需要使用这些命令。如果确实需要,可能是你的远程调试代理有
    bug,或者 GDB 有 bug。你可能要向 GDB 开发者报告问题。
    对于每种数据包名,打开或关闭这种包的命令是 set remote name-packet。可用的设置是:
    命令名 远程数据包 相关的功能
    fetch-register p info registers
    set-register P set
    binary-download X load, set
    read-aux-vector qXfer:auxv:read info auxv
    symbol-lookup qSymbol Detecting multiple threads
    attach vAttach attach
    verbose-resume vCont Stepping or resuming multiple
    threads
    run vRun run
    software-breakpoint Z0 break
    hardware-breakpoint Z1 hbreak
    write-watchpoint Z2 watch
    read-watchpoint Z3 rwatch
    access-watchpoint Z4 awatch
    target-features qXfer:features:read set architecture
    library-info qXfer:libraries:read info sharedlibrary
    memory-map qXfer:memory-map:read info mem
    read-spu-object qXfer:spu:read info spu
    write-spu-object qXfer:spu:write info spu
    get-thread-localstorage qGetTLSAddr Displaying__thread variables
    -address
    search-memory qSearch:memory find
    supported-packets qSupported Remote communications
    parameters
    pass-signals QPassSignals handle signal
    hostio-close-packet vFile:close remote get, remote put
    hostio-open-packet vFile:open remote get, remote put
    hostio-pread-packet vFile:pread remote get, remote put
    hostio-pwrite-packet vFile:pwrite remote get, remote put
    hostio-unlink-packet vFile:unlink remote delete
    noack-packet QStartNoAckMode Packet acknowledgment
    17.5 实现远程代理
    代理文件提供了目标端的通讯协议的实现,GDB 端的实现在 GDB 源代码文件’remote.c’
    里。通常,只要简单的让子程序通讯就可以了,而不需要关心 细节。(如果实现自己的代理
    代码,还是可以忽略细节:启动一个已有的代理程序。’sparc-stub.c’是组织的最好,而最
    容易读的代码。)
    要提示运行于另一机器上的程序(也就是调试目标机器),首先你必须要让程序能自己准
    备好所有的先决步骤。例如,对于 C 程序来说,需要:
    1.一个启动例程以设置 C 运行时环境;此例程名通常是’crt0′。启动例程可能是硬件供应
    商提供的,或者需要你自己实现。
    2.一个 C 子例程库以支持程序的子例程调用,主要是是管理输入和输出。
    3.将程序放到别的机器上的方法–例如,下载程序。通常由硬件制造商提供,不过也可能需
    要你根据硬件文档自己实现。
    下一步是让程序通过串口和 GDB 所在的机器通讯(主机)。总体上来说,框架如下:
    在主机上:
    GDB 已知道如何使用此协议;所有事情都设置好后,就可以用’target remote’命令了
    (参见 16 张[指定调试目标],175 页)。
    在目标上:
    必须让程序和一些特殊的子例程一起链接,这些子例程实现了 GDB 远程串口协议。包
    含这些子例程的代码称为调试代理。
    在某些远程目标上,可以用辅助程序 gdbserver 而不用将代理连接进你的程序里。详细
    说明,参见 17.3 节[使用 gdbserver 程序], 181 页。
    调试代理和远程机器的架构密切相关的;例如,要用’sparc-stub.c’来在 SPARC 板子上
    调试程序。
    下面的远程代理随 GDB 发布:
    i386-stub.c
    Intel 386和兼容系统。
    m68k-stub.c
    Motorola 68×0 架构。
    sh-stub.c
    Renesas SH 架构。
    sparcl-stub.c
    Fujitsu sparclite 架构。
    GDB 发行版里的’README’文件里列出最近添加的代理,如果有的话。
    17.5.1 代理能为你做什么
    调试代理为用户的系统架构提供 3 个子例程:
    set_debu_traps
    此例程在程序中断的时候调用 handle_exception。必须在程序开始运行时显式调用此例
    程。
    handle_exception
    此例程是代理的中心,但程序从不会显式调用它—设置代码会在触发一个陷阱(也即是
    中断之类)的时候调用 handle_exception
    。在程序中断执行的时候,handle_exception 将取得执行的控制权(例如,执行到一个
    中断的时候),并且和主机上的 GDB 通讯。
    通讯协议也在此例程里实现;handle_exception 是目标机器上的 GDB 代表。开始的时候,
    它会发送程序状态的简要信息,接着会执
    行程序,获取并传送 GDB 需要的信息,直到用户执行 GDB 命令来重新执行程序;在
    此时,handle_exception 将控制权交换给目标机器
    上的程序。
    breakpoint
    此辅助例程可以让程序得到断点。在某些特殊的环境里,GDB 肯能只能用此方式得到
    控制权。例如,如果目标系统有某种中断按钮
    ,你可能不需要调用此例程;按下中断按钮可以将控制权交给 handle_exception—实际
    上是给 GDB。在某些系统上,仅仅从串口上
    获取字符就可能引发陷阱;再有,在那样的环境里,用户不必从用户自己的程序里调用
    breakpoint—仅从主机 GDB 里运行’target
    remote’就可以获得控制权。
    如果以上限制都没有的话,或者只是想让程序在预先设定的地方中断来开始调试的话,
    调用 breakpoint。
    17.5.2 你必须为代理做什么
    随同 GDB 发布的调试代理可以设置特殊的芯片架构,但它们不知道调试目标系统余下的
    信息。
    首先,必须告诉代理如何和串口进行通讯。
    int getDebugChar()
    此例程从串口读取一个字符。此例程可能和目标系统上 getchar 相同;不同的名字可以
    区分它们,如果你想如此的话。
    void putDebugChar(int)
    此例程将一个字符写到串口去。此例程可能和目标系统上 putchar 相同;;不同的名字可
    以区分它们,如果你想如此的话。
    如果你想让 GDB 能够在程序运行的时候中断它的话,需要使用中断驱动的串口驱动,并
    设法让它在接受到’^C’(’\003′,control-C 字符)
    的时候中断。GDB 用词字符通知远程系统中断。
    要让调试目标将正确的状态返回给 GDB,可能需要修改标准代理;一个快捷但有害的方
    式是仅仅执行断点指令(”有害”是指 GDB 报告
    SIGTRAP 而不是 SIGINT)。
    其它需要提供的例程是:
    void exceptionHandler (int exception_number, void *exception_address)
    此函数用来在异常处理表注册 exception_address。由于代理没有任何途径了解目标系统
    的异常处理表是如何工作的(例如,处理
    器的异常表可能在 ROM,其包含了指向 RAM 里的表的入口),因此必须实现此函数。
    exception number 是要修改的异常编号;它的内
    涵是架构相关的(例如,不同的编号可能表示被零除,没有对齐的访问,等等)。在异
    常发生的时候,控制可以直接交给
    exception_address,并且处理器状态(栈,寄存器等)应该和处理器异常发生一样。因
    此如果要用跳转指令来跳转到
    exception_address,此跳转应该只是一个跳转,而不是跳转到一个子例程。
    对于 386 而言,exception_address 应该安装为一个中断门,在中断处理函数运行的时候
    可以被屏蔽。此门应该处于 0 级特权(最高
    权限)。SPARC 和 68k 代理不需要 exceptionHandler 的帮助就可以屏蔽中断自身。
    void flush_i_cache()
    在 SAPRC 和 SPARCLITE 架构上,此子例程清空目标机器上的指令缓存,如果有的话。
    如果没有指令缓存,此子例程可以是空操作。
    在目标系统上有指令缓存的话,GDB 需要此例程来确保程序状态的稳定。
    必须确保有以下的库函数:
    void *memset(void *, int, int)
    标准库函数 memset 将一块存储器设置为一个既定值。如果有一个自由版的 libc.a,代
    理可以在那找到 memset;否则,必须从硬件制
    造商那里得到,或者自己实现。
    如果不使用 GNU C 编译器,那就可能需要其它标准库函数;这样的话,代理的实现之间
    可能存在差异,但通常代理可能使用公共的库函数,而 GCC 将这些库函数链接成为内联代
    码。
    17.5.3 集成
    概括起来,在准备好调试程序时,必须符合如下步骤。
    1.确保已经定义了低级例程(参见 17.5.2 节[你必须为代理做什么],189 页):
    getDebugChar, putDebugChar,
    flush_i_cache, memset, exceptionHandler.
    2.在程序的开头插入下面的两行代码:
    set_debug_traps();
    breakpoint();
    3.对于 680X0 代理,需啊哟你提供一个名为 exceptionHook 的变量。通常可以使用:
    void (*exceptionHook)() = 0;
    不过,要是在调用 set_debug_traps,设置其为指向你的程序里的一个函数的话,那么这个
    函数就会在 GDB 从一个陷阱(例如,总线错
    误)中断后继续执行的时候被调用。exceptionHook 激发的函数带有一个参数:一个整型
    参数,代表一个异常编号。
    4.编译并链接到一起:程序,GDB 调试代理和支持例程。
    5.确保在目标机器和 GDB 主机之间有一个串口连接,并且指定主机上的串口号。
    6.下载程序到目标机器上(或者用制造商提供的方法将其放到机器上),然后开始运行程序。
    7.在主机上开始运行 GDB,然后连接到目标(参见 17.1 节[连接到远程目标],179 页)。
    第十八章 配置相关的信息
    虽然差不多全部 GDB 命令对于本地和交叉版本的调试器都是可用的,但还是有些例外。
    本章将介绍只有在某些配置下才可用的命令。
    有三种主要的配置:本地配置,此配置的宿主和目标都是同一个机器,嵌入式操作系统配
    置,此配置通常和几个不同处理器架构一样,
    裸嵌入式处理器,此配置的差异非常大。
    18.1 本地
    本节介绍和特殊的本地配置相关的细节。
    18.1.1 HP-UX
    在 HP-HX 系统里,如果如果引用到以符号$开头的函数或变量名,GDB 会首先搜索用户
    和系统名,然后再搜索惯用变量。
    18.1.2 BSD libkvm 接口
    BSD 类系统(FreeBSD/NetBSD/OpenBSD)提供一个内核存储器接口,可以用统一的接口
    来访问内核虚拟存储映像,包括系统和崩溃转储。 GDB 使用此接口来在很多本地 BSD 配
    置上提供调试内核和内核崩溃转储。要调试活动的系统,加载当前运行着的内核到 GDB 并
    连接到 kvm 目标:
    (gdb) target kvm
    要调试崩溃转储,要用崩溃转储的文件名作为参数:
    (gdb) target kvm /var/crash/bsd.0
    一旦连接到 kvm 目标,用下面的命令:
    kvm pcb 将 PCB(进程控制块)地址设置为当前上下文。
    kvm proc 将 proc 地址设置为当前上下文。此命令在现代 FreeBSD 系统下不可用。
    18.1.3 SVR4 进程信息
    SVR4 的许多版本和相容的系统都提供名为’/proc’的工具,可以用于检查运行着的进程
    所使用的文件系统子例程的映像。如果 GDB 在带
    有这个工具的系统上配置的话,命令 info proc 就能收集运行程序的进程的信息,或者你的
    系统上的任意进程的信息。info proc 只能在
    那些包含 procfs 代码上的 SVR4 系统上工作。包括,gnu/Linux, OSF/1 (Digital Unix), Solaris,
    Irix, 和 Unixware,但不包括 HP-UX
    ,举例来说。
    info proc
    info proc process-id
    概述进程的可用信息。如果指定了进程 id,只显示此进程的信息;否则显示被调试进程
    的信息。概述信息包括调试进程 ID,命
    令行,当前工作目录,可执行文件的绝对文件名。
    某些系统里,进程号可以是’[pid]/tid’的形式,此形式标明了一个进程内的某个线程
    号。如果缺失可选的 pid 部分,意味着此
    进程的线程正处于调试状态(前导的’/'还是必须的,否则 GDB 会将此数字认为是进
    程号,而不是线程号)。
    info proc mappings
    报告此进程内可用的内存地址空间范围,还包括此进程的各部分是否具有可读,写,可
    执行的权利。在 GNU/Linux 系统里,每个
    存储器范围包含映射到此范围的目标文件,而不是此范围的存储器方位权。
    info proc stat
    info proc status
    这两个字命令是GNU/Linux系统特有的。它们显示进程相关的信息,包括user id和group
    id;进程内有多少线程;虚拟内存的
    使用;挂起的信号,阻塞的信号,忽略的信号;TTY;消耗的系统和用户时间;堆栈大
    小;’nice’值;等等。更多信息,参见
    ‘proc’手册(在 shell 上输入 man 5 proc)。
    info proc all
    显示上面 info proc 子命令描述的所有的信息。
    set procfs-trace
    此命令打开和关闭 procfs API 调用的跟踪。
    show procfs-trace
    此命令显示当前 procfs API 调用的跟踪状态。
    set procfs-file file
    让 GDB 将 procfs API 的跟踪信息写入 file 文件。GDB 会将跟踪信息附加到文件里。缺
    省是将跟踪信息显示到标准输出里。
    show procfs-file
    显示保存 procfs API 跟踪信息的文件。
    proc-trace-entry
    proc-trace-exit
    proc-untrace-entry
    proc-untrace-exit
    这些命令打开或关闭 syscall 接口的跟踪。
    info pidlist
    此命令是 QNX Neutrino 特有,显示所有进程的列表和每个进程里的所有线程。
    info meminfo
    此命令是 QNX Neutrino 特有,显示所有映射信息的列表。
    18.1.4 调试 DJGPP 程序的功能
    DJGPP 是 MS-DOS 和 MS-Windows 下的 GNU 开发工具的一部分。DJGPP 程序是 32 位保
    护模式程序,使用 DPMI(DOS Protected-Mode
    Interface,DOS 保护模式接口)API,运行于实模式 DOS 系统和其模拟器之上。
    GDB 支持本地调试 DJGPP 程序,并定义了一些 DJGPP 口特有的命令。本节将介绍这些
    命令。
    info dos
    本命令用于打印目标系统和重要的 OS 结构的信息。
    info dos sysinfo
    本命令打印系统的分类信息:CPU 类型和功能,OS 版本号和, DPMI 版本,可用的常
    用和 DPMI 存储器。
    info dos gdt
    info dos ldt
    info dos idt
    这 3 个命令分别打印全局,本地和中断描述符表(GDT,LDT 和 IDT)。描述符表是存储描
    述符的数据结构,每个描述符代表
    一个当前使用的段。段选择器是指向一个描述符表的索引;这个索引所代表的表条目保
    存着描述符的基址和边界,其属
    性和访问权限。
    典型的 DJGPP 程序使用 3 个段:代码段,数据段(用于数据和栈)和 DOS 段(此段允
    许访问 DOS/BIOS 数据结构和在常用存
    储器里绝对地址)。不过,DJGPP 主机通常会定义附加段来支持 DJGPP 环境。
    这些命令允许打印描述符表的条目。不带参数的话,打印指定描述符表所有的条目。一
    个参数的话,并且这个参数必须
    是一个整数表达式,将会打印这个参数指定的表的条目。例如,下面是打印调试程序数
    据段的常用方式:
    (gdb) info dos ldt $ds
    0×13f: base=0×11970000 limit=0×0009ffff 32-Bit Data (Read/Write, Exp-up)
    用这个方式可以很方便的查看一个指针是否越出数据段(例如被篡改了)。
    info dos pde
    info dos pte
    这两个命令分别打印页目录和页表的条目。页目录和页表是存放控制虚拟存储器的地址
    和所映射的物理地址的数据结构
    。页表包含一个条目,这个条目里指向一个映射到程序地址空间的存储器页;可能有几
    个页表,每个最多包含 4096 个条
    目。页目录最多有 4096 个条目,每个条目指向当前正在使用的页表。
    不带参数的话,info dos pde 打印整个页目录,info dos pte 打印页表里所有的条目。一
    个整数表达式的参数的话,
    info dos pde 命令打印此参数代表的页目录表。info dos pte 带一个参数的话,打印单个
    页表的一个条目,此页表号由
    页目录所指定。
    要是程序使用了 DMA(Direct Memory Access,直接存储器访问)的话,那这两个命令就
    很有用,因为 DMA 需要物理地址来
    编 DMA 控制器。
    这些命令只支持某些 DPMI 服务器。
    info dos address-pte addr
    这个命令显示指定地址所在的页表条目。参数 addr 是一个线性地址,其应该是已经和
    正确的段基址相加的,因为此命令
    接受的地址参数允许属于各个段。例如,下面是显示变量 i 所在的页的页表条目:
    (gdb) info dos address-pte __djgpp_base_address + (char *)&i
    Page Table entry for address 0×11a00d30:
    Base=0×02698000 Dirty Acc. Not-Cached Write-Back Usr Read-Write +0xd30
    这是说 i 存储于相对页基址的偏移地址地址 0xd30 上,此页的物理地址基址是 0×
    02698000,并且显示此页的所有属性。
    注意,必须将变量的地址转化为 char *,因为否则的话,__djgpp_base_address 的值–
    DJGPP 里所有变量和函数的基址
    会用 C 指针算法来累加:如果 i 声明为一个整形 int 变量,那么 GDB 会加
    __djgpp_base_address 4 次到 i 的地址上。
    下面是另外一个例子,显示转换缓冲区的页表条目:
    (gdb) info dos address-pte *((unsigned *)&_go32_info_block + 3)
    Page Table entry for address 0×29110:
    Base=0×00029000 Dirty Acc. Not-Cached Write-Back Usr Read-Write +0×110
    (加偏移 3 是因为转换缓冲区的地址是_go32_info_block 结构的第三个成员。)输出很
    清楚的告诉我们 DPMI 服务器将其
    地址以 1:1 的比例映射到惯用存储器上,例如,物理(0×00029000+0×110)和线性(0 ×29220)地址是相同的。
    除了本地调试,DJGPP 口支持通过串口连接远程调试。下列命令专门支持 GDB 的 GJGPP
    口远程串口调试。
    set com1base addr
    本命令设置串口’COM1′的 I/O 口基址。
    set com1irq irq
    本命令设置’COM1′串口使用的中断(IRQ)线。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FreeRTOS是一个开源的实时操作系统,它提供了一套用嵌入式系统的任务调度和管理机制。GDB(GNU Debugger)是一个功能强大的调试工具,可以用于调试C/C++程序。在使用FreeRTOS进行开发时,可以结合GDB进行调试。 要在FreeRTOS中使用GDB进行调试,需要进行以下几个步骤: 1. 配置编译器:首先,需要确保你的编译器支持GDB调试功能。常用的编译器如GCC和Keil都支持GDB调试。 2. 编译选项:在编译FreeRTOS应用程序时,需要添加一些编译选项以支持GDB调试。例如,在GCC中,可以使用"-g"选项来生成调试信息。 3. 连接器脚本:在链接应用程序时,需要使用连接器脚本来指定调试信息的位置。连接器脚本可以告诉GDB在哪里找到符号表和调试信息。 4. 启动GDB调试:在编译和链接完成后,可以使用GDB启动调试会话。可以通过命令行输入"gdb"命令来启动GDB,并使用"target remote"命令连接到目标设备。 5. 设置断点:在GDB中,可以使用"break"命令设置断点。可以设置函数断点、行号断点或地址断点等。 6. 执行调试:一旦设置好断点,可以使用GDB的调试命令来执行程序。可以使用"run"命令来运行程序,使用"step"命令逐行执行,使用"next"命令执行下一行,使用"continue"命令继续执行等。 7. 查看变量:在调试过程中,可以使用GDB的"print"命令来查看变量的值。可以使用"info locals"命令查看局部变量,使用"info global"命令查看全局变量等。 8. 结束调试:当调试完成后,可以使用GDB的"quit"命令退出调试会话。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值