gdb入门系列4
mufiye 内核新手

GDB入门系列4

查看运行时数据

在你调试程序时,当程序被停住时,你可以使用print命令(简写命令为p),或是同义命令inspect来查看当前程序的运行数据。print命令的格式是:print /

一、表达式

    print和许多GDB的命令一样,可以接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,既然是表达式,那么就可以是当前程序运行中的const常量、变量、函数等内容。可惜的是GDB不能使用你在程序中所定义的宏。表达式的语法应该是当前所调试的语言的语法。

二、程序变量

    如果你的程序编译时开启了优化选项,那么在用GDB调试被优化过的程序时,可能会发生某些变量不能访问,或是取值错误码的情况。这个是很正常的,因为优化程序会删改你的程序,整理你程序的语句顺序,剔除一些无意义的变量等,所以在GDB调试这种程序时,运行时的指令和你所编写指令就有不一样,也就会出现你所想象不到的结果。对付这种情况时,需要在编译程序时关闭编译优化。一般来说,几乎所有的编译器都支持编译优化的开关,例如,GNU的C/C++编译器GCC,你可以使用“-gstabs”选项来解决这个问题。关于编译器的参数,还请查看编译器的使用说明文档。

三、数组

    有时候,你需要查看一段连续的内存空间的值。比如数组的一段,或是动态分配的数据的大小。你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。

    @的左边是数组的首地址的值,也就是变量array所指向的内容,右边则是数据的长度,其保存在变量len中。
run
1
2
(gdb) p *array@len
$1 = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40}

四、输出格式

  • x 按十六进制格式显示变量。
  • d 按十进制格式显示变量。
  • u 按十六进制格式显示无符号整型。
  • o 按八进制格式显示变量。
  • t 按二进制格式显示变量。
  • a 按十六进制格式显示变量。
  • c 按字符格式显示变量。
  • f 按浮点数格式显示变量。
    run
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    (gdb) p i
    $21 = 101

    (gdb) p/a i
    $22 = 0x65

    (gdb) p/c i
    $23 = 101 'e'

    (gdb) p/f i
    $24 = 1.41531145e-43

    (gdb) p/x i
    $25 = 0x65

    (gdb) p/t i
    $26 = 1100101

五、查看内存

你可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:
x/<n/f/u>

  • n、f、u是可选的参数。
  • n 是一个正整数,表示显示内存的长度,也就是说从当前地址向后显示几个地址的内容。
  • f 表示显示的格式,参见上面。如果地址所指的是字符串,那么格式可以是s,如果地十是指令地址,那么格式可以是i。
  • u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
  • 表示一个内存地址。
    1
    x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。

六、自动显示

你可以设置一些自动显示的变量,当程序停住时,或是在你单步跟踪时,这些变量会自动显示。相关的GDB命令是display。

  • display/ ,display/
    expr是一个表达式,fmt表示显示的格式,addr表示内存地址,当你用display设定好了一个或多个表达式后,只要你的程序被停下来,GDB会自动显示你所设置的这些表达式的值。
  • undisplay <dnums…>,delete display <dnums…>
    删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)
  • disable display <dnums…>
    使自动显示失效
  • enable display <dnums…>
    激活自动显示
  • info display
    查看display设置的自动显示的信息

Lab

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(gdb) break 24
Breakpoint 1 at 0x11ec: file tst.c, line 24.
(gdb) r
Starting program: /home/ubuntu/learn_gdb/tst

Breakpoint 1, main (argc=1, argv=0x7fffffffe148) at tst.c:24
24 long result = 0;
(gdb) display i
1: i = 21845
(gdb) n
25 for (i = 1; i <= 100; i++)
1: i = 21845
(gdb) n
27 result += i;
1: i = 1
(gdb) n
25 for (i = 1; i <= 100; i++)
1: i = 1
(gdb) n
27 result += i;
1: i = 2
(gdb) n
25 for (i = 1; i <= 100; i++)
1: i = 2
(gdb) n
27 result += i;
1: i = 3
(gdb) n
25 for (i = 1; i <= 100; i++)
1: i = 3
(gdb) n
27 result += i;
1: i = 4
(gdb) n
25 for (i = 1; i <= 100; i++)
1: i = 4

七、设置显示选项

没看

八、历史记录

    当你用GDB的print查看程序运行时的数据时,你每一个print都会被GDB记录下来。GDB会以$1, $2, $3 .....这样的方式为你每一个print命令编上号。于是,你可以使用这个编号访问以前的表达式,如$1。这个功能所带来的好处是,如果你先前输入了一个比较长的表达式,如果你还想查看这个表达式的值,你可以使用历史记录来访问,省去了重复输入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
