0%

gdb_more

gdb介绍:

gdb是一个c/c++ 语言在gnu编译器下的debugger调试器,它支持单步调试,查看运行时内存,core-dump文件等功能;
它可以做很多事情来发现程序的bug,通过以下步骤:
a Start your program, specifying anything that might affect its behavior.
b Make your program stop on specified conditions.
c Examine what has happened, when your program has stopped.
d Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.
它还支持其他语言,具体见文档;https://sourceware.org/gdb/download/onlinedocs/gdb/index.html

一个简单的例子:

https://sourceware.org/gdb/download/onlinedocs/gdb/Sample-Session.html#Sample-Session
在gcc编译时,带上-g

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(gdb) set width 70 设置gdb屏幕显示宽度为70列;
(gdb) break m4_changequote 设置一个断点,m4_changequote 这个是源文件中的一个函数名
Breakpoint 1 at 0x62f4: file builtin.c, line 879.
开始运行:
(gdb) run
Starting program: /work/Editorial/gdb/gnu/m4/m4
会在断点停住,并展示上下文信息:
Breakpoint 1, m4_changequote (argc=3, argv=0x33c70)
at builtin.c:879
879 if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3))
于是我们可以接着执行下一行:
(gdb) n #next,会执行下一行,但是碰到子函数不会进去;
882 set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\
: nil,
(gdb) s #step 真正的单步调试,碰到子函数会进去一步一步执行
(gdb) finish 会将正在调试的函数执行完然后打印返回值,等待接下来的指令;简写:fin
(gdb) bt # backtrace 可以看到当前执行的backtrace
(gdb) p var #打印当前上下文 var变量的值 #print
(gdb) l 列出当前的代码 #list
(gdb) c #continue 继续执行直到结束
(gdb) Ctrl-d / quit 结束和退出gdb

常见指令:

系统教程:

如何进入和退出gdb

  • 简单的进入和出来
    type ‘gdb’ to start GDB.
    type quit or Ctrl-d to exit.

GDB指令

可以缩写gdb的指令,用指令名字前面的几个字母,如果这个缩写是明确的;
您可以只输入回车来重复某些GDB命令,您还可以使用TAB键来让GDB填写命令中的剩余单词(或者向您显示可用的替代,如果有不止一种可能)。

指令语法

如何给定指令给gdb

关于指令语法: 指令名+参数
gdb的指令是单行输入,但是没有限制多长。起于一个指令名字,接着是一些参数,依赖于具体的指令;
例如:
step 接收一个参数,用来表示单步几次,如
step 5 ,会执行5次step
也可以不指定参数,有些指令不允许任何参数;

关于简写:
step的指令可以缩减为s,其他类似

关于回车的使用:
1)空白行回车会重复之前的指令
2)当使用回车重复list和x命令时,会构造新的参数,而不是完全按类型重复。这允许很容易地扫描源或存储器。
3)GDB还可以以另一种方式使用RET:以类似于常用实用程序more的方式对冗长的输出进行分区(参见屏幕大小)。
因为在这种情况下很容易多次按下RET,所以GDB会在生成这种显示的任何命令之后禁用命令重复。

关于注释:
从#到行尾的任何文本都是注释;它什么都不做。这主要在命令文件中有用(参见命令文件)。

Ctrl-o绑定对于重复一个复杂的命令序列非常有用。这个命令接受当前行,就像RET一样,然后从历史记录中获取相对于当前行的下一行,以便进行编辑。??

指令设置

如何改变指令的默认行为
通过set等,可以改变一些指令的行为:如

  1. 一个简单的例子:
    1
    2
    3
    4
    5
    6
    gdb)set print elements 10
    gdb)print some_array
    $1 = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90...} #只会输出10个,默认是200
    也可以这样指定,这样会覆盖之前set的设置:
    (GDB) print -elements 10 -- some_array
    $1 = {0, 10, 20, 30, 40, 50, 60, 70, 80, 90...}
  2. 临时的设置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    语法;
    with setting [value] [-- command]
    w setting [value] [-- command]
    临时设置是在指令执行的期间生效,如果command未指定,则是作用的上个指令的重复;
    eg:
    (GDB) with print array on -- print some_array
    is equivalent to the following 3 commands:

    (GDB) set print array on
    (GDB) print some_array
    (GDB) set print array off
    也可以作用到用户设置的指令:
    (GDB) with print pretty on -- my_complex_command
指令的补全:

如果只有一种可能,GDB可以在命令中为您填写单词的其余部分;
它还可以在任何时候向您显示命令中下一个单词的有效可能性。这适用于GDB命令、GDB子命令、命令选项和程序中符号的名称。
当您想让GDB填写单词的剩余部分时,请按TAB键。如果只有一种可能,GDB填充这个单词,并等待您完成命令(或按RET输入)。例如,如果你输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(gdb) info bre TAB
GDB fills in the rest of the word ‘breakpoints’, since that is the only info subcommand beginning with ‘bre’:

(gdb) info breakpoints
接着你可以按回车键来执行,如果不是它,则删除重写,当然也可以info bre后回车,利用缩写功能;

如果有多个选项:会展示出来让你选
(gdb) b make_ TAB
GDB sounds bell; press TAB 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


如果嫌打印的太多了:
可以设置:
set max-completions limit
set max-completions unlimited
Set the maximum number of completion candidates. GDB will stop looking for more completions once it collects this many candidates. This is useful when completing on things like function names as collecting all the possible candidates can be time consuming. The default value is 200. A value of zero disables tab-completion. Note that setting either no limit or a very large limit can make completion slow.

show max-completions

或者其他办法:
https://sourceware.org/gdb/current/onlinedocs/gdb/Completion.html#Completion

指令选项
有些命令接受以破折号开头的选项,例如 print -pretty,类似于命令名称,您可以使用缩写词GDB选项选项名的前几个字母,
如果这个缩写是明确的,您还可以使用TAB键让GDB填写剩下的一个字一个选项(或向你们展示可用的替代品,如果有一个以上的可能性)。
有时候加上缩写时,会混淆,如print -p是意思是 print -pretty还是打印的-p? ,所以这个时候可以用–
print –p

有些选项被描述为接受一个既可以是开的也可以是关的参数。这些被称为布尔选项。与布尔设置命令类似,on和off是典型的值,但是1、yes和enable中的任何一个也可以用作“true”值,
0、no和disable中的任何一个也可以用作“false”值。您也可以忽略“true”值,因为默认情况下它是隐含的。
例如,下面两个指令相同:

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) print -object on -pretty off -element unlimited -- *myptr
(gdb) p -o -p 0 -e u -- *myptr

当你不知道指令的可选项时:
(gdb) print -TABTAB
-address -max-depth -raw-values -union
-array -null-stop -repeats -vtbl
-array-indexes -object -static-members
-elements -pretty -symbol

完成在某些情况下会给你一个选项期望的参数的建议。例如:
(gdb) print -elements TABTAB
NUMBER unlimited
指令的帮助

gdb) help
List of classes of commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
aliases -- User-defined 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)

使用一个通用帮助类作为参数,您可以获得该类中各个命令的列表。如果命令有别名,
别名将在命令名之后,用逗号分隔。如果别名具有默认参数,则在第一行之后给出别名的完整定义。例如,下面是类状态的帮助显示:
(gdb) help status
Status inquiries.

List of commands:

info, inf, i -- Generic command for showing things
about the program being debugged
info address, iamain -- Describe where symbol SYM is stored.
alias iamain = info address main
info all-registers -- List of all registers and their contents,
for selected stack frame.
...
show, info set -- 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)

还有指令的帮助:
gdb) help command
...

用gdb运行程序

如何编译才能用gdb -g

这样调试信息会存储在对象文件中,它描述了每个变量或函数的数据类型以及可执行代码中源行号和地址之间的对应关系。

使用’ -O ‘编译器选项,将发送给客户的程序进行了优化编译。然而,有些编译器无法同时处理’ -g ‘和’ -O ‘选项。使用这些编译器,
您无法生成包含调试信息的优化的可执行文件。

如何通过gdb启动你的程序
1
2
3
4
5
gdb) run / r
几种指定可执行程序的方式:
1) gdb program
2) gdb
gdb) file program

如果您在支持进程的执行环境中运行程序,则run将创建一个较差的进程,并让该进程运行您的程序。
在某些没有进程的环境中,run跳转到程序的开头。其他目标,比如“remote”,总是在运行。如果你得到这样的错误消息:
The “remote” target does not support “run”.
Try “help target” or “continue”.

then use continue to run your program. You may need load first
运行的时候,一些预先的动作环境等,可能需要指定,如下,在接下来的几节会体现:

  1. The arguments.
    如果shell是可用的,那可以通过shell传入,可以通过SHELL环境变量来指定使用哪种shell,如果不指定,gdb默认用/bin/sh ,
    You can disable use of any shell with the set startup-with-shell command

2)The environment.
you can use the GDB commands set environment and unset environment to change parts of the environment
3)The working directory.
You can set your program’s working directory with the command set cwd
4)The standard input and output.

gdb中程序的运行可以看上面介绍,对停止:一旦您的程序停止,您可以调用程序中的函数,使用打印或调用命令。(print/call)

关于start命令:
‘ start ‘命令相当于在主过程(main)开始处设置一个临时断点,然后调用’ run ‘命令。

关于starti命令:
‘ starti ‘命令相当于在程序执行的第一个指令处设置一个临时断点,然后调用’ run ‘命令。对于包含精化阶段的程序,starti命令将在精化阶段开始时停止执行。
关于精细阶段,是指如c++一些全局变量会在main之前就执行了,所以为了细化断点,在全局变量初始化前停,需用starti

关于运行的控制:
还可以包裹在exec函数执行,还可以set startup-with-shell on/off开启关闭shell执行,以及set exec-wrapper env ‘LD_PRELOAD=libtest.so’
控制执行的环境,等等,更多见:
https://sourceware.org/gdb/current/onlinedocs/gdb/Starting.html#Starting

你的程序的参数如何传入gdb中

程序的参数可以通过run命令的参数指定。它们被传递给shell, shell展开通配符并执行I/O的重定向,从而指向您的程序。
您的SHELL环境变量(如果存在的话)指定了SHELL GDB使用什么。如果没有定义SHELL, GDB使用默认的SHELL (Unix上的/bin/sh)。

run 不带参数将使用与前一次运行相同的参数,或由set args命令设置的参数。
set args来设置:
gdb) set -p 192.3.2.1
指定下次运行程序时要使用的参数。如果set args没有参数,
run将不带参数执行程序。一旦你运行了带参数的程序,在下次运行之前使用set args是再次运行它而不带参数的唯一方法。

show args
用来显示设置的参数;

你的程序的执行环境和gdb

环境由一组环境变量及其值组成。环境变量通常会记录诸如用户名、主目录、终端类型和要运行程序的搜索路径等内容。通常您使用shell设置环境变量,它们被您运行的所有其他程序所继承。
在调试时,尝试在修改过的环境中运行程序,而不必重新启动GDB,这可能很有用。
几个指令:
1)path directory eg: path /bin/sh
将directory添加到PATH环境变量(可执行文件的搜索路径)的前面,这个环境变量将被传递给您的程序。
2)show paths
3)show environment [varname]
4)set environment varname [=value]
eg:set env USER = foo
5) unset environment varname

gdb下,你的程序的执行目录

几个命令:

  1. set cwd [directory]
    Set the inferior’s working directory to directory
  2. show cwd

3)cd [directory]
Set the GDB working directory to directory. If not given, directory uses ‘~’.
4) pwd
Print the GDB working directory.

gdb下,你的程序的输入和输出

默认情况下,在GDB下运行的程序向GDB使用的同一个终端进行输入和输出。
GDB将终端切换到它自己的终端模式以与您交互,但它会记录您的程序正在使用的终端模式,并在您继续运行程序时切换回它们。

几个指令:
1)info terminal
Displays information recorded by GDB about the terminal modes your program is using.
2)重定向运行输出:
run > outfile
3) tty /dev/ttyb 重定向tty
4) set inferior-tty [ tty ]
5) show inferior-tty

如何用gdb attach一个正在运行的程序

指令:
gdb attach process-id
这个命令附加到一个正在运行的进程—一个在GDB外部启动的进程
detach
release gdb control

gdb下如何杀死子进程

gdb) kill
终止您的程序在GDB下运行的子进程。
如果希望调试核心转储core而不是正在运行的进程,则此命令很有用。当程序运行时,GDB会忽略任何核心转储文件core。

在某些操作系统上,如果在GDB内部设置了断点,则程序不能在GDB外部执行。在这种情况下,可以使用kill命令允许在调试器外部运行程序。
如果您希望重新编译和重新链接程序,kill命令也很有用,因为在许多系统上,当可执行文件在进程中运行时,是不可能修改它的。在这种情况下,当您下一次输入run时,GDB会注意到文件已经更改,并再次读取符号表(同时试图保留当前的断点设置)。

调试多个下级连接和程序

https://sourceware.org/gdb/current/onlinedocs/gdb/Inferiors-Connections-and-Programs.html#Inferiors-Connections-and-Programs

调试多线程的程序

https://sourceware.org/gdb/current/onlinedocs/gdb/Threads.html#Threads

调试forks

https://sourceware.org/gdb/current/onlinedocs/gdb/Forks.html#Forks

设置书签以便之后返回;

checkpoint: 用于保存程序的快照,然后可以返回;
checkpoint是程序在那一刻的快照,当我们发现错过了某个调试机会时,可以再次回到checkpoint保存的那个程序状态。
https://sourceware.org/gdb/current/onlinedocs/gdb/Checkpoint_002fRestart.html#Checkpoint_002fRestart
eg:http://blog.chinaunix.net/uid-23629988-id-2943273.html

停止和继续

断点:监控点和catch points

https://sourceware.org/gdb/current/onlinedocs/gdb/Breakpoints.html#Breakpoints

  1. 设置断点
    2)设置观察点
    3)设置捕获点:catchpoints
    4)删除断点
    5)禁用断点
  2. 断点加条件控制
    7)动态打印
    8)保存断点到文件
    9)静态的probe point
    10)error in breakpoints
  3. Breakpoint-related Warnings:
continue和step指令
skip函数和文件
信号
停止和启动多线程程序

回退运行

进程记录和重放

测试栈

栈帧的概念

调用堆栈被分成若干连续的块,称为堆栈帧,或简称帧;每一帧是与一个函数调用相关联的数据。
框架包含给出给函数的参数、函数的局部变量和函数执行的地址。

当程序启动时,堆栈只有一个帧,即main函数的帧。这叫做初始坐标系或者最外层的坐标系。每次调用一个函数,就会产生一个新的框架。每次函数返回时,用于该函数调用的帧就会被消除。如果一个函数是递归的,那么同一个函数可以有多个框架。
执行实际发生的函数的框架称为最内层框架。这是所有仍然存在的堆栈帧中最近创建的。

在程序中,堆栈帧通过它们的地址来标识。堆栈帧由许多字节组成,每个字节都有自己的地址;每一种计算机都有一个约定,即选择一个字节的地址作为帧的地址。
通常这个地址被保存在一个叫做帧指针寄存器(参见$fp)的寄存器中,而在这个帧中执行。

