GDB调试

GDB调试原理是通过在源代码进行gcc|g++编译过程加入-g参数创建符号表,符号表包含了程序中使用的变量名称的列表, 同时关闭所有的优化机制,以便程序执行过程中严格按照原来的源代码进行。

一、开启方式

如果GDB调试未开启GDB的程序,会因为找不到符号表无法继续:

Reading symbols from ./mainx...(no debugging symbols found)...done.
(gdb) list
No symbol table is loaded.  Use the "file" command.
(gdb)

那么,我们如何在程序的编译过程开启GDB的调试模式呢?

  1. gcc 模式

    $ gcc -g -o main main.c
    

    通过加入 -g 参数启用GDB调试.

  2. make 模式
    make模式旨在根据Makefile文件的内容进行编译工作,要开启GDB调试,在Makeifle文件中加入CFLAGS = -g则可

    CFLAGS = -g
    main:main.o
      gcc -o {$CFLAGS} main main.c
    
  3. cmake 模式

    # cmake最低版本需求,不加入此行会受到警告信息
    CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
    # 项目名称
    PROJECT(MAIN)
    # 生成应用程序 hello (在windows下会自动生成hello.exe)
    ADD_EXECUTABLE(main main.c)
    # 设置GDB调试支持
    set(CMAKE_BUILD_TYPE "Debug")
    set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
    set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
    

二、用法

2.1 启停程序

  • 加载符号表
    在通过以上编译得来的应用程序,则可以正式进行GDB调试了. gdb main GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 ... Reading symbols from main...done. (gdb)

如果应用程序在编译时未添加-g参数,则会提示"Reading symbols from ./mainx...(no debugging symbols found)...done.",从而无法继续进行GDB调试。
gdb main 只是加载了调试程序的符号表,并没有启动程序。

  • 启动调试
    进入gdb控制台后,输入run命令,则可以启动调试。

    (gdb) run
    Starting program: /home/vagrant/obj2/main
    

    此时如果未添加任何断点,程序会一直执行下去,直到正常退出。因此,我们需要在启动前添加断点。 当然除此之外,我们可以使用start命令。

    (gdb) start
    Temporary breakpoint 3 at 0x4007de: file /home/vagrant/obj2/main.c, line 3.
    Starting program: /home/vagrant/obj2/main
    

    start 命令是自动为程序的主函数添加一个断点。并在主函数处停顿。

  • 中止调试
    调试启动后,可以使用kill或者k来启动调试

    (gdb) k
    Kill the program being debugged? (y or n) y
    

    如果程序是通过run启动,或无断点停顿,程序在前端运行是无法通过kill中止的, 只能通过ctrl+c强制中止,这和tty端运行程序是一个道理。 只有存在断点停顿,系统才会有人机交互的可能。

  • 查看调试状态

    info program                 // 查看当前程序运行情况
    info stack                   // 查看堆栈使用情况
    

2.2 断点管理

  • 添加断点,使用b或者break:
    break main // 给指定的函数添加断点 break main.c:main // 给main.c文件中的main函数添加断点 break 10 // 在第10行添加断点 break main.c:10 // 给main.c文件中的第10行添加断点 break 0x00000000004007e8 // 给指定的内存地址添加断点 break +1 // +偏移量(目前还不知道怎么用) break -1 // -偏移量(目前还不知道怎么用) break 10 if value==9 // 给断点添加触发条件,如第10行,当vlaue=9时才会触发 * 查看断点 info b // 或者info break * 给已添加的断点添加触发条件 condition 1 if index=2 // 给断点ID为1的断点添加条件,condition 断点编号:除触发条件 * 删除断点 delete 1 // 删除断点ID为1的断点 delete // 删除所有断点 clear 10 // 删除第10行断点(clear不能删除指定断点ID的断点) clear main // 删除main函数的断点 crear main.c:10 // 删除文件main.c的第10行断点 clrer main.c:main // 删除文件main.c的main函数断点 * 断点启停 disable 1 // 停用断点ID为1的断点 enable 1 // 启用断点ID为1的断点 enable once 1 // 启用断点ID为1的断点一次后停用
  • 断点控制 step(s) // 单步进入,遇到子函数会进入子函数内部。 next(s) // 下一步,按代码顺序执行,遇到子函数不会进入内部。 continue(c) // 继续执行,直到下一个断点 return 返回值 // 单步进入子函数内部时,可提前通过此函数中止子函数,并返回指定的返回值 call + 函数 // 在当前位置调用指定函数 until(u) + 行号 // 跳到当前执行文件的第n行。
  • 变量监测
    在调试的过程,我们可能想要知道随着调试过程的变动,某些关键变量的值是如何变化的。 display 变量 // 将变量加入监控面板,每次断点停顿时都会显示监控面板的所有变量信息。 info display // 查看监控面板中变量信息(包含变量的编号) delete display 编号 // 删除监控面板中指定变量编号的变量 enable display 编号 // 启用监控面板中的指定变量 disable display 编号 // 停用监控面板中的指定变量 undisplay 编号 // 与`delete display 编号`效果一致 whatis 变量 // 查看变量类型 print(p) 变量 // 打印指定变量 set key=value // 修改变量值 > 在使用print命令时,可以对变量按指定格式进行输出,其命令格式为print /变量名 + 格式,其中常用的变量格式:x:十六进制;d:十进制;u:无符号数;o:八进制;c:字符格式;f:浮点数。