(gdb) break 26
Breakpoint 1 at 0x1269: file tst.c, line 26.
(gdb) r
Starting program: /home/ubuntu/learn_gdb/tst

Breakpoint 1, main (argc=1, argv=0x7fffffffe148) at tst.c:26
26 for(int k=0;k<10;k++)
(gdb) p *arr
$1 = 0
(gdb) p arr
$2 = {0, 11, 22, 33, 44, 55, 66, 77, 88, 99}
(gdb) p j
$3 = 3
(gdb) p i
$4 = -8137
(gdb) p result
$5 = 0
(gdb) p argc
$6 = 1
(gdb) show convenience
$bpnum = 1
$_gdb_minor = 2
$_gdb_major = 9
$_as_string = <internal function _as_string>
$_regex = <internal function _regex>
$_streq = <internal function _streq>
$_strlen = <internal function _strlen>
$_memeq = <internal function _memeq>
$_any_caller_matches = <internal function _any_caller_matches>
$_any_caller_is = <internal function _any_caller_is>
$_caller_matches = <internal function _caller_matches>
$_caller_is = <internal function _caller_is>
--Type <RET> for more, q to quit, c to continue without paging--q
Quit
(gdb) p $1
$7 = 0
(gdb) set $1 = 11
Left operand of assignment is not a modifiable lvalue.
(gdb) p $1
$8 = 0
(gdb) c
Continuing.
result[1-100] = 5050
result[1-250] = 31125
[Inferior 1 (process 3039503) exited normally]
(gdb) p i
No symbol "i" in current context.
(gdb) p $4
$9 = -8137

上面的实验说明,单纯的就是历史记录,而且该历史记录无法被修改(上面尝试赋值,但是提示Left operand of assignment is not a modifiable lvalue.),就算当初print的变量已经被销毁了,这条历史记录还是存在的,并且可以被打印出来。

九、GDB环境变量

    你可以在GDB的调试环境中定义自己的变量,用来保存一些调试程序中的运行数据。要定义一个GDB的变量很简单只需。使用GDB的set命令。GDB的环境变量和UNIX一样,也是以$起头。如:
    set $foo = *object_ptr
    使用环境变量时,GDB会在你第一次使用时创建这个变量,而在以后的使用中,则直接对其賦值。环境变量没有类型,你可以给环境变量定义任一的类型。包括结构体和数组。

Lab1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(gdb) set $k = 0
(gdb) break 21
Breakpoint 1 at 0x124c: file tst.c, line 21.
(gdb) r
Starting program: /home/ubuntu/learn_gdb/tst

Breakpoint 1, main (argc=1, argv=0x7fffffffe148) at tst.c:21
21 j = 1;
(gdb) print arr[$k++]
$1 = 0
(gdb)
$2 = 11
(gdb)
$3 = 22
(gdb)
$4 = 33
(gdb)
$5 = 44
(gdb)
$6 = 55
(gdb)
$7 = 66
(gdb)
$8 = 77
(gdb)
$9 = 88
(gdb)
$10 = 99
(gdb) show convenience
$bpnum = 1
$k = 10
$_gdb_minor = 2
$_gdb_major = 9
$_as_string = <internal function _as_string>
$_regex = <internal function _regex>
$_streq = <internal function _streq>
$_strlen = <internal function _strlen>
$_memeq = <internal function _memeq>
$_any_caller_matches = <internal function _any_caller_matches>
$_any_caller_is = <internal function _any_caller_is>
$_caller_matches = <internal function _caller_matches>
--Type <RET> for more, q to quit, c to continue without paging--

Lab2

之前是对环境变量传了一个值,那么如果对环境变量传了一个指针会怎么样?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) break 20
Breakpoint 1 at 0x1245: file tst.c, line 20.
(gdb) r
Starting program: /home/ubuntu/learn_gdb/tst

Breakpoint 1, main (argc=1, argv=0x7fffffffe148) at tst.c:20
20 int j=0;
(gdb) set $ptr = arr
(gdb) show convenience
$ptr = (int *) 0x7fffffffe020
$bpnum = 1
$_gdb_minor = 2
$_gdb_major = 9
...
...
(gdb) print *($num)
Attempt to take contents of a non-pointer value.

结论是可以赋值,就是赋值了一个地址。

十、查看寄存器

  • info registers
    查看寄存器的情况。(除了浮点寄存器)
  • info all-registers
    查看所有寄存器的情况。(包括浮点寄存器)
  • info registers <regname …>
    查看所指定的寄存器的情况。
  • 本文标题:gdb入门系列4
  • 本文作者:mufiye
  • 创建时间:2021-11-26 13:31:50
  • 本文链接:http://mufiye.github.io/2021/11/26/gdb入门系列4/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论