GDB调试指南

本文介绍了如何在GCC编译C++代码时处理链接C++标准库的问题,并详细讲解了GDB调试工具中设置命令行参数、断点、查看变量、寄存器、栈帧、结构体以及窗口切换等高级功能的使用方法。
摘要由CSDN通过智能技术生成

gcc在链接C++代码时没有自动链接C++标准库,因此链接失败。

可以添加-lstdc++

gcc -o helloworldplus helloworldplus.cpp -lstdc++

1、gdb调试 使用命令行参数

#include <stdio.h>

int main(int argv,char *args[]){
    printf("argv =  %d\n",argv);
    for (int i=0;i<argv;i++){
			  printf("%s\n",args[i]);	
    }
    return 0;
}

将上面代码保存为helloworld.c,使用gcc -g -o helloworld helloworld.c编译成可执行程序。

使用gdb helloworld 进入gdb调试窗口。

第一种方法:

在运行前使用set args后跟命令行参数就可以。之后使用run命令运行程序就可以了

在这里插入图片描述

第二中方法

直接在在run后跟命令行参数就可以。

在这里插入图片描述

上图中也有一行Starting program: /root/gdb_study/helloworld a b c,从这里也能看到我们的命令行参数设置是生效的。

2、附加到一个运行的进程

使用gdb attach pid可以附加到一个正在运行的程序上

gdb中的断点可以分为好几个种类,比如普通断点、函数断点、条件断点等。

  • break 文件名:行号 在指定文件指定行设置断点。

b helloworld.c:4helloworld.c第4行设置断点。

输入list(缩写为l)来查看断点附近的代码,输入print(缩写为p)来查看变量的值,

在这里插入图片描述

  • break 函数名在指定函数设置断点

    b mainmain函数设置断点

  • 正则表达式设置函数断点

    rb 正则表达式

    rb main*在所有以main开头的函数添加断点

  • 通过偏移量设置断点

    b +偏移量
    b -偏移量
    

    当前代码执行到某一行时,如果要为当前代码行的前面某一行或者后面某一行设置断点,就可以通过偏移量来达到快速设置断点的目的。

  • 设置条件断点

    b 断点 条件

    b main:4 if argv==3 当argv==3时在main方法第4行设置断点

3、查看变量

使用p命令查看变量的值

代码如下:

#include <stdio.h>

int main(){
    int a = 0x1234;
    char b[] = "abcdefg";
    double c = 10.1;
    getchar();
    return 0;
}

执行

gcc -g -o memoryview memoryview.c  #编译成可执行程序
gdb memoryview                  
#进入gdb后执行run命令,程序会停在第7行的位置,这时我们就可以查看内存。

查看内存

命中断点时,使用x命令来查看各个变量的内存信息。x命令的语法如下:

x /选项 地址

各种选项

d 按十进制格式显示变量
x 按十六进制格式显示变量
a 按十六进制格式显示变量
u 按十六进制格式显示无符号整型
o 按八进制格式显示变量
t 按二进制格式显示变量
c 按字符格式显示变量
f 按浮点数格式显示变量

执行x /7x b ,以十六进制显示内存信息,/7x中的7表示显示7个字节

(gdb) x  /7x  b
0xfffffffff198:	0x61	0x62	0x63	0x64	0x65	0x66	0x67

字符串b值是abcdefg

命令x /s str还可以以字符串的形式输出。

(gdb) x /s b
0xfffffffff198:	"abcdefg"

命令x /d str还可以以十进制方式显示、按照单个字节的方式显示,同时可以指定显示的字节数量。

(gdb) x /d b
0xfffffffff198:	97
(gdb) x /4d b
0xfffffffff198:	97	98	99	100

对于数字变量,变量名不是地址,使用 &a的方式获取变量地址,然后再显示。

(gdb) x /4t  &a
0xfffffffff1ac:	00110100	00010010	00000000	00000000
(gdb) x /2x  &a
0xfffffffff1ac:	0x34	0x12
(gdb) x /2d  &a
0xfffffffff1ac:	52	18

函数地址、变量地址,还是其他地址,只要地址合法并且可以访问,就可以使用x命令来查看。

4、查看寄存器

在gdb中,通用寄存器会存储一些变量的值、函数的参数以及函数的返回值等。

x86和ARM寄存器的对应关系

  1. x86寄存器
    • EAXEBXECXEDX:数据寄存器,用于存储计算结果和中间值。
    • ESPEBP:指针寄存器,用于存储堆栈指针。
    • EIP:指令指针寄存器,存储当前执行的指令地址。
    • EFlags:标志寄存器,用于存储状态标志位。
  2. ARM寄存器
    • R0-R15:通用寄存器,用于存储数据和地址。
    • PC:程序计数器,存储当前执行的指令地址。
    • SP:堆栈指针寄存器。
    • LR:链接寄存器,存储子程序的返回地址。
    • CPSR(在ARMv7架构中):条件码寄存器,存储当前的条件标志位。

查看寄存器的命令是

info registers
#可以简写成 i r

info all-registers命令就会显示所有寄存器的值,包括浮点寄存器等。

可以使用i r 寄存器名查看指定寄存器的值

(gdb) i r pc
pc             0x4101dc            0x4101dc <main+52>

5、打印结构体

struct t{
        int a;
        char* b;
} ;

要查看结构体的值,比如上面的结构体,当前有一个st的结构体 ,a=10,b="abc",

(gdb) p  st
$1 = {a = 10, b = 0x420010 "abc"}

可以执行set print pretty使打印更漂亮

(gdb) set print pretty
(gdb) p  st
$2 = {
  a = 10,
  b = 0x420010 "abc"
}

对于数组,可以使用set print array on

6、查看栈帧

#include<stdio.h>

int b(int n){
    return n+1;

}
int a(int n){
    return b(n+1);
}
int main(){
    int n=0;
    n=a(n+1);
    return 0;
}

将上面的代码保存成framedemo.c。执行gcc -g -c framedemo framedemo.c编程。

执行gdb framedemo进入gdb窗口,执行b framedemo.c:4b函数中设置断点,执行run命令运行,程序就会在b函数中停住。

查看栈帧

这时可以使用btbt n命令查看调用栈帧。n代表现在最上层的栈帧个数。

(gdb) bt
#0  b (n=2) at framedemo.c:4
#1  0x00000000004101d8 in a (n=1) at framedemo.c:8
#2  0x00000000004101f8 in main () at framedemo.c:12
(gdb) bt 1
#0  b (n=2) at framedemo.c:4
(More stack frames follow...)
(gdb) bt 2
#0  b (n=2) at framedemo.c:4
#1  0x00000000004101d8 in a (n=1) at framedemo.c:8
(More stack frames follow...)
切换栈帧frame(简写f)

默认情况下,我们只能查看当前栈帧的变量,变量的值也只能显示当前栈帧的值。

当前代码停在了第4行,也就是b函数中 return n+1;的位置,由于当前n+1还没执行,此时n的值是2。

如果我们想要看下a函数中变量n,就可以执行frame 1切换到a函数的栈帧,这时使用p命令就能查看当前栈帧中的变量值。

(gdb) bt
#0  b (n=2) at framedemo.c:4
#1  0x00000000004101d8 in a (n=1) at framedemo.c:8
#2  0x00000000004101f8 in main () at framedemo.c:12
(gdb) p n
$1 = 2
(gdb) frame 1
#1  0x00000000004101d8 in a (n=1) at framedemo.c:8
8	    return b(n+1);
(gdb) p n
$2 = 1

可以使用info locals来查看当前帧所包含的所有局部变量的值,也可以使用info args来查看当前帧包含的所有函数参数.

#当前栈帧在b函数,只有一个参数n,值是2。当前没有局部变量。
(gdb) info args
n = 2
(gdb) info locals
No locals.

还可以使用up命令和down命令来切换帧,up命令和down命令都是基于当前帧来计数的。比如,当前帧号为1,使用up 1则会切换到2号帧,使用down 1则会切换到0号帧,

(gdb) up
#1  0x00000000004101d8 in a (n=1) at framedemo.c:8
8	    return b(n+1);
(gdb) up
#2  0x00000000004101f8 in main () at framedemo.c:12
12	    n=a(n+1);
(gdb) down
#1  0x00000000004101d8 in a (n=1) at framedemo.c:8
8	    return b(n+1);
(gdb) down
#0  b (n=2) at framedemo.c:4
4	    return n+1;
查看栈帧的详细信息

可以使用info frame(简写 i f)查看栈帧的详细信息,默认是当前栈帧,可以后跟数字入info frame 1查看编号1的栈帧。

(gdb) info frame
Stack level 0, frame at 0xfffffffff170:
 pc = 0x4101b0 in b (framedemo.c:4); saved pc = 0x4101d8
 called by frame at 0xfffffffff190
 source language c.
 Arglist at 0xfffffffff160, args: n=2
 Locals at 0xfffffffff160, Previous frame's sp is 0xfffffffff170
(gdb) info frame 1
Stack frame at 0xfffffffff190:
 pc = 0x4101d8 in a (framedemo.c:8); saved pc = 0x4101f8
 called by frame at 0xfffffffff1b0, caller of frame at 0xfffffffff170
 source language c.
 Arglist at 0xfffffffff170, args: n=1
 Locals at 0xfffffffff170, Previous frame's sp is 0xfffffffff190
 Saved registers:
  x29 at 0xfffffffff170, x30 at 0xfffffffff178

7、显示变量的类型

ptype可选参数 变量或者类型可以显示变量的类型

其中,可选参数用来控制显示信息,变量或者类型可以是任意的变量,也可以是定义的数据类型,比如类、结构体、枚举等。

ptype命令的可选参数如下所示。

/r:以原始数据的方式显示,不会代替一些typedef定义。

/m:查看类时,不显示类的方法,只显示类的成员变量。

/M:与/m相反,显示类的方法(默认选项)。

/t:不打印类中的typedef数据。

/o:打印结构体字段的偏移量和大小。

以下面的代码为例

#include <stdio.h>

struct t{
        int a;
        char* b;
} ;
int main(){
    struct t  st;
    st.a=10;
    st.b="abc";
    int n=10;
    printf("%d\t%s\n",st.a,st.b);
    return 0;
}

让代码暂停在12行,printf函数的位置,执行ptype输出n,st类型信息:

(gdb) ptype n
type = int
(gdb) ptype /m st
type = struct t {
    int a;
    char *b;
}
(gdb) ptype /o st
/* offset      |    size */  type = struct t {
/*      0      |       4 */    int a;
/* XXX  4-byte hole      */
/*      8      |       8 */    char *b;

                               /* total size (bytes):   16 */
                             }

whatis,可以用来查看变量或者表达式的类型,只是相对比较简单。

(gdb) whatis n
type = int
(gdb) whatis st
type = struct t

8、窗口切换

gdb可以同时显示几个窗口,比如命令窗口、源代码窗口、汇编窗口、寄存器窗口等。

● 命令窗口:gdb命令输入和结果输出的窗口,该窗口始终是可见的。

● 源代码窗口:显示程序源代码的窗口,会随着代码的执行自动显示代码对应的行。

● 汇编窗口:汇编窗口也会随着代码的执行而变化,显示代码对应的汇编代码行。

● 寄存器窗口:显示寄存器的值。

可以使用layout命令来控制窗口的显示。layout命令可以设置显示哪个窗口、是否切分窗口等,主要命令如下所示。

显示下一个窗口:layout next

显示前一个窗口:layout prev
只显示源代码窗口:layout src

在这里插入图片描述

只显示汇编窗口:layout asm

在这里插入图片描述

显示源代码窗口和汇编窗口:layout split

在这里插入图片描述

显示寄存器窗口,与源代码窗口和汇编窗口一起显示:layout regs

设置窗口为活动窗口,以便能够响应上下滚动键:focus next | prev | src | asm | regs | split

更新源代码窗口:refresh

更新源代码窗口:update

要关闭这些窗口(除命令窗口以外),可以执行tui disable命令。

gdb中直接输入info ,可以查到当前支持的查看的信息的所有命令。

比如我们想查看命令p的详细信息,可以在gdb中输入help p,就能显示p命令的具体用法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值