1.GDB

GDB全称GNU symbolic debugger,是LinuxC/C++开发人员需要熟练使用的调试工具。使用GDB调试程序有两种方式:第一种是调试未运行的程序;第二种是调试正在运行的程序;

1.GDB调试未运行的程序

1.启动gdb这个调试程序前需要做的事情
  1. 示例程序如下:
#include <stdio.h>

int main(int argc,const char* args[])
{
    int a = 100;
    float b = 200.0;
    printf("HelloWorld\n");
    printf("%f\n",a + b);
    int c = argc;
    const char* str1 = args[1];
    const char* str2 = args[2];
    printf("%d%s%s\n",c,str1,str2);
    printf("%s\n",args[0]);
    return 0;
}
  1. 在编译成目标可执行程序时加上-g选项,就会在生成目标可执行程序文件中加入调试信息。注意:在生成调式程序时,一般会关闭编译器程序的优化选项(使用O0选项,O0表示关闭编译器优化)。
gcc -o result.exe -g example.c
  1. 需要发布在生产环境时移除某个程序中的调式信息
strip result.exe
  1. 启动GDB调式工具: gdb 可执行程序名
gdb result.exe

# 传参
gdb --args result.exe 参数
2.gdb直接调试程序的一些命令
  1. 在gdb调试程序里运行可执行程序:输入run或者r即可,即r[un]。程序会运行到断点的位置停下来
(gdb) run
##或者
(gdb) r

2.start:开启分步调试,停留在main函数

(gdb) start
Temporary breakpoint 1 at 0x40056c: file example.c, line 5.
Starting program: /home/xiaocer/练习/include/result.exe 

Temporary breakpoint 1, main (argc=1, args=0x7fffffffe4f8) at example.c:5
  1. n[xet]: 执行下一条语句,如果该语句为函数调用,则不会进入函数内部执行。类似于visual studio中的F10快捷键
  2. s[tep]: 执行下一条语句,可以进入函数内部,但是库函数或者第三方提供的函数不能进入,因为没有源码。类似于visual studio中的F11快捷键
  3. 设置主函数启动的参数:
    1. set args xxx1 xxx2
    2. run xxx1 xxx2。run xxx1 xxx2也可以设置程序启动的参数
    3. set args[1] = "xxx1" set args[2] = "xxx2";args[0]的值默认是可执行程序名。
    4. 启动GDB调式工具时传参
    # 传参
    gdb --args result.exe 参数
    
  4. 显示源文件代码
    1. l[ist]:查看主函数的源码。默认只显示主函数所在文件的十行,需要显示更多的话敲回车。
    2. l[ist] xxx.c:行号:查看某个文件从指定的行号开始显示十行
  5. 设置断点调试:
    1. 指定行号加断点:b[reak] 行号,默认在主函数所在文件的行号
    2. 指定函数名加断点:b[reak] 函数名
    3. 指定文件对应的行:b[reak] xxx.c:行号
    4. 设置条件断点:b[reak] 行号 条件
  6. 查看已经设置好的断点信息:i[nfo] b[reak],这样得到断点的信息
  7. 删除断点:d[el] number
  8. 跳到下一断点或者说让程序继续运行: c[ontinue]
  9. 输出/修改变量的值:p[rint] 变量名/p[rint] 变量名=值
  10. ptype 变量名: 打印变量的类型
  11. 跟踪某个变量的值:display 变量名.
  12. 取消跟踪某个变量:undisplay
    number,查看number info display。
  13. 调试时设置参数
    1. 设置变量的值:set var 变量名=变量值
    2. gdb 程序名称 --args 参数名 参数值
  14. 查看当前线程的调用堆栈:使用bt或者backtrace命令
  15. 退出gdb这个调试程序:使用q[uit]即可退出gdb调试程序
  16. 跳到指定堆栈:f 堆栈编号n或者frame n
  17. 禁用断点:disable 断点编号,不加断点编号表示禁用所有断点,下同
  18. 启用断点:enable 断点编号
  19. 查看代码:list。参见6
  20. 启动调试后,让程序中断下来:CTRL + c
  21. 进入图形界面:ctrl + x +a,图像界面里可以查看代码
  22. 运行到指定的代码行:until 行号或者u 行号
  23. 结束当前函数调用,回到上一层函数调用的地方:finish
  24. 立即结束当前函数调用并且可以返回指定值回到上一层函数调用的地方,如果函数内部还有未执行完毕的代码也不会执行:return
  25. 查看设置的命令行参数:show args
  26. 查看传入函数的参数:info args

