gdb和vscode的调试初探

gdb和vscode调试初探与程序内部结构

核心:1、了解两种调试方法。

2、掌握程序在储存和加载情况下的内存结构

gdb

gdb是gnu下gcc的调试工具,和汇编的debug差不多

它以命令行方式运行。它的相关命令可以自己查看gdb参数使用。

熟练使用gdb需要相当长时间的学习,更需要在实际使用中不断熟习

下面我们了解并快速使用它。首先写一个小程序

int main()
{
   
     int i = 3333;
     return 0;
}

要能调试,必须加上 -g 选项编译,以生成带调试信息的可执行文件,导入 gdb :

$ gcc -g gdbTest.c -o gdbTest
$ gdb gdbTest

现在,如果你看到的是一堆 gdb 提示信息,说明你已经进入 gdb 了。接下来:

(gdb) print 1 + 2
$1 = 3

print 是 gdb 用于处理 C 表达式的内建命令。如果你对 gdb 的某个命令不清楚, 试试help 命令查看 gdb 的帮助。接下来,看一个更有趣的例子:

(gdb) print (int) 2147483648
$2 = -2147483648

为什么 2147483648 = -2147483648 ,因为是带符号的整型

现在我们在 main 函数设置断点并运行:

(gdb) break main
(gdb) run

好了,现在程序暂停在第3行,变量 i 初始化之前。趁 i 还未初始化,我们用 print 命令来显示它的值:

(gdb) print i
$3 = 0

在 C 语言中,本地变量在未初始化前,它的值是不确定的,所以在你的电脑上 gdb 显示的结果可能跟我的不一样!我们用 next 命令执行当前这行代码:

(gdb) next
(gdb) print i
$4 = 3333

内存查看命令 x

C 语言为变量分配连续的内存空间。一个变量的内存空间由两方面决定:

1.内存块的起始字节地址。2.内存块的 byte 大小。变量内存大小是由变量类型决定的。

C语言鲜明的特征之一便是,可以直接访问内存空间。取地址操作符 & 用于获取变量的地址,而 sizeof 操作符用于计算变量占用的内存空间大小。

尝试在 gdb 运行下面的命令:

(gdb) print &i
$5 = (int *) 0x7fff5fbff584
(gdb) print sizeof(i)
$6 = 4

输出结果表明,变量 i 的内存起始地址为 0x7fff5fbff584 ,并且占用4 byte 空间。我在上面曾说过,变量占用内存的大小由它的类型决定,实际上 sizeof 运算符也可以直接对变量类型进行计算:

(gdb) print sizeof(int)
$7 = 4
(gdb) print sizeof(double)
$8 = 8

这说明,在我的机器上, int 类型变量占4字节,而 double 类型变量占8字节。

x 命令,是 gdb 直接查看内存的强大工具。 x 命令查看指定位置起始的内存段,并有一系列格式化命令精确控制查看的字节数以及显示方式。

如有疑问,请在 gdb 中查看帮助 help x

由于 & 操作符用于计算变量地址,因此可以把 &i 的结果作为参数传入 x ,这样,我们便可以窥探到变量 i 在内存的原始字节形式啦:

(gdb) x/4xb &i
0x7fff5fbff584: 0x39    0x05    0x00    0x00

4xb 意思是,我要查看4个数值,格式化为 he x (十六进制), 按1 byte一次显示。我只选择显示4字节,是因为变量 i 大小为4个字节。上面的输出显示, i 是逐字节存放在内存中的。

在直接查看内存字节时,这里有个细节需要记住:在 Intel 的机器上,字节内容是按照"little-edian" 小端的方式存放的。与人的直觉相反,小端方式是数值的最低有效位放在内存的最前面。

为了更加直观,举个例子来说明:

(gdb) set var i = 0x12345678
(gdb) x/4xb &i
0x7fff5fbff584: 0x78 0x56 0x34 0x12

类型查看命令 ptype

ptype 是我最常用的命令,它可以返回 C 表达式的类型。

(gdb) ptype i
type = int
(gdb) ptype &i
type = int *
(gdb) ptype main
type = int (void)

C 语言中,有非常复杂的数据类型,不过,利用 ptype 命令,便可以以交互的方式来查看,实在是方便的很~

拓展与作业1
用gdb探索一下数组和指针。

示例

int main()
{
   
    int a[] = {
   1,2,3};
    return 0;
}

-g 选项编译,装入 gdb 中运行, next 直到运行完数组初始化:

$ gcc -g arrays.c -o arrays
$ gdb arrays
(gdb) break main
(gdb) run
(gdb) next

看下经过初始化化后 a 中的内容:

(gdb) print a
$1 = {
   1, 2, 3}
(gdb) ptype a
type = int [3]

好了,程序正确地运行了。接下来我们首先要做的是用 x 命令看看 a 在内存中到底是怎样一种存在:

(gdb) x/12xb &a
0x7fff5fbff56c: 0x01  0x00  0x00  0x00  0x02  0x00  0x00  0x00
0x7fff5fbff574: 0x03  0x00  0x00  0x00

