你背朝太阳,就只能看到自己的影子。 --纪伯伦语录
文章目录
- 简介
- 准备
- 常用命令
- 查看代码(list)
- 运行(run)
- 打断点(break)
- 逐语句(step)
- 逐过程(next)
- 完成当前函数(finish)
- 打印变量值(print)
- 设置变量(set)
- 跳到下一断点(continue)
- 删除断点(delete)
- 禁用断点(disable)
- 启用断点(enable)
- 循环打印(display)
- 取消循环打印(undisplay)
- 运行到指定行(until)
- 查看调用栈(backtrace)
- 设置观察点
- 退出(quit)
- 总结
简介
gdb
是 GNU Debugger 的缩写,是一款由 GNU 项目开发的、用于调试程序的强大工具。它允许开发者在程序执行过程中观察程序的行为,检查变量的值,跟踪函数调用,定位程序崩溃的原因等。gdb
支持多种编程语言,包括 C、C++、Fortran 等。
主要特点和功能包括:
-
断点设置: 允许在程序中的特定位置暂停执行,以便进行调试。
-
变量查看: 可以查看程序运行时的变量值,包括局部变量和全局变量。
-
函数调用跟踪: 可以跟踪程序的函数调用链,了解程序的执行流程。
-
内存查看: 允许查看和修改程序运行时的内存内容。
-
寄存器查看: 提供对计算机寄存器状态的访问,可用于了解程序的底层执行情况。
-
多线程调试: 支持多线程程序的调试。
-
远程调试: 可以通过网络连接调试运行在远程机器上的程序。
-
脚本支持: 允许使用脚本语言编写调试脚本,以自动执行调试任务。
gdb
在开发者社区中广泛使用,是许多开发环境中集成的调试工具之一。它为开发者提供了强大的调试能力,有助于快速诊断和解决程序中的问题。掌握gdb调试是一名成熟的程序员必不可少的技能。
准备
在开发过程中,通常会有两种构建模式:Debug 模式和 Release 模式。在 Debug 模式下,编译器会生成包含调试信息的可执行文件。调试信息包括源代码的行号、变量名称等,这使得在调试器中能够更容易地跟踪和调试程序。编译时通常会使用 -g
选项,以包含调试信息。这样的二进制文件较大,但对于开发和调试来说非常有用。在 Release 模式下,编译器会进行优化以提高程序的性能,并且通常不包含调试信息。生成的二进制文件更加紧凑,适合用于最终部署和生产环境。
在使用 gdb
进行调试时,确保在编译源代码时使用了 -g
选项,以便生成包含调试信息的可执行文件。例如,在使用 gcc
编译 C 代码时,可以这样:
gcc -g -o my_program my_program.c
其中,-g
选项告诉编译器生成调试信息,而 -o
选项用于指定输出文件的名称。这样生成的 my_program
文件就包含了调试信息,可以在 gdb
中进行调试。
之后就可以进行调试了,使用gdb
命令加生成的可执行文件,例如gdb my_program
。
常用命令
查看代码(list)
在 gdb
中,list
命令用于显示源代码。当没有参数时,它将显示当前执行位置周围的源代码,也可以简写为l
。下面是一些 list
命令的常用形式:
-
显示当前位置周围的源代码:
list
-
显示特定行附近的源代码:
list line_number
-
显示指定文件的源代码:
list file_name:line_number
-
显示函数的源代码:
list function_name
-
显示指定范围内的源代码:
list start_line, end_line
在使用 list
命令时,可以根据需要指定不同的参数,以便在调试过程中更好地查看和理解源代码,对于定位问题、设置断点和了解程序的执行流程非常有用。
运行(run)
run
命令用于启动被调试的程序。当你在 gdb
中加载了可执行文件后,可以使用 run
命令来开始执行该程序,也可以使用简写r
。如果程序需要输入参数,你也可以在 run
命令后面添加参数。
使用 run
命令的基本语法如下:
run [arguments]
例如,如果你的程序需要两个整数参数,你可以这样运行:
run 10 20
这将启动程序,并将参数 10
和 20
传递给它。
在执行 run
命令后,程序将开始运行,直到遇到断点、程序结束或者由用户中断。在这个过程中,你可以使用 gdb
提供的其他命令进行调试,如查看变量、设置断点、单步执行等。
打断点(break)
break
命令用于设置断点,即在程序的某一行或某个函数内设置一个停止点,使得程序在执行到该处时会暂停,以便进行调试。也可以使用简写b
。如果想要查看已设置的断点,可以使用 info breakpoints
命令。
break
命令的基本语法如下:
break [location]
其中 location
可以是行号、函数名、文件名、或者其他标识位置的方式。
一些常见的用法示例:
- 在当前文件的某一行设置断点:
break line_number
-
在指定文件的某一行设置断点:
break file_name:line_number
-
在函数内设置断点:
break function_name
-
在地址处设置断点:
break *address
-
在条件满足时设置断点:
break line_number if condition
设置断点后,当程序执行到断点处时,gdb
会暂停执行,允许你查看变量、执行单步调试等操作。你可以在多个地方设置断点,以便在不同的地方进行调试。
逐语句(step)
step
命令用于单步执行程序,即按照程序的执行流程逐行执行代码。当遇到函数调用时,step
会进入函数内部执行,允许你逐步跟踪程序的执行过程。
step
命令的基本语法如下:
step
或者可以使用简写形式:
s
当你执行 step
命令时,gdb
将执行当前行并停在下一行。如果当前行是一个函数调用,gdb
会进入该函数并停在函数内的第一行。
逐过程(next)
next
命令用于执行程序的下一行代码,但不会进入到函数内部。如果当前行是一个函数调用,next
将执行整个函数并在函数返回后停止。
next
命令的基本语法如下:
next
或者可以使用简写形式:
n
当你执行 next
命令时,gdb
将执行当前行并停在下一行。如果当前行是一个函数调用,gdb
将执行整个函数并在函数返回后停止。这与 step
命令的区别在于,step
会进入函数内部执行。
next
命令适用于当你不希望进入函数内部,而只是想执行当前行的下一行代码。这在你对函数内部的细节不感兴趣,而只关注主程序流程时很有用。
完成当前函数(finish)
finish
命令用于执行程序直到当前函数返回为止,然后停止。在你进入了一个函数内部,但你想要跳过函数的执行,直到函数返回到调用点可以使用该命令。
finish
命令的基本语法如下:
finish
或者可以使用简写形式:
fin
当你执行 finish
命令时,gdb
将继续执行程序,直到当前函数返回到调用点。在这之后,程序会暂停执行,允许你进行下一步调试操作。finish
命令对于在进入函数后快速返回到调用点而不关心函数内部细节的情况非常有用。
打印变量值(print)
print
命令用于查看变量的值,表达式的值或内存位置的内容。这在调试中经常用到,可以帮助在调试过程中了解程序的状态。
print
命令的基本语法如下:
print expression
或者可以使用简写形式:
p expression
其中,expression
是你想要评估的变量、表达式或内存位置。
以下是一些示例:
-
查看变量的值:
print variable_name
或
p variable_name
-
查看表达式的值:
print x + y
或
p x + y
-
查看内存位置的内容:
print *address
或
p *address
-
查看数组的一部分:
print array_name[start_index:end_index]
或
p array_name[start_index:end_index]
设置变量(set)
set
命令用于设置各种调试环境的参数和选项。这包括设置变量的值、更改显示选项、配置断言处理等。set
命令的基本语法如下:
set parameter value
以下是一些 set
命令的示例:
-
设置变量的值:
set variable_name = new_value
-
更改显示格式:
set print pretty on
-
设置断言处理方式:
set assert-style fail
-
设置自动加载的源文件:
set auto-load safe-path /
-
设置显示堆栈帧的深度:
set backtrace limit 10
-
设置显示行号:
set list source-lines 5
set
命令提供了一种动态配置调试环境的方式,使得你可以根据需要调整 gdb
的行为。你可以使用 show
命令来查看当前的设置。
跳到下一断点(continue)
continue
命令用于继续执行程序,直到程序结束或者遇到下一个断点。当你在调试过程中想让程序一直运行到下一个断点或程序结束时,可以使用 continue
命令。
continue
命令的基本语法如下:
continue
或者可以使用简写形式:
c
执行 continue
命令后,gdb
将继续执行程序,直到遇到断点、程序结束,或者由用户中断。在这个过程中,你的程序将在不同的点上暂停执行,以允许你进行调试操作。
删除断点(delete)
delete
命令用于删除设置的断点,可以通过断点编号或者断点地址来删除断点。delete
命令的基本语法如下:
delete breakpoint_number
或者可以使用简写形式:
d breakpoint_number
示例:
-
通过断点编号删除断点:
delete 1
-
通过断点地址删除断点:
delete *0x0804842a
-
删除所有断点:
delete
在这里,breakpoint_number
是你想要删除的断点的编号。如果没有提供断点编号,则 delete
命令将删除所有断点。
禁用断点(disable)
disable
命令用于禁用设置的断点、观察点或自动显示点,禁用后,这些断点将不再生效,程序运行时将不会在这些位置停止。
disable
命令的基本语法如下:
disable breakpoint_number
或者可以使用简写形式:
dis breakpoint_number
示例:
-
通过断点编号禁用断点:
disable 1
-
禁用所有断点:
disable
在这里,breakpoint_number
是你想要禁用的断点的编号。如果没有提供断点编号,则 disable
命令将禁用所有当前设置的断点。
请注意,禁用断点并不会删除它们,而是暂时阻止它们生效。你可以使用
enable
命令来重新启用断点。
启用断点(enable)
enable
命令用于启用之前禁用的断点、观察点或自动显示点。一旦断点被启用,程序运行时将在这些位置停止,与之前的设置一致。
enable
命令的基本语法如下:
enable breakpoint_number
或者可以使用简写形式:
en breakpoint_number
示例:
-
通过断点编号启用断点:
enable 1
-
启用所有断点:
enable
在这里,breakpoint_number
是你想要启用的断点的编号。如果没有提供断点编号,则 enable
命令将启用所有之前禁用的断点。
请注意,启用断点后,程序运行时将在这些位置停止。你可以使用
disable
命令来禁用断点,以暂时阻止它们生效。
循环打印(display)
display
命令用于设置显示表达式,以便在每次程序停止时都自动显示其值,简写形式为disp
。可以避免每次都使用print
来打印的麻烦。display
命令的基本语法如下:
display expression
示例:
-
显示变量的值:
display variable_name
-
显示表达式的值:
display x + y
-
显示内存位置的内容:
display *address
-
显示数组的一部分:
display array_name[start_index:end_index]
取消循环打印(undisplay)
undisplay
命令用于取消之前使用 display
命令设置的表达式的显示。这意味着在每次程序停止时不再显示取消的表达式的值。undisplay
命令的基本语法如下:
undisplay display_number
或者可以使用简写形式:
und display_number
display_number
是你要取消显示的表达式的编号。
运行到指定行(until)
until
命令用于执行程序直到达到或超过指定的行号,然后停止执行。这个命令通常用于跳过循环或一系列代码,直到达到感兴趣的位置。
until
命令的基本语法如下:
until line_number
或者可以使用简写形式:
u line_number
在这里,line_number
是你希望程序执行直到达到或超过的行号。
查看调用栈(backtrace)
backtrace
是 gdb
中一个用于显示函数调用栈的命令。它展示了程序当前执行的位置及其在调用栈中的调用路径。这对于理解程序的执行流程和查找问题的来源非常有用。
基本语法如下:
backtrace
或者使用简写形式:
bt
执行这个命令会显示调用栈的内容,其中每个帧都表示一个函数调用。
示例:
(gdb) backtrace
#0 main () at example.c:10
#1 0x0000555555555149 in _start ()
backtrace
显示了两个帧。第一个帧显示当前执行的 main
函数,第二个帧显示程序的入口点 _start
。你还可以使用 info frame
命令来查看有关特定帧的更多信息:
(gdb) info frame 0
这将显示当前帧(帧编号为 0)的详细信息,包括寄存器状态、局部变量等。
设置观察点
在 gdb
中,watch
命令用于设置观察点,以便在变量的值发生更改时暂停程序的执行。这对于跟踪特定变量的变化并找出引起问题的地方非常有用。
watch
命令的基本语法如下:
watch variable_name
或者可以使用简写形式:
w variable_name
以下是一个示例:
(gdb) watch counter
或者使用简写形式:
(gdb) w counter
这将在变量 counter
的值发生变化时暂停程序的执行。
你还可以使用条件观察点,只有当满足特定条件时才暂停程序。例如:
(gdb) watch x > 10
这将在变量 x
的值大于 10 时暂停程序。
使用 info watch
命令可以查看当前设置的观察点。
(gdb) info watch
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004005f2 in main at example.c:10
watchpoint already hit 1 time
退出(quit)
quit
命令用于退出 gdb
调试器,结束调试会话。执行这个命令将终止正在调试的程序并关闭 gdb
。
quit
命令的基本语法如下:
quit
或者可以使用简写形式:
q
执行这个命令将退出 gdb
,并返回到命令行终端。
请注意,退出
gdb
时,正在调试的程序也将被终止。确保在退出之前保存任何重要的调试信息或程序状态。
总结
gdb调试的重要性不言而喻,本篇文章介绍了使用gdb调试经常会用到的一些命令,希望文章对你有所帮助!