GDB给每个现有的堆栈帧标上一个级别,最里面的帧为0,调用它的帧为1,以此类推。这些级别数字为您提供了一种在GDB命令中指定堆栈帧的方法。
术语帧数和帧级可以互换地用来描述这个数字。

有些编译器提供了一种方法来编译函数,使它们在不使用堆栈帧的情况下运行。(例如,GCC选项
“-fomit-frame-pointer”
生成没有框架的函数。)为了节省帧设置时间,我们偶尔会使用大量的库函数。GDB只有有限的工具来处理这些函数调用。如果最内层的函数调用没有堆栈帧,
GDB仍然认为它有一个单独的帧,它通常被编号为零,允许正确地跟踪函数调用链。然而,GDB没有在栈的其他地方提供无框架函数。

backtraces调用链

回溯是程序如何到达当前位置的摘要。它显示了每帧一行,对于许多帧,从当前执行的帧(帧0)开始,接着是它的调用者(帧1),一直到堆栈。

要打印整个堆栈的回溯信息,可以使用backtrace命令或它的别名bt。该命令将为堆栈中的每帧打印一行。
默认情况下,将打印所有堆栈帧。您可以在任何时候通过输入系统中断字符(通常是Ctrl-c)来停止回溯。

语法:
backtrace [option]… [qualifier]… [count]
bt [option]… [qualifier]… [count]
https://sourceware.org/gdb/current/onlinedocs/gdb/Backtrace.html#Backtrace

选择一个栈帧:

大多数用于检查程序中堆栈和其他数据的命令都在此时选择的任何堆栈帧上工作。
下面是选择堆栈帧的命令;所有这些都通过打印刚刚选择的堆栈帧的简要描述来完成。

1)指令1:
语法:
frame [ frame-selection-spec ]
f [ frame-selection-spec ]
frame命令允许选择不同的堆栈帧。帧选择规范可以是以下任意一种:
num
level num
选择帧级别num,回想一下,帧0是最内层(当前正在执行)的帧,帧1是调用最内层的帧,以此类推。最高层次的框架通常是主框架。
由于这是在帧堆栈中导航最常用的方法,所以可以省略字符串级别。例如,下面两个命令是等价的:
(gdb) frame 3
(gdb) frame level 3

2)指令2:
address stack-address
选择堆栈地址为stack-address的帧。例如,一个帧的堆栈地址可以在信息帧的输出中看到
(gdb) info frame
Stack level 1, frame at 0x7fffffffda30:
rip = 0x40066d in b (amd64-entry-value.cc:59); saved rip 0x4004c5
tail call frame, caller of frame at 0x7fffffffda30
source language c++.
Arglist at unknown address.
Locals at unknown address, Previous frame’s sp is 0x7fffffffda30

The stack-address for this frame is 0x7fffffffda30 as indicated by the line:
Stack level 1, frame at 0x7fffffffda30:

3)指令3:
function function-name
选择函数function-name的堆栈帧。如果function function-name有多个堆栈帧,则选择最内部的堆栈帧。

4)指令4:
view stack-address [ pc-addr ]
查看一个不属于GDB回溯的帧。被查看的帧有堆栈地址stack-addr,也可以选择pc-addr的程序计数器地址

5)指令5:
up n
向上移动n帧堆栈;N默认为1。对于正数n,它向最外层的帧移动,向更高的帧移动,向存在时间更长的帧移动。

6)指令6:
down n
向下移动n帧堆栈;N默认为1。对于正数n,它向最里面的帧推进,向更低的帧数推进,向最近创建的帧推进。
eg:
(gdb) up
#1 0x22f0 in main (argc=1, argv=0xf7fffbf4, env=0xf7fffbfc)
at env.c:10
10 read_input_file (argv[i]);

栈帧的相关信息:

有一些指令可以打印选定的栈帧的相关信息:

  1. frame/f
    当不带任何参数使用时,这个命令不会改变所选择的帧,而是打印当前所选择的堆栈帧的简要描述。它可以缩写为f。该命令用于选择一个堆栈帧,带参数。

2)info frame
info f
该命令打印所选堆栈帧的详细描述,包括:
the address of the frame
the address of the next frame down (called by this frame)
the address of the next frame up (caller of this frame)
the language in which the source code corresponding to this frame is written
the address of the frame’s arguments
the address of the frame’s local variables
the program counter saved in it (the address of execution in the caller frame)
which registers were saved in the frame

  1. info frame [ frame-selection-spec ]
    info f [ frame-selection-spec ]
    Print a verbose description of the frame selected by frame-selection-spec

  2. info args [-q]
    Print the arguments of the selected frame

  3. info args [-q] [-t type_regexp] [regexp]

6)info locals [-q]
Print the local variables of the selected frame, each on a separate line.
7)info locals [-q] [-t type_regexp] [regexp]

Applying a Command to Several Frames.
Management of Frame Filters.

测试源文件

打印源行

list 的各种用法:
https://sourceware.org/gdb/current/onlinedocs/gdb/List.html#List

具体化位置:

https://sourceware.org/gdb/current/onlinedocs/gdb/Specify-Location.html#Specify-Location

编辑源文件以及选择编辑器

https://sourceware.org/gdb/current/onlinedocs/gdb/Edit.html#Edit

查找源文件:如何利用正则查找

https://sourceware.org/gdb/current/onlinedocs/gdb/Search.html#Search

具体化源目录

可执行程序有时不记录源文件的目录,而只记录其名称。即使是这样,也可以在编译会话和调试会话之间移动目录。GDB有一个目录列表来搜索源文件;
这称为源路径。每次GDB想要一个源文件时,它会按照目录在列表中出现的顺序尝试列表中的所有目录,直到找到一个具有所需名称的文件。

例如,假设一个可执行文件引用/usr/src/foo-1.0/lib/foo.c文件,没有记录编译目录,源路径是/mnt/cross。GDB将在以下位置查找源文件:
/usr/src/foo-1.0/lib/foo.c
/mnt/cross/usr/src/foo-1.0/lib/foo.c
/mnt/cross/foo.c

如果源文件不在上述任何位置,则会打印一个错误。GDB不查找源文件名的部分,例如/mnt/cross/src/foo-1.0/lib/foo.c。同样,也不会搜索源路径的子目录:如果源路径是/mnt/cross,
并且二进制文件指向foo.c, GDB将无法在/mnt/cross/usr/src/foo-1.0/lib下找到它。

。。。
怎么设置目录呢?

源和机器码

可以使用命令信息行将源行映射到程序地址(反之亦然),命令disassemble将地址范围作为机器指令显示。可以使用命令集disassemble-next-line设置在停止执行时是否反汇编下一个源行。
当在GNU Emacs模式下运行时,info line命令会使箭头指向指定的行。此外,信息行以符号形式和十六进制形式打印地址。

检查数据

常规的检查数据是使用print指令,或者缩写为p,或者它的替代者 inspect. 它能求值和打印你的程序中表达式写入的值;它也能利用python来做格式化打印,如果你的gdb配置了正确的python;

print 举例:

