vimer linux kernel 爱好者

gdb调试举例

2016-03-04

终于有机会总结gdb了

打开寄存器窗口

layout regs

显示的窗口如下:

┌──Register group: general─────────────────────────────────────────────────────────────────────────────────────┐
│zero           0x0      0                                                                                     │
│ra             0x252ea  0x252ea <__real_libc_init(void*, void (*)(), int (*)(int, char**, char**), structors_a│
│sp             0x3ffffff640     0x3ffffff640                                                                  │
│gp             0xf4e28  0xf4e28 <je_extents_rtree+337096>                                                     │
│tp             0x1555568fc0     0x1555568fc0                                                                  │
│t0             0x253a8  152488                                                                                │
│t1             0x3c412  246802                                                                                │
│t2             0x8f2b8  586424                                                                                │
│fp             0x88028  0x88028 <__FINI_ARRAY__>                                                              │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
B+>│0x25078 <main+2>        sd     ra,8(sp)                                                                    │
   │0x2507a <main+4>        auipc  a0,0xfffec                                                                  │
   │0x2507e <main+8>        addi   a0,a0,1215                                                                  │
   │0x25082 <main+12>       auipc  ra,0x22                                                                     │
   │0x25086 <main+16>       jalr   -1426(ra)                                                                   │
   │0x2508a <main+20>       li     a0,0                                                                        │
   │0x2508c <main+22>       ld     ra,8(sp)                                                                    │
   │0x2508e <main+24>       addi   sp,sp,16                                                                    │
   │0x25090 <main+26>       ret                                                                                │
   └───────────────────────────────────────────────────────────────────────────────────────────────────────────┘
remote Thread 12044.12044 In: main                                                            L4    PC: 0x25078
(gdb)

如果想查看浮点寄存器,可以使用“tui reg float”命令

首先声明一点吧,这是我一直以来的没有搞明白的地方。

(gdb) b main
Breakpoint 1 at 0x5555555546e9: file my.c, line 21.
(gdb) c
Starting program: /home/yubo/test/my

Breakpoint 1, main () at my.c:21
21		n = 1;
(gdb) s
22		n++;
(gdb) p n
$1 = 1

在这段代码中,我设置了一个断点并且运行起来,注意,断点处,21,这个21的n=1还没有被执行,只是下一次将要执行的语句,例证就是22的代码,你看,在22处, 虽然打印了n++,但是你打印 p n, 则还是n,说明n没有加1,所以这就是下一条语句要执行的过程。

下面的这个程序我还弄不明白,继续debug

#include<stdio.h>
int main()
{
	int a[5] = { 1,2,3,4,5};
	int *ptr1 = (int *)(&a + 1);
	int *ptr2 = ((int *)(int)a + 1);
	printf("%x,%x",ptr1[-1],*ptr2);
	return 0;
}

list命令

list num

显示源代码的num行的周围几行。

list function

显示函数。

p命令

打印结构体元素

使用这个命令,还可以查看结构体的各元素值

p *pstDate

打印数组元素

如果你想查看一个数组的各个元素,别像我似的傻乎乎的一个个 p a[i],节省生命的做 法应该是:

p ArrayVal@itemnum

什么意思?意思就是打印数组ArrayVal的前itemnum个元素。

格式控制

p /x Val    十六进制

  /c		字符?

  /f		浮点型?

查看内存变量

x

我也不知道这一个有什么作用现在为止

查看局部变量

info local

run

set args num1 num2 num3 ..

使用show args查看设置的参数。

break

break function

你可以在一个函数前面加上b,到时候就可以在这个函数设置断点了。

break lines

break filename:linenum

在源文件filename的linenum行设置断点

break filename:function

在源文件的filename的function设置断点

break *address

在程序运行的内存空间地址处停住.

break

没有参数时,表示在下一条指令处停住。

break if

比如,设置

break if i = 10;

就是在循环体内当i等于10的时候停住。

方便一点的话,可以使用

filename:line_numbers if i == 5

比如, 我想在文件gdb.c的循环体for内当I等于5的停下来,则:

break gdb.c:6 if i == 5

单步执行 next和step

在调用函数的那行,设置个断点,然后step就进入函数体内。 扩展的用法是next和step

finish

运行程序直到当前函数完成返回,并打印函数的返回时的堆栈地址、返回值及参数值。

until

使用until退出循环体。这一条十分重要哈^_^.

stepi(si) 和nexti(ni)

stepi用于单步跟踪一条机器指令,另外使用

display/i $pc

在打印出程序代码的同时打印出机器指令。

watch

watch 为表达式exp设置一个观察点,一旦表达式的值发生变化,马上停止程序运行。

rwatch 当表达式被读时,停止程序运行。

awatch 当表达式的值被读或者被写时,停止程序运行。

info watchpoints: 列出当前所设置的所有观察点。

查看函数体内的变量值

当我们使用 s 命令进入函数体内时,除了简单的 p 打印,还要更加丰富些,比如,使用backtrace(bt)

(gdb) bt
#0  add_range (low=1, high=10) at main.c:6
#1  0x080483c1 in main () at main.c:14

从这里可以看出add_range()函数是main函数调用的,main传进来的参数是low=1,high=10,main的帧为1,add_range()的帧为0,使用i(info)来看函数局部变量的值。

如果想查看main函数的当前局部变量也可以,先使用frame(f)命令,选择1号栈再查看局部变量的值:

(gdb) f 1
#1  0x080483c1 in main () at main.c:14
14		result[0] = add_range(1, 10);
(gdb) i locals
result = {0, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
  -1208623680}

assemble

disassembles /rm这个函数就会显示当前函数的汇编信息,其中,/r是十六进制(raw instructions), /m,如果你对intel 的汇编语法感兴趣,可以使用这个命令。

x/i $pc 这条命令是AT&T的语法格式的。 下面是一个简单的Hello.c的反汇编,

(gdb) disassemble /m main
Dump of assembler code for function main:
7	{
   0x0000000000027936 <+0>:	addi	sp,sp,-16

8		int a=10;
9		int b=10;
10		printf("Hello world\n");
   0x000000000002793a <+4>:	auipc	a0,0xfffea
   0x000000000002793e <+8>:	addi	a0,a0,-1025 # 0x11539
   0x0000000000027942 <+12>:	auipc	ra,0x22
   0x0000000000027946 <+16>:	jalr	-1502(ra) # 0x49364 <puts(char const*)>

11		printf("int a+b = %d\n", sum(a, b));
   0x000000000002794e <+24>:	addi	a0,a0,1573 # 0x13f6f
   0x0000000000027952 <+28>:	li	a1,20
   0x0000000000027954 <+30>:	auipc	ra,0x22
   0x0000000000027958 <+34>:	jalr	-1664(ra) # 0x492d4 <printf(char const*, ...)>

12		return 0;
   0x000000000002795c <+38>:	li	a0,0
   0x000000000002795e <+40>:	ld	ra,8(sp)
   0x0000000000027960 <+42>:	addi	sp,sp,16
   0x0000000000027962 <+44>:	ret

请仔细看这段代码,这是riscv64的反汇编:)

gdb查看内存指令–x

使用案例

一次性查看结构体各个成员的值

比如,我有一个结构体结构是这样的:

/*一个有权值的边集在图中*/
 struct Edge{
 	int src, dest, weight;

 };

 /* V: 顶点数 E:边数,无向图*/
 struct Graph{
 	int V,E;
 	struct Edge* edge;
 };

先定义一个graph,在完成一次loop操作后,这时我想查看其中的各个域的值,可以一次一次的使用:

p graph@v

等进行查看,不过这样查看效率可是不高,所以在gdb中,需要使用脚本:

(gdb) set $i=5
(gdb) while ($i!=0)
 >print graph->edge[$i]
 >set $i = $i - 1
 >end
$5 = {src = 2, dest = 4, weight = 7}
$6 = {src = 4, dest = 1, weight = 6}
$7 = {src = 0, dest = 4, weight = 5}
$8 = {src = 49, dest = 0, weight = 0}
$9 = {src = 3, dest = 2, weight = 4}

下一篇 qemu调试kernel

Comments

Content