上面的结果显示,a 这块内存起始地址为 0x7fff5fbff56c ,第一个4 byte 存储在 a[0] ,下一个4 byte 存放在 a[1] ,最后4 byte 放在 a[2] 。当然,也可以通过 sizeof 得知 a 所占内存大小:

(gdb) print sizeof(a)
$2 = 12

从这点上来看,数组确实有其作为数组的特征:有他们自己像数组的类型以及存放数组于连续的内存空间。然而,从特定的角度看,数组的行为又与指针极为相似:


    (gdb) print a + 1
    $3 = (int *) 0x7fff5fbff570

这显示,a + 1 是一个指向 0x7fff5fbff570int 指针。到这里,本能地用 x 命令看下这地址的内容:


    (gdb) x/4xb a + 1
    0x7fff5fbff570: 0x02  0x00  0x00  0x00

我们注意到 0x7fff5fbff570a 的起始地址 0x7fff5fbff56c 大4个字节。因为给定 int 类型占4byte ,所以 a + 1 应该是指向 a[1]

事实上,数组的下标运算是指针的语法糖:a[i] 等同于 *(a + i) 。看下面的尝试:


    (gdb) print a[0]
    $4 = 1
    (gdb) print *(a + 0)
    $5 = 1
    (gdb) print a[1]
    $6 = 2
    (gdb) print *(a + 1)
    $7 = 2
    (gdb) print a[2]
    $8 = 3
    (gdb) print *(a + 2)
    $9 = 3

由上面种种可知,在一些环境下 a 类似于数组,而在另外一些条件下它又像指向数组第一个元素的指针。这究竟是怎么一回事呢?

这个问题的答案是:当数组名用在 C 表达式中时,它便“退化"为指向数组第一个元素的地址。不过,对于这条规则,有2个例外:

•数组名作为 sizeof 参数时;•数组名用于 & 操作符时。

这就让人疑惑了,难道同为指针的 a&a 竟是不相同的吗?

就数值而言,它们都表示同一地址:


    (gdb) x/4xb a
    0x7fff5fbff56c: 0x01  0x00  0x00  0x00
    (gdb) x/4xb &a
    0x7fff5fbff56c: 0x01  0x00  0x00  0x00

然而,它们本身的类型却是不相同的。

此前,我们已经知道 a 是指向数组第一元素的指针,所以是 int * 类型。

至于 &a 的话,我们直接用 gdb 查看一下:


    (gdb) ptype &a
    type = int (*)[3]

很明显, &a 是指向带有3个整数的数组的指针,亦即 int [3] ,显然与 a 不一样嘛。在下面的指针运算中,你可以更加明显的看到 a&a 的差异:


    (gdb) print a + 1
    $10 = (int *) 0x7fff5fbff570
    (gdb) print &a + 1
    $11 = (int (*)[3]) 0x7fff5fbff578

a + 1 只是在 a 的地址上加4,而 &a + 1 却增加了12!指针 a 实际上是与 &a[0] 等同的。

相比gdb,vscode的调试就简单直观很多

vscode的调试

我们换一个程序,这程序比较难一些。我们主要观察它在vscode中调试的情况

#
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 VS Code 中使用 GDB 调试 Linux 内核源码的步骤如下: 1. 安装插件:首先需要在 VS Code 中安装插件 "Native Debug",该插件提供了对 GDB 的支持。 2. 配置工作目录:在 VS Code 中打开 Linux 内核源码所在的目录,并在 VS Code 中打开一个终端,进入到内核源码目录中。 3. 生成内核配置:使用 "make menuconfig" 命令生成内核配置,可以根据需要选择需要编译的内核模块。 4. 编译内核:使用 "make -j4" 命令编译内核,其中 "-j4" 表示使用 4 个线程进行编译。 5. 启动 GDB:在终端中输入 "gdb ./vmlinux" 命令启动 GDB,其中 "vmlinux" 是内核文件名。 6. 配置 GDB:在 GDB 中输入以下命令进行配置: * "set architecture i386:x86-64:intel":设置架构为 x86-64。 * "set sysroot /usr/src/linux":设置系统根路径为内核源码路径。 * "file vmlinux":设置内核文件名。 * "target remote :1234":连接到远程目标,端口号为 1234。 7. 启动虚拟机:在另一个终端中启动虚拟机,使用以下命令: ``` qemu-system-x86_64 -s -S -kernel arch/x86/boot/bzImage -nographic ``` 其中 "-s" 表示开启 GDB 服务器,"-S" 表示启动时暂停虚拟机,"-kernel" 指定内核文件名,"-nographic" 表示不使用图形界面。 8. 调试内核:在 GDB 中输入 "continue" 命令,让虚拟机继续执行,即可开始调试内核。 在调试过程中,可以使用 GDB 提供的各种命令进行调试,例如 "break" 设置断点,"step" 单步执行,"print" 打印变量值等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值