语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
print [[option] --] expr 
print [[option] --] /f expr
expr是一个表达式(在语言中)。默认的,表达式打印值的格式取决于数据的类型;你也可以选择一个不同的格式,通过使用/f,
f是一个具体的格式:如
x:将该值的位视为整数,以16进制方式输出。
d:以带符号的十进制形式打印整数。
u:无符号十进制整数
o:打印为八进制整数。
t:以二进制形式打印整数。字母“t”代表“二”。11
a:以地址的形式打印,十六进制的绝对地址和从最近的前面符号的偏移量。你可以使用这种格式来发现未知地址的位置(在什么函数中):
(gdb) p/a 0x54320
$3 = 0x54320 <_initialize_vx+396>
和指令:info symbol 0x54320 类似
c:将其视为整数并将其打印为字符常量。这将打印数值及其字符表示形式。对于7位ASCII范围以外的字符,字符表示将被八进制转义' \nnn '替换。
如果没有这种格式,GDB将char、unsigned char和signed char数据显示为字符常量。向量的单字节成员显示为整数数据。
f:将值的位视为浮点数并使用典型的浮点语法打印。
s:如果可能,将其视为字符串。使用这种格式,指向单字节数据的指针显示为以空结束的字符串,单字节数据的数组显示为固定长度的字符串。其他值以其自然类型显示。
如果没有这种格式,GDB将char、unsigned char和signed char的指针和数组显示为字符串。向量的单字节成员显示为整数数组。
z:与' x '格式一样,该值被视为整数并打印为十六进制,但打印前导零以将值填充到整数类型的大小。
r:使用' raw '格式打印。默认情况下,GDB将使用基于python的漂亮打印机,如果有的话(请参阅漂亮打印)。这通常会导致值内容的更高级别显示。' r '格式绕过了任何可能存在的Python漂亮打印机。
例如,要以十六进制方式打印程序计数器(请参阅寄存器),输入
p/x $pc
注意斜杠前不需要空格;这是因为GDB中的命令名不能包含斜杠。
若要以不同的格式重新打印值历史中的最后一个值,可以使用只带格式而不带表达式的print命令。例如,' p/x '以十六进制重新打印最后一个值。

[option]可以有哪些值? print命令支持很多options来允许覆盖由set print子命令设置的相关全局打印设置:
表达式:

print和许多其他GDB命令接受一个表达式并计算它的值。您正在使用的编程语言定义的任何类型的常量、变量或操作符在GDB的表达式中都是有效的。这包括条件表达式、函数调用、强制转换和字符串常量。
它还包括预处理器宏,如果你编译你的程序包含这些信息;看到编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
-address [on|off]
Set printing of addresses. Related setting: set print address.
打印设置:
GDB提供了以下方法来控制数组、结构和符号的打印方式
set print address
set print address on 默认是开启的:
(gdb) f
#0 set_quotes (lq=0x34c78 "<<", rq=0x34c88 ">>")
at input.c:530
530 if (lquote != def_lquote)
set print address off
Do not print addresses when displaying their contents. For example, this is the same stack frame displayed with 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)

-array [on|off] 更漂亮的打印数组,默认关闭
Pretty formatting of arrays. Related setting: set print array.

-array-indexes [on|off] 设置数组索引是否打印,即在显示数组时打印每个元素的索引。,默认关闭
Set printing of array indexes. Related setting: set print array-indexes.

-elements number-of-elements|unlimited 设置要打印的字符串字符或数组元素的限制。这个限制默认被设置为200
Set limit on string chars or array elements to print. The value unlimited causes there to be no limit. Related setting: set print elements.

-max-depth depth|unlimited 设置打印的结构化内容的最大深度,默认未知?
Set the threshold after which nested structures are replaced with ellipsis. Related setting: set print max-depth.
举例:
For example, given this C code

typedef struct s1 { int a; } s1;
typedef struct s2 { s1 b; } s2;
typedef struct s3 { s2 c; } s3;
typedef struct s4 { s3 d; } s4;

s4 var = { { { { 3 } } } };
The following table shows how different values of depth will effect how var is printed by GDB:

depth setting Result of ‘p var’
unlimited $1 = {d = {c = {b = {a = 3}}}}
0 $1 = {...}
1 $1 = {d = {...}}
2 $1 = {d = {c = {...}}}
3 $1 = {d = {c = {b = {...}}}}
4 $1 = {d = {c = {b = {a = 3}}}}
To see the contents of structures that have been hidden the user can either increase the print max-depth, or they can print the elements of the structure that are visible, for example

(gdb) set print max-depth 2
(gdb) p var
$1 = {d = {c = {...}}}
(gdb) p var.d
$2 = {c = {b = {...}}}
(gdb) p var.d.c
$3 = {b = {a = 3}}

-null-stop [on|off] 将字符数组的打印设置为在第一个空字符处停止。
Set printing of char arrays to stop at first null char

-object [on|off] 设置是否打印虚函数表;
Set printing C++ virtual function tables.

-pretty [on|off] 设置是否以比较好看的格式打印;
Set pretty formatting of structures.

-raw-values [on|off] 设置是否以原始值打印,绕过好看的打印格式;
Set whether to print values in raw form, bypassing any pretty-printers for that value

-repeats number-of-repeats|unlimited
Set threshold for repeated print elements. unlimited causes all elements to be individually printed.

-static-members [on|off] 设置是否打印c++的静态成员;
Set printing C++ static members.

-symbol [on|off] 打印指针时设置符号名打印;这个符号和地址关联,默认关闭;!!!这个挺好的,可以打开;
Set printing of symbol names when printing pointers

-union [on|off]
Set printing of unions interior to structures.

-vtbl [on|off]
Set printing of C++ virtual function tables.
因为print命令接受看起来像选项(包括缩写)的任意表达式,如果指定任何命令选项,则必须使用双破折号(——)来标记选项处理的结束。
如果单纯的p 会打印之前的值;
双破折号举例:注意空格:
(gdb) print -pretty -- *myptr
$1 = {
next = 0x0,
flags = {
sweet = 1,
sour = 1
},
meat = 0x54 "Pork"
}

print [option]
print [option] /f

