gdb
—— 开始调试程序;set args
—— 设置程序需要的参数;break
—— 设置断点;run
—— 开始调试程序;next
—— 执行下一步;quit
—— 退出调试;info
: 查看局部变量的值examine (x) addr
x/nfu addr : 查看内存地址
x /10wx addr:查看10个4字节,按16进制输出
x /10i addr :查看改地址的10条指令
输出格式:
info break,也可简写为 i b
,作用是显示当前所有断点信息;
disable [breakpoints] [list…] / disable 断点编号
禁用指定的断点或所有断点(如果未列出)。 禁用的断点没有任何作用,但不会被忘记。 所有选项(例如忽略计数、条件和命令)都会被记住,以防稍后再次启用断点。 可以将disable缩写为dis。
enable [breakpoints] [list…] / enable 断点编号
启用指定的断点(或所有定义的断点)。 它们再次有效地阻止您的程序。
enable [breakpoints] once list…
暂时启用指定的断点。 gdb 在停止程序后立即禁用任何这些断点。
enable [breakpoints] count count list…
暂时启用指定的断点。 gdb 记录每个指定断点的计数,并在命中断点时减少断点的计数。
当任意计数达到 0 时,gdb 会禁用该断点。 如果断点有忽略计数,则在计数受到影响之前,该计数将递减至 0。
enable [breakpoints] delete list…
使指定的断点工作一次,然后就死掉。 一旦你的程序停止在那里,gdb 就会删除任何这些断点。 tbreak 命令设置的断点在此状态下开始。
delete 断点编号,删除某个断点。
回溯(backtrace)是程序如何到达当前位置的摘要。 它每帧显示一行,对于许多帧,从当前正在执行的帧(零帧)开始,然后是其
调用者(第一帧),并在堆栈上。
要打印整个堆栈的回溯,请使用 backtrace 命令或其别名 bt。
此命令将为堆栈中的帧每帧打印一行。 默认情况下,所有堆栈框架被打印。
可以随时通过输入系统中断来停止回溯字符,通常是 Ctrl-c。
backtrace [option]... [qualifier]... [count]
bt [option]... [qualifier]... [count]
可选计数可以是以下之一:
n
仅打印最里面的 n 帧,其中 n 是正数。
-n
只打印最外面的n帧,其中n是正数。
选项(option):
backtrace 命令还支持许多选项,这些选项允许覆盖由 set backtrace 和 set print 设置的相关全局打印设置
子命令:
-past-main [on|off] :设置回溯是否应继续经过 main
-past-entry [on|off] :设置回溯是否应继续经过入口点程序。
-entry-values no|only|preferred|if-needed|both|compact|default :在函数入口处设置函数参数的打印。
-frame-arguments all|scalars|none :设置非标量框架参数的打印
-raw-frame-arguments [on|off] :设置是否以原始形式打印帧参数
-frame-info auto|source-line|location|source-andlocation|location-and-address|short-location :设置打印帧信息
保留可选限定符是为了向后兼容。 它可以是一个以下的:
大多数用于检查程序中堆栈和其他数据的命令都适用于当前选择的堆栈帧。 以下是选择堆栈帧的命令;
所有这些都通过打印刚刚选择的堆栈框架的简短描述来完成。
frame [ frame-selection-spec ]
f [ frame-selection-spec ]
帧命令允许选择不同的堆栈帧。 frameselection-spec
可以是以下任何一个
(gdb) frame 3
(gdb) frame level 3
(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
该帧的堆栈地址是 0x7fffffffda30,如以下行所示:
Stack level 1, frame at 0x7fffffffda30:
function function-name
选择函数 function-name 的堆栈帧。 如果函数 function-name 有多个堆栈帧,则选择最里面的堆栈帧。查看堆栈地址 [ pc-addr ]
view stack-address [ pc-addr ]
查看不属于 gdb 回溯的帧。 所查看的帧具有堆栈地址 stack-addr,以及可选的程序计数器地址 pc-addr。
这主要在堆栈帧的链接被错误损坏、导致 gdb 无法为所有帧正确分配编号的情况下有用。 此外,当您的程序具有多个堆栈并在它们之间切换时,这会很有用。
当使用帧视图查看当前回溯之外的帧时,您始终可以使用先前的堆栈帧选择指令之一返回到原始堆栈,例如帧级别 0。
up n
在堆栈中向上移动 n 个帧; n 默认为 1。对于正数 n,这将前进到最外层的帧、更高的帧编号、存在时间更长的帧。
down n
在堆栈中向下移动 n 个帧; n 默认为 1。对于正数 n,这将向最里面的帧前进,到较低的帧编号,到最近创建的帧。 可以像这样做一样缩写。
所有这些命令都以打印描述该帧的两行输出结束。 第一行显示帧号、函数名称、参数以及该帧中执行的源文件和行号。 第二行显示该源行的文本。
例如:
(gdb) up
#1 0x22f0 in main (argc=1, argv=0xf7fffbf4, env=0xf7fffbfc)
at env.c:10
10 read_input_file (argv[i]);
在这样的打印输出之后,不带参数的 list 命令打印以帧中执行点为中心的十行。 还可以通过键入 edit 使用最喜欢的编辑程序在执行时编辑程序。
select-frame [ frame-selection-spec ]
select-frame 命令是frame 的一个变体,它在选择新的frame 后不显示新的frame。 该命令主要用于 gdb 其中的输出可能是不必要的并且会分散注意力
up-silently n
down-silently n
这两个命令分别是 up 和 down 的变体; 它们的不同之处在于它们默默地完成工作,不会导致新帧的显示。 它们主要用于 gdb 命令脚本,其中的输出可能是不必要的并且会分散注意力
info frame
info f
此命令打印所选堆栈帧的详细描述,包括:
当出现问题导致堆栈格式不符合通常的约定时,详细描述非常有用。
要打印源文件中的行,请使用 list 命令(缩写为 l)。
默认情况下,打印十行。
有多种方法可以指定要打印文件的哪一部分;
命令格式及作用:
list命令默认只会输出 10 行源代码 : 也可以使用如下命令修改
print expr : 计算表达式 expr 并显示结果值。 该表达式可以包括对正在调试的程序中的函数的调用。
print
print
print a+b+c : 可以进行一定的表达式计算 : 这里是计算a、b、c三个变量之和
print func() : 输出func函数执行的结果 : 常见的用途是打印系统函数执行失败原因:print strerror(errno)
*print this : 在c++对象中 : 可以输出当前对象的各成员变量的值
命令格式及功能:
命令格式及作用:
next 和 step 都是单步执行,但也有差别:
return 和 finish 都是退出函数,但也有差别:
Until 执行直到程序到达大于当前帧或当前帧内指定位置(与break 命令相同的参数)的源代码行。
执行到指定位置停下来,命令参数和 break 命令一样
有2点注意的:
该命令用于查看某段代码的汇编指令。
很多程序启动需要我们传递参数,set args 就是用来设置程序启动参数的,show args 命令用来查询通过 set args 设置的参数
设置仅针对一次停止启用的断点。 参数与break命令相同,断点设置方式相同,但程序第一次停止后断点会自动删除。
只要表达式的值发生变化,就可以使用观察点来停止执行,而无需预测可能发生这种情况的特定位置。 (这有时称为数据断点)
表达式可以像单个变量的值一样简单,也可以像通过运算符组合的多个变量一样复杂。
示例包括:
• 对单个变量值的引用。
• 地址转换为适当的数据类型。 例如,“*(int )0x12345678
”将监视指定地址处的 4 字节区域(假设 int 占用 4 字节)。
• 任意复杂的表达式,例如“ab + c/d
”。 该表达式可以使用程序本机语言中有效的任何运算符。
watch 命令用来监视一个变量或者一段内存,当这个变量或者内存的值发生变化时,GDB就会中断下来。被监视的某个变量或内存地址会产生一个 watch point(观察点)。
通过 info watch 命令可以查看当前所有监视的变量,通过 delete watch编号 可以删除对某个变量的监视。yo
在某些操作系统中,例如 GNU/Linux 和 Solaris,单个程序可能有多个执行线程。
线程的精确语义因操作系统而异,但一般来说,单个程序的线程类似于多个进程,只是它们共享一个地址空间(即它们都可以检查和修改相同的变量)。
另一方面,每个线程都有自己的寄存器和执行堆栈,也许还有私有内存。
多线程程序的编写更容易产生异常或 Bug,例如线程之间因竞争同一资源发生了死锁、多个线程同时对同一资源进行读和写等等。
使用GDB调试多线程,需要监控多个线程的执行过程。
用GDB调试多线程程序时,编译需要添加 -lpthread
参数。
gdb 线程调试工具允许在程序运行时观察所有线程,但每当 gdb 控制时,一个特定的线程始终是调试的焦点。 该线程称为当前线程。 调试命令从当前线程的角度显示程序信息。
每当 gdb 在程序中检测到新线程时,它都会显示该线程的目标系统标识,并显示一条格式为“[New systag]”的消息,其中 systag 是一个线程标识符,其形式根据特定系统而有所不同。 例如,在 gnu/Linux 上,当 gdb 注意到新线程时,您可能会看到
[New Thread 0x41e02940 (LWP 25582)]
。
相比之下,在其他系统上,系统标记只是类似于“process 368”,没有进一步的限定符。
出于调试目的,gdb 将其自己的线程号(始终是单个整数)与下级( inferior )线程的每个线程相关联。 该数字在下级的所有线程之间是唯一的,但在不同下级的线程之间不是唯一的。
可以使用限定的inferior-num.thread-num
语法(也称为限定的线程ID)引用下级中的给定线程,其中inferior-num是下级编号,thread-num是给定下级的线程编号。 例如,线程 2.3 指的是下级线程 2 的线程号 3。如果省略了下级线程号(例如线程 3),则 gdb 会推断您指的是当前下级线程。
在创建第二个下级之前,gdb 不会显示线程 ID 的下级编号部分,即使始终可以使用完整的下级编号.线程编号形式来引用下级 1(初始下级)的线程。
某些命令接受空格分隔的线程 ID 列表作为参数。 列表元素可以是:
下级的所有线程,用星号通配符指定,带或不带下级限定符,如 inf.*(
例如“1.*
”)或 *
。 前者是指给定的下级的所有线程,而后者不带下级限定符的形式是指当前下级的所有线程。*
例如,当前inferior为1,inferior 7有1个ID为7.1的线程,则线程列表’1 2-3 4.5 6.7-9 7.*'包含inferior 1的线程1到3,inferior 4的线程5 、inferior 6 的线程 7 到 9 以及inferior 7 的所有线程。即,在扩展限定形式中,与“1.1 1.2 1.3 4.5 6.7 6.8 6.9 7.1”相同。
除了每个下级编号之外,每个线程还分配有一个唯一的全局编号,也称为全局线程 ID,是一个整数。 与线程 ID 的线程号组成部分不同,即使您正在调试多个下级线程,也没有两个线程具有相同的全局 ID。
从 gdb 的角度来看,一个进程总是至少有一个线程。 换句话说,即使程序不是多线程的,gdb 也会为程序的“主线程”分配一个线程号。
新线程的自动通知
thread thread-id:在线程之间切换的命令,切换为 编号为thread-id的线程
info thread: 查询现有所有线程的命令,打印所有线程的信息
thread apply [thread-id-list | all] args:将命令应用于线程列表的命令
break location thread 线程编号:在 location 位置设置普通断点,该断点只作用在特定编号的线程上;
线程特定的断点
set print thread-events:控制线程启动和退出时消息的打印
set libthread-db-search-path path: 如果默认选择与程序不兼容,用户可以指定要使用的 libthread_db
gdb 支持调试具有多个线程的程序。
有两种模式可以在调试器中控制程序的执行。
在默认模式(称为全停止模式 All-Stop Mode)下,当程序中的任何线程停止时(例如,在断点处或单步执行时),程序中的所有其他线程也会被 gdb 停止。
在某些目标上,gdb 还支持不间断模式(Non-Stop Mode),在该模式下,当调试器中检查停止的线程时,其他线程可以继续自由运行。
使用GDB调试多线程程序时,默认的调试模式是:一个线程暂停运行,其他线程也随即暂停;一个线程启动运行,其他线程也随即启动。但在一些场景中,我们希望只让特定线程运行,其他线程都维持在暂停状态,即要防止线程切换,要达到这种效果,需要借助 set scheduler-locking 命令。
set scheduler-locking mode : 设置调度程序锁定模式。 它适用于正常执行、记录模式和重放模式。
set scheduler-locking off : 没有锁定,任何线程都可以随时运行
set scheduler-locking on : 当下级线程恢复时,只有当前线程可以运行
set scheduler-locking step :
当step时,其行为类似于当前线程打开,其他线程关闭。 当单步执行时,当前线程以外的线程永远没有机会运行,而当使用“continue”、“until”或“finish”等命令时,它们可以完全自由地运行。 该模式针对单步进行了优化; 它可以防止其他线程在单步执行时抢占当前线程,这样调试的焦点就不会意外改变。 但是,除非另一个线程在其时间片内遇到断点,否则 gdb 不会将当前线程更改为远离正在调试的线程。
set scheduler-locking step replay: 其行为类似于重播模式下的打开状态,以及记录模式或正常执行期间的关闭状态。 这是默认模式。
show scheduler-locking : 查看线程锁定状态;
默认情况下,当发出诸如 continue、next 或 step 等执行命令之一时,gdb 仅允许当前下级线程运行。
例如,如果 gdb 附加到两个下级线程,每个下级线程有两个线程,则 continue 命令仅恢复当前下级线程的两个线程。
例如,当调试 fork 出的程序并且您希望在调试子程序时保持父程序停止(例如,这样它就不会运行退出)时,这很有用。
在其他情况下,你可能对检查 gdb 附加到的任何进程的当前状态不感兴趣,并且你可能希望恢复所有进程,直到遇到某个断点。 在后一种情况下,可以使用 set Schedule-multiple
命令指示 gdb 允许所有下级的所有线程运行。
set schedule-multiple
设置发出执行命令时允许恢复多个进程的线程的模式。 打开时,允许所有进程的所有线程运行。 关闭时,仅恢复当前进程的线程。 默认关闭。 当设置为打开时,或者当您单步执行并设置为单步执行时,调度程序锁定模式优先。
show schedule-multiple
显示当前多进程线程恢复执行的模式。
对于某些多线程目标,gdb 支持一种可选的操作模式,可以在调试器中检查已停止的程序线程,而其他线程继续自由执行。
这可以最大限度地减少调试实时系统时的入侵,例如某些线程具有实时限制或必须继续响应外部事件的程序。 这称为不间断模式(Non-Stop Mode)。
在不间断模式下,当一个线程停止报告调试事件时,只有该线程停止; 与全停止模式行为相反,gdb 也不会停止其他线程。
此外,诸如 continue 和 step 之类的执行命令默认仅适用于不间断模式下的当前线程,而不是像全停止模式下那样适用于所有线程。
这允许你可以在全停止模式下不可能的方式显式控制线程 - 例如,单步执行一个线程,同时允许其他线程自由运行,单步执行一个线程,同时保持所有其他线程停止,或者独立且同时单步执行多个线程。
要进入不间断模式,请在运行或附加到程序之前使用以下命令序列:
# If using the CLI, pagination breaks non-stop.
set pagination off
# Finally, turn it on!
set non-stop on
可以使用这些命令来操作不间断模式设置: