Linux基础知识(14)- GDB 调试器(二)| 普通断点、单步调试和查看变量


在 “Linux基础知识(13)- GDB 调试器(一)| 安装配置和基本使用方法” 里我们完成了 GDB 的安装配置,并演示了 GDB 几个内部命令的基本使用方法,本文将演示普通断点、单步调试和查看变量。


1. 创建 C 程序

    1) 代码如下

        $ cd ~/
        $ vim test2.c

            #include <stdio.h>

            int testFunc() {
                for (int i=0; i<3; i++ ) {
                    printf("printf i=%d\n", i);
                }
            }

            int main(int argc, char* argv[]) {

                if (argv[1] == NULL) {
                    printf("Hello world - GDB\n");
                } else {
                    printf("Hello world - %s\n", argv[1]);
                }

                testFunc();
            }


    2)编译并设置调试信息

        $ gcc -g test2.c -o test2
        $ ./test2

            Hello world - GDB
            printf i=0
            printf i=1
            printf i=2


        $ ./test2 "Message"

            Hello world - Message
            printf i=0
            printf i=1
            printf i=2


2. 设置普通断点

    1) 使用 break 命令设置断点

        使用 GDB 启动 test2 程序:

            $ gdb -q test2

                Reading symbols from test...

                (gdb) l
                1       #include <stdio.h>
                2
                3       int testFunc() {
                4           for (int i=0; i<3; i++ ) {
                5               printf("printf i=%d\n", i);
                6           }
                7       }
                8
                9       int main(int argc, char* argv[]) {
                10
                (gdb)
                11          if (argv[1] == NULL) {
                12              printf("Hello world - GDB\n");
                13          } else {
                14              printf("Hello world - %s\n", argv[1]);
                15          }
                16
                17          testFunc();
                18      }
                19


        运行 break (简写 b) 命令在指定行设置断点,使用方法是 b 空格 行号。
    
            (gdb) b 11

                Breakpoint 1 at 0x11b4: file test2.c, line 11.

            (gdb) b 17

                Breakpoint 2 at 0x11f1: file test2.c, line 17.

        查看断点信息:

            (gdb) info b

                Num     Type           Disp Enb Address            What
                1       breakpoint     keep y   0x00000000000011b4 in main at test2.c:11
                2       breakpoint     keep y   0x00000000000011f1 in main at test2.c:17

            说明:

                NUM: 断点编号
                Disp:断点执行一次之后是否还有效(keep: 有效,dis: 无效)
                Enb: 当前断点是否有效(y: 有效,n: 无效)
                Address:内存地址

            (gdb) q

    2) 使用 tbreak 命令设置断点

        使用 tbreak 命令设置的断点,仅会作用 1 次,程序暂停之后,该断点就会自动消失。

        使用 GDB 启动 test2 程序:

            $ gdb -q test2

                Reading symbols from test...
                (gdb)

        运行 tbreak 命令在指定行设置断点,使用方法是 tbreak 空格 行号。

            (gdb) tbreak 11

                Temporary breakpoint 1 at 0x11b4: file test2.c, line 11.

            (gdb) tbreak 17

                Temporary breakpoint 2 at 0x11f1: file test2.c, line 17.

            (gdb) r

                Starting program: /home/xxx/test2

                Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe228) at test2.c:11
                11          if (argv[1] == NULL) {

            (gdb) c
            
                Continuing.
                Hello world - GDB

                Temporary breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17
                17          testFunc();

            (gdb) c

                Continuing.
                printf i=0
                printf i=1
                printf i=2
                [Inferior 1 (process 12928) exited normally]

            (gdb) info b

                No breakpoints or watchpoints.

            (gdb) q 


    3) 使用 rbreak 命令设置断点

        rbreak 命令的作用对象是 C、C++ 程序中的函数,它会在指定函数的开头位置打断点。

        语法格式:

            (gdb) rbreak regex

            regex 为一个正则表达式,程序中函数名只要满足 regex 条件,rbreak 命令就会在其内部的开头位置打断点。值得一提的是,rbreak 命令打的断点和 break 命令打断点的效果是一样的,会一直存在,不会自动消失。  
        
        使用 GDB 启动 test2 程序:

            $ gdb -q test2

                Reading symbols from test...
                (gdb)

        运行 rbreak 命令在指定行设置断点,使用方法是 tbreak 空格 regex。

            (gdb) rbreak testFunc         

                Breakpoint 1 at 0x1169: file test2.c, line 3.
                int testFunc();

            (gdb) r

                Starting program: /home/xxx/test2
                Hello world - GDB

                Breakpoint 1, testFunc () at test2.c:3
                3       int testFunc() {

                ...

            (gdb) q

    4) 清除断点

        运行 clear 命令删除某一行对应的断点,使用方法是 clear 空格 行号。

            (gdb) clear 11
            (gdb) clear 17
            (gdb) info b

                Deleted breakpoint 2 No breakpoints or watchpoints.


3. 单步调试

    使用 GDB 启动 test2 程序,设置断点并运行到断点:

        $ gdb -q test2

            Reading symbols from test...

            (gdb) b 11

                Breakpoint 1 at 0x11b4: file test2.c, line 11.

            (gdb) b 17

                Breakpoint 2 at 0x11f1: file test2.c, line 17.

            (gdb) r

                Starting program: /home/xxx/test2

                Breakpoint 1, main (argc=1, argv=0x7fffffffe228) at test2.c:11
                11          if (argv[1] == NULL) {


    1) step 命令

        运行 step (简写 s) 命令单步执行,进入 testFunc() 函数:

            (gdb) c

                Continuing.
                Hello world - GDB

                Breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17
                17          testFunc();

            (gdb) s

                testFunc () at test2.c:3
                3       int testFunc() {

            (gdb) s

                4           for (int i=0; i<3; i++ ) {

            (gdb) c

                Continuing.
                printf i=0
                printf i=1
                printf i=2
                [Inferior 1 (process 12944) exited normally]

            (gdb) q


    2) next 命令

        运行 next (简写 n) 命令单步执行,跳过 testFunc() 函数:

            (gdb) c

                Continuing.
                Hello world - GDB

                Breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17
                17          testFunc();

            (gdb) n

                printf i=0
                printf i=1
                printf i=2
                18      }

            (gdb) c

                Continuing.
                [Inferior 1 (process 12950) exited normally]

            (gdb) q 


    3)until 命令

        运行 until (简写 u) 命令:

            (gdb) u

                12              printf("Hello world - GDB\n");

            (gdb) u 17

                Hello world - GDB

                Breakpoint 2, main (argc=1, argv=0x7fffffffe228) at test2.c:17
                17          testFunc();


        注:不带参数的 until 命令,可以使 GDB 调试器快速运行完当前的循环体,并运行至循环体外停止。until 命令并非任何情况下都会发挥这个作用,只有当执行至循环体尾部(最后一行代码)时,until 命令才会发生此作用;反之,until 命令和 next 命令的功能一样,只是单步执行程序。


4. 查看变量

    使用 GDB 启动 test2 程序:

        $ gdb -q test2

            Reading symbols from test...
            (gdb)

    设置断点和运行:

        (gdb) b 11

            Breakpoint 1 at 0x11b4: file test2.c, line 11.

        (gdb) r "message"
        
            Starting program: /home/xxx/test2 "message"

            Breakpoint 1, main (argc=2, argv=0x7fffffffe228) at test2.c:11
            11          if (argv[1] == NULL) {


    运行 print (简写 p) 命令变量的值,使用方法是 p 空格 变量:

        (gdb) p argv[1]

            $1 = 0x7fffffffe509 "message"


    运行 display 命令查看变量或表达式的值,使用方法是 display 空格 变量 (或 display/fmt 空格 变量):

        (gdb) display argv[1]

            1: argv[1] = 0x7fffffffe509 "message"

        (gdb) display/t argv[1]

            2: /t argv[1] = 11111111111111111111111111111111110010100001001

        (gdb) c

            Continuing.
            Hello world - message
            printf i=0
            printf i=1
            printf i=2
            [Inferior 1 (process 12982) exited normally]

        (gdb) r "message2"

            Starting program: /home/xxx/test2 "message2"

            Breakpoint 1, main (argc=2, argv=0x7fffffffe228) at test2.c:11
            11          if (argv[1] == NULL) {
            1: argv[1] = 0x7fffffffe508 "message2"
            2: /t argv[1] = 11111111111111111111111111111111110010100001000


            注:使用 display 命令查看的目标变量或表达式,不仅在执行该命令的同时会看到目标变量的值,后续每次程序停止执行时,GDB 调试器都会将目标变量的值打印出来。

        (gdb) info display

            Auto-display expressions now in effect:
            Num Enb Expression
            1:   y  argv[1]
            2:   y  /t argv[1]

            注:使用 display 命令查看的目标变量或表达式,都会被记录在一张列表(称为自动显示列表)中,通过执行 info dispaly 命令,可以打印出这张表。列表各列的含义:

                (1) Num 列,为各变量或表达式的编号,GDB 调试器为每个变量或表达式都分配有唯一的编号;
                (2) Enb 列,表示当前各个变量(表达式)是处于激活状态还是禁用状态,如果处于激活状态(用 y 表示),则每次程序停止执行,该变量的值都会被打印出来;反之,如果处于禁用状态(用 n 表示),则该变量(表达式)的值不会被打印;
                (3) Expression 列,表示查看的变量或表达式;

        (gdb) disable display 2
        (gdb) info display

            Auto-display expressions now in effect:
            Num Enb Expression
            1:   y  argv[1]
            2:   n  /t argv[1]


            注:可以看到,编号为 2 的 argv[1] 变量的 Enb 由 y 变成了 n。处于禁用状态的变量或表达式,程序停止执行时将不再自动打印出它们的值。

        (gdb) enable display 2
        (gdb) info display

            Auto-display expressions now in effect:
            Num Enb Expression
            1:   y  argv[1]
            2:   y  /t argv[1]


            注:参数 2 表示要激活的变量或表达式的编号,编号的个数可以是多个,表示一次性激活多个变量或表达式。


        参数 fmt 用于指定输出变量或表达式的格式,下表时常用的一些 fmt 参数。

/fmt 描述
/x 以十六进制的形式打印出整数
/d 以有符号、十进制的形式打印出整数
/u 以无符号、十进制的形式打印出整数
/o 以八进制的形式打印出整数
/t  以二进制的形式打印出整数
/f 以浮点数的形式打印变量或表达式的值
/c 以字符形式打印变量或表达式的值


            注:display 命令和 /fmt 之间不要留有空格。以 /x 为例,应写为 (gdb) display/x expr。


posted @ 2023-01-03 14:42  垄山小站  阅读(641)  评论(0编辑  收藏  举报