1)如果体系结构支持内存标记,如果打印的是指针或引用类型,print命令将显示指针/内存标记不匹配。看到内存标签。
2)检查数据的一种更低级的方法是使用x命令。它检查内存中指定地址的数据,并以指定的格式打印。看到检查内存。https://sourceware.org/gdb/current/onlinedocs/gdb/Memory.html#Memory
3)如果您对类型信息或结构或类的字段如何声明感兴趣,请使用ptype exp命令而不是print命令。https://sourceware.org/gdb/current/onlinedocs/gdb/Symbols.html#Symbols
例如可以直接打印某个变量的类型结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(gdb) whatis var
type = complex_t
(gdb) ptype var
type = struct complex {
real_t real;
double imag;
}
再通过这个进一步打印:
(gdb) ptype /o struct complex
/* offset | size */ type = struct tyu {
/* 0:31 | 4 */ int a1 : 1;
/* 0:28 | 4 */ int a2 : 3;
/* 0: 5 | 4 */ int a3 : 23;
/* 3: 3 | 1 */ signed char a4 : 2;
/* XXX 3-bit hole */
/* XXX 4-byte hole */
/* 8 | 8 */ int64_t a5;
/* 16: 0 | 4 */ int a6 : 5;
/* 16: 5 | 8 */ int64_t a7 : 3;
/* XXX 7-byte padding */

或者可以用python的:我测试了下,效果不是很好,输入数字的时候卡住了;
(gdb) explore

表达式:

print和许多其他GDB命令接受一个表达式并计算它的值。
您所使用的编程语言定义的任何类型的常量、变量或运算符在GDB的表达式中都是有效的。
这包括条件表达式、函数调用、强制类型转换和字符串常量。它还包括预处理器宏,如果你编译你的程序包括这个信息
(gdb) p 123-3
120
(gdb) p func()

GDB支持用户输入的表达式中的数组常量。语法是{element, element…}。例如,可以使用命令print{1, 2, 3}创建一个由三个整数组成的数组。
如果将数组传递给函数或将其赋值给程序变量,GDB会将数组复制到目标程序中错误定位的内存中。
除了编程语言中常见的操作符外,GDB还支持这些操作符:

‘ @ ‘是一个二进制操作符,用于将部分内存作为数组处理。
::
‘:: ‘允许你在定义变量的文件或函数中指定变量,类似作用域
{type} addr
指向存储在内存地址addr的类型为类型的对象。地址addr可以是值为整数或指针的任何表达式(但是在二进制操作符周围需要圆括号,就像在强制转换中一样)。
这种构造是允许的,无论什么类型的数据通常应该驻留在addr。

模糊的表达式:

有些表达式有时候是模糊的,比如在c/c++中,一个函数名可能被定义几次在不同的上下文(作用域,命名空间或类等)。它们叫重载;其他语言也类似;
可以调整表达式来使其意义明确。例如,在c++中,你可以具体化函数的签名,如 break 中break function(type).第一个选项总是’ [0]cancel ‘,输入0 RET将终止当前命令。
如果使用表达式的命令允许选择多个选项,则菜单中的下一个选项是’ [1]all ‘,输入1 RET将选择所有可能的选项。

当检测到需要解决的歧义时,调试器能够为每种可能性显示一个有编号的选项菜单,然后使用提示符“>”等待选择。
例如,下面的会话摘录显示了在重载符号String::after上设置断点的尝试。我们选择函数名的三种特定定义:
(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)
这里String可能是一个类或作用域,而after是一个函数;
set multiple-symbols mode
此选项允许您在表达式不明确时调整调试器行为。缺省情况下,mode为all。如果使用表达式的命令允许多个选择,那么GDB会自动选择所有可能的选择。
例如,使用二义性名称在函数上插入断点将导致在每个可能的匹配上插入一个断点。
但是,如果必须做出唯一的选择,那么GDB将使用菜单来帮助您消除表达式的歧义。例如,打印重载函数的地址将导致使用菜单。

当mode设置为ask时,当检测到歧义时,调试器总是使用菜单。
最后,当mode设置为cancel时,调试器会报告一个由于歧义而导致的错误,命令会终止。

show multiple-symbols
Show the current value of the multiple-symbols setting.

程序的变量:

最常用的表达式类型是程序中变量的名称。表达式中的变量在选定的堆栈帧中被理解(参见选择帧);它们必须是:
global (or file-static)

根据编程语言的作用域规则可见,从该框架的执行角度来看

1
2
3
4
5
6
7
8
9
10
eg:
foo (a)
int a;
{
bar (a);
{
int b = test ();
bar (b);
}
}

当程序在函数foo中执行时,您可以检查和使用变量a,但只有当程序在声明b的块中执行时,您才能使用或检查变量b。
当然,有一种例外,就是你可以打印或引用一个变量或函数,它的作用域是一个文件的范围,即使当前的执行点不在这个文件;
当然,可能出现不同文件或函数有相同的变量名或函数名,这个时候你需要用::符号,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
file::variable
function::varibale
Here file or function is the name of the context for the static variable.
for example, to print a global value of x defined in f2.c:

(gdb) p 'f2.c'::x
::符号通常用于引用静态变量,因为通常通过选择适当的框架和使用变量的简单名称来消除函数中局部变量的使用的歧义。然而,你也可以使用这种表示法来引用包含选定帧的帧中的局部变量:
void
foo (int a)
{
if (a < 10)
bar (a);
else
process (a); /* Stop here */
}

int
bar (int a)
{
foo (a + 5);
}
For example, if there is a breakpoint at the commented line, here is what you might see when the program stops after executing the call bar(0):

(gdb) p a
$1 = 10
(gdb) p bar::a
$2 = 5
(gdb) up 2
#2 0x080483d0 in foo (a=5) at foobar.c:12
(gdb) p a
$3 = 5
(gdb) p bar::a
$4 = 0

‘:: ‘的这些用法很少与c++中非常相似的相同表示法的用法发生冲突。当它们发生冲突时,c++的含义优先;但是,可以通过用单引号引用文件或函数名来覆盖这一点。
例如,假设程序在一个具有includefile字段的类的方法中停止,并且还有一个名为includefile的包含文件,该文件定义了一个变量,some_global.
(gdb) p includefile
$1 = 23
(gdb) p includefile::some_global
A syntax error in expression, near `’.
(gdb) p ‘includefile’::some_global
$2 = 27
有时候并不能很顺利打印出来,比如:
编译器优化的另一个可能的效果是优化不存在的变量,或者将变量分配给寄存器(而不是内存地址)。
根据编译器使用的调试信息格式对这种情况的支持,GDB可能无法显示这些局部变量的值。如果发生这种情况,GDB将打印如下消息:No symbol “foo” in current context.
若出现这种情况,可以关闭编译优化,或者改变debug info format
如果你打印的类型是无法确认的,gdb会打印‘’,或者做下类型强制转换:
(gdb) p var
‘var’ has unknown type; cast it to its declared type
(gdb) p (float) var
$1 = 3.14

其他: @entry ?

伪造数组

在内存中连续输出几个相同类型的对象通常是有用的;数组的一段,或大小动态确定的数组,在程序中只有指针。

你可以通过使用二元运算符’ @ ‘引用一个连续的内存范围作为一个伪造数组来实现。’ @ ‘的左操作数应该是所需数组的第一个元素,并且是一个单独的对象。
正确的操作数应该是数组所需的长度。结果是一个数组值,其元素都是左参数的类型。第一个元素实际上是左参数;第二个元素来自存储第一个元素的字节之后的内存字节,依此类推。
下面是一个例子:
int *array = (int *) malloc (len * sizeof (int));
you can print the contents of array with

p *array@len

‘ @ ‘的左操作数必须驻留在内存中。以这种方式使用’ @ ‘生成的数组值在下标方面的行为与其他数组类似,并且在表达式中使用时被强制转换为指针。
伪造数组通常通过值历史(参见值历史)出现在表达式中,在输出一个值之后。

另一种创建伪造数组的方法是使用类型转换。这将重新解释一个值,就像它是一个数组。该值不需要在内存中:
(gdb) p/x (short[2])0x12345678
$1 = {0x1234, 0x5678}
为了方便起见,如果你将数组长度留出来(如’ (type[])value ‘), GDB会计算大小来填充值(如’ sizeof(value)/sizeof(type) ‘:
(gdb) p/x (short[])0x12345678
$2 = {0x1234, 0x5678}

有时,伪造数组的方式是不够的;在中等复杂的数据结构中,感兴趣的元素实际上可能不是邻接的—例如,如果您感兴趣的是数组中的指针值。在这种情况下,
一个有用的解决办法是在一个表达式中使用一个方便变量(请参阅便利变量)作为计数器,该表达式打印第一个感兴趣的值,然后通过RET重复该表达式。你感兴趣的是每个结构中字段fv的值。
set $i = 0
p dtab[$i++]->fv
RET
RET

输出的格式:进制

见上”#### 检查数据”
https://sourceware.org/gdb/current/onlinedocs/gdb/Output-Formats.html#Output-Formats

打印和检查内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
https://sourceware.org/gdb/current/onlinedocs/gdb/Memory.html#Memory
你可以使用指令 x来检查内存,并以各种格式打印出来,独立于你的程序数据类型;
语法:
x/nfu addr
x addr
x来检查内存,并以各种格式打印出来,独立于你的程序数据类型;
n f和u是所有的可选参数,用来具体化多少内存展示出来,和什么格式。 addr是一个表达式,给定起始内存地址。如果你是用默认的nfu,
你不需要打'/'.有几个命令为addr设置了方便的缺省值。
n, the repeat count是一个十进制整数;默认值是1。它指定显示多少内存(以单位u计数)。如果指定了一个负数,则从addr开始向后检查内存。
f, the display format 是是print (' x '' d '' u '' o '' t '' a '' c '' f '' s '),
' i '(用于机器指令)和' m '(用于显示内存标签)使用的格式之一。初始值是' x '(十六进制)。每次使用x或print时,默认值都会改变。

u,the unit size
即单位内存大小:
b
Bytes.

h
Halfwords (two bytes).

w
Words (four bytes). This is the initial default.

g
Giant words (eight bytes).

每次你用x指定一个单位大小,下一次你使用x时,这个大小就会成为默认的单位。对于' i '格式,单位大小会被忽略,通常不会被写入。对于' s '格式,除非明确给出,否则单位大小默认为' b '。使用x /hs显示16位字符串,
使用x /ws显示32位字符串。下一次使用x /s将再次显示8位字符串。注意,结果取决于当前编译单元的编程语言。如果语言是C, ' s '修饰符将使用UTF-16编码,而' w '将使用UTF-32

addr:starting display address
addr是你想要GDB开始显示内存的地址。表达式不需要有指针值(尽管可以);它总是被解释为内存的一个字节的整数地址。
例如,' x/3uh 0x54320 '是一个显示内存的三个半字(h)的请求,格式化为无符号十进制整数(' u '),从地址0x54320开始。' x/4xw $sp '打印堆栈指针上方的内存的四个单词(' w ')
您还可以指定一个负重复计数来从给定地址反向检查内存。例如,' x/-3uh 0x54320 '打印三个半字(h)在0x543140x543280x5431c

当检查机器指令时,当前程序计数器上的指令用=>标记显示。例如:
(gdb) x/5i $pc-6
0x804837f <main+11>: mov %esp,%ebp
0x8048381 <main+13>: push %ecx
0x8048382 <main+14>: sub $0x4,%esp
=> 0x8048385 <main+17>: movl $0x8048460,(%esp)
0x804838c <main+24>: call 0x80482d4 <puts@plt>
内存标签

不太理解,和架构支持有关,暂时不管;

自动显示

当你发现你想频繁的打印一个表达式时,你可以添加它到自动打印,gdb会自动打印它每当你的程序stop时,每个添加进自动打印的表达式列表,都有个数字标识它。若要移除
需要指定对应的数字:
这个列表看起来是这样的:
2: foo = 38
3: bar[5] = (struct hack *) 0x3804
此显示显示项目编号、表达式和它们的当前值。与使用x或print手动显示请求一样,
您可以指定您喜欢的输出格式;事实上,display决定是使用print还是x,这取决于你的格式规范——如果你指定了’ i ‘或’ s ‘格式或单位大小,它会使用x;否则使用print。
display expr 添加表达式,display does not repeat if you press RET again after using it.
display/fmt expr 上面的基础上指定格式
display/fmt addr 打印内存;
例如,’ display/i $pc ‘可能是有帮助的,可以在每次执行停止时看到将要执行的机器指令(‘ $pc ‘是程序计数器的通用名称;见寄存器)。
如何删除或暂停,使能
undisplay dnums…
delete display dnums…
disable display dnums…
enable display dnums…
display
显示列表中表达式的当前值,就像程序停止时所做的那样。
info display
打印先前设置为自动显示的表达式列表,每个表达式都带有项目编号,但不显示值。这包括被标记为禁用的表达式。它还包括一些表达式,这些表达式现在不会显示出来,因为它们引用了当前不可用的自动变量。

打印设置

见上print相关内容

漂亮的打印:

https://sourceware.org/gdb/current/onlinedocs/gdb/Pretty-Printing.html#Pretty-Printing

值的历史:

print命令打印的值保存在GDB的值历史中。这允许您在其他表达式中引用它们。值将一直保持,直到符号表被重新读取或丢弃(例如使用file或symbol-file命令)。
当符号表发生变化时,值的历史记录将被丢弃,因为值可能包含指向符号表中定义的类型的指针。

打印的值是给定的历史数字,您可以通过这些历史数字来引用它们。它们是从1开始的连续整数。Print通过在值之前打印’ $num = ‘来显示分配给某个值的历史编号;这里num是历史号码。

要引用之前的任何值,请使用’ $ ‘后跟该值的历史编号。print标签的输出就是为了提醒您这一点。
只是$指的是历史上最近的值,而$$指的是在那之前的值。$$n表示倒数第n个值;$$2是$$之前的值,$$1等价于$$,$$0等价于$。
例如,假设您刚刚打印了一个指向结构的指针,并希望查看该结构的内容。
p *$
如果你有一个结构链,其中组件的下一个指向下一个,你可以用这个打印下一个的内容:p *$.next
您可以通过重复这个命令来打印链中的连续链接——只需输入RET即可完成此操作。

注意,历史记录的是值,而不是表达式。如果x的值是4,你输入以下命令:
Print x
set x=5
那么即使x的值发生了变化,Print命令记录在值历史中的值仍然保持为4。
show values
打印值历史中的最后10个值及其项目编号。这就像’ p $$9 ‘重复了10次,除了显示的值没有改变历史。
show values n
以历史项目号n为中心打印10个历史值。
show values +
在上次打印的值之后打印10个历史值。如果没有更多可用的值,则show values +不产生任何显示。
按RET重复show value n的效果与’ show values + ‘完全相同。

gdb的变量

GDB提供了方便的变量,您可以在GDB中使用这些变量来保存一个值并在以后引用它。这些变量完全存在于GDB中;它们不是程序的一部分,设置方便变量对程序的进一步执行没有直接影响。
这就是为什么你可以自由地使用它们。

使用$为开头,不过要避免和内置的冲突:
set $foo = *object_ptr
将object_ptr指向的对象中包含的值保存在$foo中。
第一次使用方便变量创建它,但它的值是空的,直到您分配一个新值。您可以在任何时候使用另一个赋值来更改该值。
gdb变量没有固定的类型。您可以为方便变量指定任何类型的值,包括结构和数组,即使该变量已经具有不同类型的值。方便变量在用作表达式时,具有其当前值的类型。

show convenience
打印到目前为止使用的便利变量列表及其值,以及便利函数列表。缩写展示conv。

使用方便变量的一种方法是作为递增的计数器或前进的指针。例如,从结构数组的连续元素中打印字段:
set $i = 0
print bar[$i++]->contents
还有一些自动变量,类似makefile;
如: $_thread
当前线程的线程号。
更多见手册;https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Vars.html#Convenience-Vars

gdb除了提供方便变量,还提供了函数;

https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Funs.html#Convenience-Funs

gdb中的寄存器使用
gdb中内存区域的属性
如何dump指定内存区域内容到文件,或反向,即文件中读取到内存;

dump指令

如何产生一份core文件:

有时候需要一份程序执行的内存快照:
generate-core-file [file]
(gdb) gcore [file]

通过find指令在指定内存中查找字符或其他值

https://sourceware.org/gdb/current/onlinedocs/gdb/Searching-Memory.html#Searching-Memory

调试优化后的代码 –

c预处理宏 –

追踪点,无干扰的调试

在某些应用程序中,调试器中断程序执行的时间不够长,以至于开发人员无法了解有关程序行为的任何有用信息。
如果程序的正确性取决于它的实时行为,那么调试器引入的延迟可能会导致程序彻底改变其行为,甚至可能会失败,即使代码本身是正确的。能够在不中断的情况下观察程序的行为是很有用的。
在服务器或网络程序中的调试经常遇到;

使用GDB的跟踪和收集命令,您可以在程序中指定位置、调用跟踪点以及在到达这些跟踪点时计算的任意表达式。稍后,使用tfind命令,您可以检查程序到达跟踪点时那些表达式的值。
表达式还可以表示内存结构或数组中的对象,例如GDB应该记录哪些对象的值;当访问一个特定的跟踪点时,您可以检查那些对象,就像它们当时在内存中一样。
由于GDB不需要与您交互就可以记录这些值,所以它可以快速而不引人注目地记录这些值,希望不会干扰程序的行为。

使用这种方式,需要gdb以类似attach的方式,这里是用的remote方式:
举例一个进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
test.c:编译后为test gcc -g test.c -o test 
先启动进程:进程会进入等待:
gdbserver attach进程:
gdbserver --attach 127.0.0.1:1234 `pidof test` /直接填进程号
另一个窗口使用:
gdb --command=command
command文件示例:
file test
target remote 127.0.0.1:1234
trace test.c:15
actions
collect vara
end
tstart
break test.c:14
bt
continue
tstop
tstatus
tfind start
while($trace_frame != -1)
printf "Frame %d : indx = %d\n", $trace_frame, indx
tfind
end

https://developer.apple.com/library/archive/documentation/DeveloperTools/gdb/gdb/gdb_11.html

调试使用覆盖的程序 –暂时不用

如果您的程序太大,无法完全装入目标系统的内存,有时可以使用覆盖来解决这个问题。GDB为调试使用覆盖的程序提供了一些支持。

用在不同的语言上 –

测试符号表

支持打印变量,打印变量的类型,打印类中的成员和函数,打印变量所在地址等;
https://sourceware.org/gdb/current/onlinedocs/gdb/Symbols.html#Symbols

修改可执行程序

一旦您认为在程序中发现了错误,您可能希望确定纠正明显的错误是否会导致在其余的运行中得到正确的结果。您可以通过实验找到答案,使用GDB特性更改程序的执行。
例如,您可以将新值存储到变量或内存位置,给您的程序一个信号,在不同的地址重新启动它,甚至从函数提前返回。

• Assignment: Assignment to variables
• Jumping: Continuing at a different address
• Signaling: Giving your program a signal
• Returning: Returning from a function
• Calling: Calling your program’s functions
• Patching: Patching your program
• Compiling and Injecting Code: Compiling and injecting code in GDB

gdb和相关文件

GDB需要知道要调试的程序的文件名,这既是为了读取它的符号表,也是为了启动程序。要调试上一次运行的核心转储,还必须告诉GDB核心转储文件的名称。

• Files: Commands to specify files
您可能需要指定可执行文件和核心转储文件的名称。通常的方法是在启动时使用GDB的启动命令的参数,或者在启动gdb后,再进行指定;
在GDB会话期间,偶尔需要更改到不同的文件。或者您可能在运行GDB时忘记指定要使用的文件。或者您正在通过gdbserver调试远程目标(请参阅使用gdbserver程序)。在这些情况下,指定新文件的GDB命令很有用。

file filename:
使用文件名作为要调试的程序。读它是为了它的符号和纯粹记忆的内容。它也是使用run命令时执行的程序。如果没有指定目录,并且在GDB工作目录中没有找到该文件,
那么GDB将使用环境变量PATH作为要搜索的目录列表,就像shell在寻找要运行的程序时所做的那样。您可以使用path命令为GDB和您的程序更改这个变量的值。

可以使用file命令将未链接的对象.o文件加载到GDB中。你将不能“运行”一个目标文件,但你可以反汇编函数和检查变量。此外,如果底层的BFD功能支持它,
您可以使用gdb -write使用这种技术来修补对象文件。
注意,在这种情况下,GDB既不能解释也不能修改重定位,因此分支和一些初始化的变量似乎会移到错误的位置。但是这个功能还是很方便的。

file
file with no argument makes GDB discard any information it has on both executable file and the symbol table.

exec-file [ filename ]
指定在文件名中找到要运行的程序(而不是符号表)。如果需要的话,GDB会搜索环境变量PATH来定位您的程序。省略文件名意味着丢弃可执行文件上的信息。

symbol-file [ filename [ -o offset ]]
从文件文件名读取符号表信息。必要时搜索PATH。使用file命令从同一个文件获取符号表和要运行的程序。
如果指定了可选的偏移量,它将被添加到符号文件中每个部分的起始地址中。如果程序在运行时被重新定位,例如启用了kASLR的Linux内核,这将非常有用。

core-file [filename]
core
指定要用作“内存内容”的核心转储文件的下落。传统上,核心文件只包含生成它们的进程的地址空间的一部分;GDB可以为其他部分访问可执行文件本身。
core-file with no argument specifies that no core file is to be used.
注意,当您的程序在GDB下实际运行时,核心文件将被忽略。因此,如果您一直在运行您的程序,而您希望调试核心文件,则必须杀死程序正在其中运行的子进程。为此,可以使用kill命令(参见杀死子进程)。

其他:
add-symbol-file filename [ -readnow | -readnever ] [ -o offset ] [ textaddress ] [ -s section address … ]
remove-symbol-file filename
remove-symbol-file -a address

info files
info target
两个都是打印当前的目标;

• File Caching: Information about GDB’s file caching
为了加快文件加载速度,减少内存占用,GDB会重用用于跟踪打开文件的bfd对象。请参见二进制文件描述符库中的BFD。下面的命令允许缓存行为的可见性和控制。
maint info bfds
输出GDB已知的每个bfd对象的信息
maint set bfd-sharing
maint show bfd-sharing
控制是否可以共享bfd对象。当启用共享时,GDB将重用已经打开的bfd对象,而不是重新打开相同的文件。关闭共享不会导致已经共享的bfd对象被取消共享,
但所有未来打开的文件将创建一个新的bfd对象。同样,重新启用共享功能不会导致多个现有的bfd对象坍缩为一个共享的bfd对象。
set debug bfd-cache level
Turns on debugging of the bfd cache, setting the level to level.

show debug bfd-cache
Show the current debugging level of the bfd cache.
• Separate Debug Files: Debugging information in separate files
GDB允许您将程序的调试信息放在与可执行文件本身分离的文件中,以一种允许GDB自动查找和加载调试信息的方式。
由于调试信息可能非常大——有时比可执行代码本身还要大——一些系统将可执行文件的调试信息分发到单独的文件中,用户只能在需要调试问题时安装这些文件。
https://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html#Separate-Debug-Files
• MiniDebugInfo: Debugging information in a special section
一些系统提供预构建的可执行文件和具有特殊’的库。gnu_debugdata”部分。这个特性被称为MiniDebugInfo。这个部分保存一个lzma压缩的对象,用于为回溯提供额外的符号。

• Index Files: Index files speed up GDB
当GDB发现一个符号文件时,它会扫描文件中的符号,以构造一个内部符号表。这使得大多数GDB操作能够快速工作—但要以早期的延迟为代价。对于大型程序,这种延迟可能相当长,因此GDB提供了一种构建索引的方法,这可以加快启动速度。

• Symbol Errors: Errors reading symbol files
在读取符号文件时,GDB偶尔会遇到一些问题,比如它不能识别的符号类型,或者编译器输出中的已知错误。默认情况下,GDB不会通知您此类问题,
因为它们是相对常见的,主要是调试编译器的人感兴趣的问题。如果您对查看有关构造不良的符号表的信息感兴趣,您可以要求GDB对每种类型的问题只打印一条消息,
不管问题发生了多少次;或者您可以使用set compl命令请求GDB打印更多消息,以查看问题发生的次数
• Data Files: GDB data files
GDB有时会读取辅助数据文件。这些文件保存在一个称为数据目录的目录中。
您可以设置数据目录的名称,并查看GDB当前使用的名称。

具体化一个调试目标

目标是程序所占用的执行环境。通常,GDB运行在与程序相同的主机环境中;在这种情况下,当您使用file或core命令时,调试目标被指定为副作用。
当你需要更多的灵活性,在物理上独立的主机上运行GDB或控制一个独立的系统通过串口或实时系统在一个TCP / IP连接可以使用目标命令来指定一个目标类型配置为GDB(见命令来管理目标)。

调试远程程序

配置具体的信息 — 和体系结构相关,暂时不看

尽管几乎所有的GDB命令都可以用于所有本机和跨版本的调试器,但也有一些例外。本章描述仅在某些配置中可用的东西。
有三种主要的配置类型:本机配置,其中主机和目标是相同的;嵌入式操作系统配置,这对于几个不同的处理器体系结构通常是相同的;裸嵌入式处理器,它们彼此之间差别很大。

控制gdb

扩展GDB

GDB提供了几种扩展机制。GDB还提供了在读取文件进行调试时自动加载扩展名的能力。这允许用户为正在调试的程序自动定制GDB。
为了方便使用扩展语言,GDB能够评估文件的内容。这样做时,GDB可以通过查看文件名扩展名来识别正在使用的扩展语言。带有无法识别的文件名扩展名的文件总是被视为GDB命令文件。

命令解释器

gdb用户界面接口 TUI

​TUI(TextUser Interface)为GDB调试的文本用户界面,可以方便地显示源代码、汇编和寄存器文本窗口,支持类似ide中的源码级单步调试;

基本介绍

在TUI模式中,可以显示以下几个窗口:
命令窗口:用于 GDB调试时的命令输入和命令结果输出显示,与普通 GDB窗口无异。
源代码窗口:用于显示程序源代码,包括当前运行行、中断以中断标识等。
汇编窗口:显示当前程序的汇编代码。
寄存器窗口:显示处理器的寄存器内容,当寄存器内容发生改变时会高亮显示。
源代码窗口和汇编窗口会高亮显示程序运行位置并以’>’符号标记。有两个特殊标记用于标识断点,第一个标记用于标识断点类型:
B
程序至少有一次运行到了该断点
b
程序没有运行到过该断点
H
程序至少有一次运行到了该硬件断点
h
程序没有运行到过该硬件断点
第二个标记用于标识断点使能与否:
+
断点使能 Breakpointis enabled.
-
断点被禁用 Breakpointis disabled.
当调试程序时,源代码窗口、汇编窗口和寄存器窗口的内容会自动更新。
在命令窗口上方有一行状态栏,显示效果如下图所示,主要显示内容有:目标,函数,进程,行号,pc指针等

TUI快捷键:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
C-x C-a
C-x a
C-x A
Enter or leave the TUI mode.
C-x 1
Use a TUI layout with only one window
C-x 2
Use a TUI layout with at least two windows.
C-x o
Change the active window.
C-x s
Switch in and out of the TUI SingleKey mode
在TUI模式下:
The following key bindings only work in the TUI mode:

PgUp
Scroll the active window one page up.

PgDn
Scroll the active window one page down.

Up
Scroll the active window one line up.

Down
Scroll the active window one line down.

Left
Scroll the active window one column left.

Right
Scroll the active window one column right.

C-L
Refresh the screen.

##### tui 的single key模式
tui也提供single key模式,这个模式一个字母绑定一个指令,type C-x s进入退出这个模式;
c
continue

d
down

f
finish

n
next

o
nexti. The shortcut letter ‘o’ stands for “step Over”.

q
exit the SingleKey mode.

r
run

s
step

i
stepi. The shortcut letter ‘i’ stands for “step Into”.

u
up

v
info locals

w
where
tui鼠标支持:

If the curses library supports the mouse, the TUI supports mouse actions.

The mouse wheel scrolls the appropriate window under the mouse cursor.

The TUI itself does not directly support copying/pasting with the mouse. However, on Unix terminals, you can typically press and
hold the SHIFT key on your keyboard to temporarily bypass GDB’s TUI and access the terminal’s native mouse copy/paste functionality
(commonly, click-drag-release or double-click to select text, middle-click to paste).
This copy/paste works with the terminal’s selection buffer, as opposed to the TUI’s buffer.

tui的具体指令:

(gdb)tui enable

https://sourceware.org/gdb/onlinedocs/gdb/TUI-Commands.html#TUI-Commands

tui的可变配置:

https://sourceware.org/gdb/onlinedocs/gdb/TUI-Configuration.html#TUI-Configuration

GDB/MI 接口 –

GDB/MI是一个基于行的面向GDB的机器文本接口,通过指定使用——interpreter命令行选项来激活(参见模式选项)。它专门用于支持将调试器作为更大系统的一个小组件使用的系统的开发。

gdb注释 –

本章描述GDB中的注释。注解的设计目的是将GDB与图形用户界面或其他希望在相对较高的层次上与GDB交互的类似程序连接起来。注释机制在很大程度上已经被GDB/MI所取代

JIT编译接口 –

本章描述了GDB的JIT (just-in-time)编译接口。JIT编译器是在运行时生成本机可执行代码并执行它的程序或库,通常是为了在保持平台独立性的同时获得良好的性能。–java?

进程代理

传统的调试模型在概念上是低速的,但工作得很好,因为大多数错误都可以在调试模式执行中复制。然而,随着多核或多核处理器成为主流,多线程程序越来越流行,
应该会有越来越多的错误只在正常模式执行时出现,例如线程竞争,因为调试器对程序时间的干扰可能会隐藏这些错误。另一方面,在某些应用程序中,调试器中断程序执行的时间不够长,
以至于开发人员无法了解到有关程序行为的任何有用信息,
这是不可行的。如果程序的正确性取决于它的实时行为,那么调试器引入的延迟可能会导致程序失败,即使代码本身是正确的。能够在不中断的情况下观察程序的行为是很有用的。

因此,传统的调试模型干扰太大,无法重现一些bug。为了减少对程序的干扰,我们可以减少调试器执行的操作次数。进程内代理(In-Process Agent)是一个共享库,它运行在同一个进程中,
并且可以自己执行一些调试操作。因此,只在必要时使用调试器,从而提高调试性能。请注意,可以减少对程序的干扰,但不能完全消除,因为进程内代理仍然会停止或减慢程序。

进程内代理可以在执行调试操作时解释并执行agent表达式(参见agent表达式)。代理表达式可以用于不同的目的,例如在跟踪点中收集数据,以及在断点中计算条件。

可以使用以下命令控制进程内代理是否用于辅助调试:
set agent on
设置代理
使进程内代理代表调试器执行某些操作。用户所请求的操作将由进程内代理执行,这取决于它的能力。
例如,如果您请求在进程内代理中评估断点条件,并且进程内代理也具有这样的功能,那么将在进程内代理中评估断点条件。

set agent off
Disables execution of debugging operations by the in-process agent. All of the operations will be performed by GDB.

show agent
Display the current setting of execution of debugging operations by the in-process agent.

交互式使用历史记录

命令行交互指令

如何保存历史指令和加载历史指令;

常规的如果在一个session中,通过上下箭头键可以找到历史指令,但是退出就没了,如何使用上个session的指令?
echo ‘set history save on’ >> ~/.gdbinit && chmod 600 ~/.gdbinit
set history size 1000
set history remove-duplicates //删除重复指令;
set history filename ~/.gdb_history
这样就可以了;