2.GDB调试正在运行的程序

1.附加进程进行调试
  1. 我们的一个程序已经启动运行,现在想要调试这个程序,但是不想重启程序。这时候只需要使用gdb attach 程序进程ID将gdb调试器附加到我们的程序中。
  2. 使用以下命令获取我们的程序的进程id
ps -ef | grep 运行的程序名
  1. 结束调试,并且不会对运行着的程序有影响:(gdb)detach

3.GDB调试core文件

1.gdb跟踪core文件
查看系统参数:ulimit -a,下图中core file size的0表示Linux系统默认是不开启在程序崩溃时产生core文件这个机制的。
image.png
  • 查看core文件的大小:
  • ulimit -c
    0 #输出为0表示关闭生成core文件
    
    1. 设置生成core的大小
    ##设置core文件大小1024
    ulimit -c 1024
    ##设置core文件大小无限制
    ulimit -c unlimited
    
    1. 让设置生成core文件的大小永久生效,有以下方式:
      1. 在/etc/security/limits.conf增加一行
      * soft core unlimited
      
      1. 在/etc/profile文件中增加ulimit -c unlimited,然后使用source /etc/profile让修改立即生效
    2. 设置core文件的保存位置和文件名称格式
      1. 常用文件名称格式如下:
        1. %p:将进程pid添加到core文件名中
        2. %u:将当前 uid 到 core 文件名中
        3. %g:将当前 gid 到 core 文件名中
        4. %s:将导致产生 core 的信号到core文件名中
        5. %t:将core 文件生成时间(UNIX)到 core 文件名中
        6. %h:添加主机名到 core 文件名中
        7. %e:添加程序名到 core 文件名中
      2. 示例:core文件保存到/var/crash/目录下
      # 文件名称格式为core.%u.%e.%p
      # core文件保存到/var/crash/目录下
      sysctl -w kernel.core_pattern=/var/crash/core.%u.%e.%p
      
    3. 调试core文件: gdb 可执行文件名 core文件名

    4.GDB调试多进程/多线程程序

    1.调试多进程服务程序
    1. 调试父进程:set follow-fork-mode parent (默认的)
    2. 调试子进程:set follow-fork-mode child
    3. 设置调试模式:set detach-on-fork [on|off]
      1. 默认是on,表示调试当前进程的时候其他的进程继续运行
      2. 如果使用off,调试当前进程的时候其他的进程被gdb挂起
    4. 查看调试的进程:info inferiors
    5. 切换调试的进程:inferior 进程id
    2.调试多线程服务程序
    1. shell中执行
      1. 在shell中查看当前运行的进程:ps aux | grep 可执行程序名
      2. 在shell中查看当前运行的轻量级进程:ps -aL | grep 可执行程序名
      3. 在shell中查看主线程和新线程的关系:pstree -p 主线程id
    2. gdb中执行
      1. 查看线程:info threads
      2. 切换线程:thread 线程编号或者t 线程编号
      3. 只运行当前的线程:set scheduler-locking on
      4. 运行全部的线程:set scheduler-locking off
      5. 指定某个线程执行某gdb命令:thread apply 线程id cmd
      6. 全部的线程执行某gdb命令:thread apply all cmd

    综上:调试一个程序必备命令

    1. gdb 可执行程序名
    2. 打断点:b 行号或者break 行号
    3. 程序运行到第一个断点处:r或者run
    4. 启动调试后,让程序中断下来:CTRL + c
    5. 执行下一条语句:n或者next
    6. 进入自定义函数内部:s或者step
    7. 打印变量的值:p 变量名或者print 变量名
    8. 查看断点信息:i b或者info break
    9. 启用/禁用/删除断点:enable/disable/delete number,number为info break命令显示的断点编号
    10. 查看当前线程的调用堆栈:bt或者backtrace
    11. 运行到下一个断点处:c或者continue
    12. 退出调试:quit或者q
    13. 查看项目中的线程:info threads
    14. 切换到指定的线程:thread 线程编号或者t n
    15. 查看指定堆栈的信息:frame 堆栈编号或者f n

    2.CGDB

    1.安装
    1. Ubantu上安装:sudo apt-get install cgdb
    2.基本使用
    1. 切换至代码窗口:Esc
    2. 切换到gdb窗口:i