GDB调试实践

GDB调试实践

调试实例

 huhan@HuHan  ~/cpp/coredump  ls
test.cpp
 huhan@HuHan  ~/cpp/coredump 
 huhan@HuHan  ~/cpp/coredump  g++ -o test test.cpp -g
 huhan@HuHan  ~/cpp/coredump  ulimit
unlimited
 huhan@HuHan  ~/cpp/coredump  ulimit- c unlimited
zsh: command not found: ulimit-
 ✘ huhan@HuHan  ~/cpp/coredump  ulimit -c unlimied
ulimit: invalid number: unlimied
 ✘ huhan@HuHan  ~/cpp/coredump  ulimit -c unlimited
 huhan@HuHan  ~/cpp/coredump 
 huhan@HuHan  ~/cpp/coredump  g++ -o test test.cpp -g
 huhan@HuHan  ~/cpp/coredump  ulimit -c unlimited
 huhan@HuHan  ~/cpp/coredump  ls
test  test.cpp
 huhan@HuHan  ~/cpp/coredump  ./test
10
[1]    290 segmentation fault (core dumped)  ./test
 ✘ huhan@HuHan  ~/cpp/coredump  gdb test core
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...
[New LWP 290]
Core was generated by `./test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00005586369c71f9 in func (p=0x0) at test.cpp:6
6                   int y = *p;
(gdb) where
#0  0x00005586369c71f9 in func (p=0x0) at test.cpp:6
#1  0x00005586369c7296 in main () at test.cpp:19
(gdb) l
1       #include <iostream>
2       using namespace std;
3
4       int func(int *p)
5       {
6                   int y = *p;
7                       return y;
8       }
9
10      int add(int const& a, int const& b)
(gdb) l
11      {
12                      return a + b;
13      }
14
15      int main()
16      {
17                      cout<< add(4, 6) <<endl;
18                          int *p = NULL;
19                              return func(p);
20      }
(gdb) b 5
Breakpoint 1 at 0x5586369c71e9: file test.cpp, line 5.
(gdb) b 13
Breakpoint 2 at 0x5586369c7221: file test.cpp, line 13.
(gdb) b 17
Breakpoint 3 at 0x5586369c723e: file test.cpp, line 17.
(gdb) b main
Breakpoint 4 at 0x5586369c7223: file test.cpp, line 16.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00005586369c71e9 in func(int*) at test.cpp:5
2       breakpoint     keep y   0x00005586369c7221 in add(int const&, int const&) at test.cpp:13
3       breakpoint     keep y   0x00005586369c723e in main() at test.cpp:17
4       breakpoint     keep y   0x00005586369c7223 in main() at test.cpp:16
(gdb) r
Starting program: /home/huhan/cpp/coredump/test

Breakpoint 4, main () at test.cpp:16
16      {
(gdb) s

Breakpoint 3, main () at test.cpp:17
17                      cout<< add(4, 6) <<endl;
(gdb) s
add (a=@0x55555555537d: 29590344, b=<error reading variable>) at test.cpp:11
11      {
(gdb) s
12                      return a + b;
(gdb) p a
$1 = (const int &) @0x7fffffffdae8: 4
(gdb) n

Breakpoint 2, add (a=@0x7fffffffdae8: 4, b=@0x7fffffffdaec: 6) at test.cpp:13
13      }
(gdb) n
10
main () at test.cpp:18
18                          int *p = NULL;
(gdb) n
19                              return func(p);
(gdb) disassemble
Dump of assembler code for function main():
   0x0000555555555223 <+0>:     endbr64
   0x0000555555555227 <+4>:     push   %rbp
   0x0000555555555228 <+5>:     mov    %rsp,%rbp
   0x000055555555522b <+8>:     sub    $0x20,%rsp
   0x000055555555522f <+12>:    mov    %fs:0x28,%rax
   0x0000555555555238 <+21>:    mov    %rax,-0x8(%rbp)
   0x000055555555523c <+25>:    xor    %eax,%eax
   0x000055555555523e <+27>:    movl   $0x6,-0x14(%rbp)
   0x0000555555555245 <+34>:    movl   $0x4,-0x18(%rbp)
   0x000055555555524c <+41>:    lea    -0x14(%rbp),%rdx
   0x0000555555555250 <+45>:    lea    -0x18(%rbp),%rax
   0x0000555555555254 <+49>:    mov    %rdx,%rsi
   0x0000555555555257 <+52>:    mov    %rax,%rdi
   0x000055555555525a <+55>:    callq  0x555555555203 <add(int const&, int const&)>
   0x000055555555525f <+60>:    mov    %eax,%esi
   0x0000555555555261 <+62>:    lea    0x2dd8(%rip),%rdi        # 0x555555558040 <_ZSt4cout@@GLIBCXX_3.4>
   0x0000555555555268 <+69>:    callq  0x5555555550e0 <_ZNSolsEi@plt>
   0x000055555555526d <+74>:    mov    %rax,%rdx
   0x0000555555555270 <+77>:    mov    0x2d59(%rip),%rax        # 0x555555557fd0
   0x0000555555555277 <+84>:    mov    %rax,%rsi
   0x000055555555527a <+87>:    mov    %rdx,%rdi
   0x000055555555527d <+90>:    callq  0x5555555550b0 <_ZNSolsEPFRSoS_E@plt>
   0x0000555555555282 <+95>:    movq   $0x0,-0x10(%rbp)
=> 0x000055555555528a <+103>:   mov    -0x10(%rbp),%rax
   0x000055555555528e <+107>:   mov    %rax,%rdi
   0x0000555555555291 <+110>:   callq  0x5555555551e9 <func(int*)>
   0x0000555555555296 <+115>:   nop
   0x0000555555555297 <+116>:   mov    -0x8(%rbp),%rcx
   0x000055555555529b <+120>:   xor    %fs:0x28,%rcx
   0x00005555555552a4 <+129>:   je     0x5555555552b9 <main()+150>
   0x00005555555552a6 <+131>:   jmp    0x5555555552b4 <main()+145>
   0x00005555555552a8 <+133>:   endbr64
   0x00005555555552ac <+137>:   mov    %rax,%rdi
   0x00005555555552af <+140>:   callq  0x5555555550f0 <_Unwind_Resume@plt>
   0x00005555555552b4 <+145>:   callq  0x5555555550c0 <__stack_chk_fail@plt>
   0x00005555555552b9 <+150>:   leaveq
   0x00005555555552ba <+151>:   retq
End of assembler dump.
(gdb) s

Breakpoint 1, func (p=0x555555555330 <__libc_csu_init>) at test.cpp:5
5       {
(gdb) s
6                   int y = *p;
(gdb) s

Program received signal SIGSEGV, Segmentation fault.
0x00005555555551f9 in func (p=0x0) at test.cpp:6
6                   int y = *p;
(gdb) q
A debugging session is active.

        Inferior 1 [process 299] will be killed.

Quit anyway? (y or n) n
Not confirmed.
(gdb) info frame
Stack level 0, frame at 0x7fffffffdae0:
 rip = 0x5555555551f9 in func (test.cpp:6); saved rip = 0x555555555296
 called by frame at 0x7fffffffdb10
 source language c++.
 Arglist at 0x7fffffffdac8, args: p=0x0
 Locals at 0x7fffffffdac8, Previous frame's sp is 0x7fffffffdae0
 Saved registers:
  rbp at 0x7fffffffdad0, rip at 0x7fffffffdad8
(gdb) n

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) q
 huhan@HuHan  ~/cpp/coredump 

命令速查

启动 GDB

gdb object                # 正常启动,加载可执行
gdb object core           # 对可执行 + core 文件进行调试
gdb object pid            # 对正在执行的进程进行调试
gdb                       # 正常启动,启动后需要 file 命令手动加载
gdb -tui                  # 启用 gdb 的文本界面

断点

break main                # 对函数 main 设置一个断点,可简写为 b main
break 101                 # 对源代码的行号设置断点,可简写为 b 101
break basic.c:101         # 对源代码和行号设置断点
break basic.c:foo         # 对源代码和函数名设置断点
break *0x00400448         # 对内存地址 0x00400448 设置断点
info breakpoints          # 列出当前的所有断点信息,可简写为 info break
delete 1                  # 按编号删除一个断点
delete                    # 删除所有断点
clear                     # 删除在当前行的断点
clear function            # 删除函数断点
clear line                # 删除行号断点
clear basic.c:101         # 删除文件名和行号的断点
clear basic.c:main        # 删除文件名和函数名的断点
clear *0x00400448         # 删除内存地址的断点
disable 2                 # 禁用某断点,但是不删除
enable 2                  # 允许某个之前被禁用的断点,让它生效
rbreak {regexpr}          # 匹配正则的函数前断点,如 ex_* 将断点 ex_ 开头的函数
tbreak function|line      # 临时断点
hbreak function|line      # 硬件断点
ignore {id} {count}       # 忽略某断点 N-1 次
condition {id} {expr}     # 条件断点,只有在条件生效时才发生
condition 2 i == 20       # 2号断点只有在 i == 20 条件为真时才生效
watch {expr}              # 对变量设置监视点
info watchpoints          # 显示所有观察点
catch exec                # 断点在exec事件,即子进程的入口地址

运行程序

run                       # 运行程序
run {args}                # 以某参数运行程序
run < file                # 以某文件为标准输入运行程序
run < <(cmd)              # 以某命令的输出作为标准输入运行程序
run <<< $(cmd)            # 以某命令的输出作为标准输入运行程序
set args {args} ...       # 设置运行的参数
show args                 # 显示当前的运行参数
cont                      # 继续运行,可简写为 c
step                      # 单步进入,碰到函数会进去
step {count}              # 单步多少次
next                      # 单步跳过,碰到函数不会进入
next {count}              # 单步多少次
CTRL+C                    # 发送 SIGINT 信号,中止当前运行的程序
attach {process-id}       # 链接上当前正在运行的进程,开始调试
detach                    # 断开进程链接
finish                    # 结束当前函数的运行
until                     # 持续执行直到代码行号大于当前行号(跳出循环)
until {line}              # 持续执行直到执行到某行
kill                      # 杀死当前运行的函数

栈帧

bt                        # 打印 backtrace 
frame                     # 显示当前运行的栈帧
up                        # 向上移动栈帧(向着 main 函数)
down                      # 向下移动栈帧(远离 main 函数)
info locals               # 打印帧内的相关变量
info args                 # 打印函数的参数

代码浏览

list 101                  # 显示第 101 行周围 10行代码
list 1,10                 # 显示 1 到 10 行代码
list main                 # 显示函数周围代码
list basic.c:main         # 显示另外一个源代码文件的函数周围代码
list -                    # 重复之前 10 行代码
list *0x22e4              # 显示特定地址的代码
cd dir                    # 切换当前目录
pwd                       # 显示当前目录
search {regexpr}          # 向前进行正则搜索
reverse-search {regexp}   # 向后进行正则搜索
dir {dirname}             # 增加源代码搜索路径
dir                       # 复位源代码搜索路径(清空)
show directories          # 显示源代码路径

浏览数据

print {expression}        # 打印表达式,并且增加到打印历史
print /x {expression}     # 十六进制输出,print 可以简写为 p
print array[i]@count      # 打印数组范围
print $                   # 打印之前的变量
print *$->next            # 打印 list
print $1                  # 输出打印历史里第一条
print ::gx                # 将变量可视范围(scope)设置为全局
print 'basic.c'::gx       # 打印某源代码里的全局变量,(gdb 4.6)
print /x &main            # 打印函数地址
x *0x11223344             # 显示给定地址的内存数据
x /nfu {address}          # 打印内存数据,n是多少个,f是格式,u是单位大小
x /10xb *0x11223344       # 按十六进制打印内存地址 0x11223344 处的十个字节
x/x &gx                   # 按十六进制打印变量 gx,x和斜杆后参数可以连写
x/4wx &main               # 按十六进制打印位于 main 函数开头的四个 long 
x/gf &gd1                 # 打印 double 类型
help x                    # 查看关于 x 命令的帮助
info locals               # 打印本地局部变量
info functions {regexp}   # 打印函数名称
info variables {regexp}   # 打印全局变量名称
ptype name                # 查看类型定义,比如 ptype FILE,查看 FILE 结构体定义
whatis {expression}       # 查看表达式的类型
set var = {expression}    # 变量赋值
display {expression}      # 在单步指令后查看某表达式的值
undisplay                 # 删除单步后对某些值的监控
info display              # 显示监视的表达式
show values               # 查看记录到打印历史中的变量的值 (gdb 4.0)
info history              # 查看打印历史的帮助 (gdb 3.5)

目标文件操作

file {object}             # 加载新的可执行文件供调试
file                      # 放弃可执行和符号表信息
symbol-file {object}      # 仅加载符号表
exec-file {object}        # 指定用于调试的可执行文件(非符号表)
core-file {core}          # 加载 core 用于分析

信号控制

info signals              # 打印信号设置
handle {signo} {actions}  # 设置信号的调试行为
handle INT print          # 信号发生时打印信息
handle INT noprint        # 信号发生时不打印信息
handle INT stop           # 信号发生时中止被调试程序
handle INT nostop         # 信号发生时不中止被调试程序
handle INT pass           # 调试器接获信号,不让程序知道
handle INT nopass         # 调试器不接获信号
signal signo              # 继续并将信号转移给程序
signal 0                  # 继续但不把信号给程序

线程调试

info threads              # 查看当前线程和 id
thread {id}               # 切换当前调试线程为指定 id 的线程
break {line} thread all   # 所有线程在指定行号处设置断点
thread apply {id..} cmd   # 指定多个线程共同执行 gdb 命令
thread apply all cmd      # 所有线程共同执行 gdb 命令
set schedule-locking ?    # 调试一个线程时,其他线程是否执行,off|on|step
set non-stop on/off       # 调试一个线程时,其他线程是否运行
set pagination on/off     # 调试一个线程时,分页是否停止
set target-async on/off   # 同步或者异步调试,是否等待线程中止的信息

进程调试

info inferiors                       # 查看当前进程和 id
inferior {id}                        # 切换某个进程
kill inferior {id...}                # 杀死某个进程
set detach-on-fork on/off            # 设置当进程调用fork时gdb是否同时调试父子进程
set follow-fork-mode parent/child    # 设置当进程调用fork时是否进入子进程

汇编调试

info registers            # 打印普通寄存器
info all-registers        # 打印所有寄存器
print/x $pc               # 打印单个寄存器
stepi                     # 指令级别单步进入,可以简写为 si
nexti                     # 指令级别单步跳过,可以简写为 ni
display/i $pc             # 监控寄存器(每条单步完以后会自动打印值)
x/x &gx                   # 十六进制打印变量
info line 22              # 打印行号为 22 的内存地址信息
info line *0x2c4e         # 打印给定内存地址对应的源代码和行号信息
disassemble {addr}        # 对地址进行反汇编,比如 disassemble 0x2c4e

历史信息

show commands             # 显示历史命令 (gdb 4.0)
info editing              # 显示历史命令 (gdb 3.5)
ESC-CTRL-J                # 切换到 Vi 命令行编辑模式
set history expansion on  # 允许类 c-shell 的历史
break class::member       # 在类成员处设置断点
list class:member         # 显示类成员代码
ptype class               # 查看类包含的成员
print *this               # 查看 this 指针

其他命令

define command ... end    # 定义用户命令
<return>                  # 直接按回车执行上一条指令
shell {command} [args]    # 执行 shell 命令
source {file}             # 从文件加载 gdb 命令
quit                      # 退出 gdb

参考资料

GDB命令 | ZDZN 文档教程

GDB 调试指南

如何通过gdb查看反汇编代码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值