GDB调试实用技巧

零、GDB用途:
        在完成代码开发或者在编译后,我们关心编译的运行程序是否按预期执行。为了达到这一目标,在长期的代码经验上,我们通常会对运行关键节点按不同输出等级添加日志打印或者进行assert断言。但是日志和断言,无法全部预先埋点好,等到运行结果与预期不一致时,才发现日志输出少了,该断言的地方没有添加断言,此时再通过修改代码来完善日志使得调试效率非常低下,特别是编译语言,每次修改还需要进行编译、链接、打包整个过程。
因此,掌握动态调试技巧是每个开发人员必备的技能之一,而GDB在linux或者类unix环境中,已然成为调试的标准,总的来说,GDB主要帮助你完成下面四个方面的功能:

1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。

说明:本文所涉及到代码在文末体现,使用的c语言进行开发,通过gcc进行开启调试模式 -g 来编译输出:
该程序目的是给定长与宽来计算的长方形的面积
输入:长、宽两个参数
输出:长、宽、面积
运行示例:
./rectangle 10 20
输出:
length:10,width:20.
Area is 200.

一、GDB本地调试:
1、启动相关

  • 1.1:普通启动
$ gdb rectangle
  • 1.2:带参数启动
    方式一:启动时带上参数
$ gdb --args rectangle 10 20

方式二:普通启动后再设置参数

#普通启动
$ gdb rectangle
(gdb) set args 10 20

方式三:运行命令run 时再设置

#普通启动
$ gdb rectangle
(gdb) run 10 20
  • 1.3、检查设置参数与否
(gdb) show args
Argument list to give program being debugged when it is started is "10 20".
  • 1.4、查看当前源代码
(gdb) list

2、断点调试

  • 1.1:对当前文件下断点
    对主程序main.c文件的第8行下断点
(gdb) b 8
  • 1.2:对引用的文件下断点
    对rectangle.c第3行下断点
(gdb) b rectangle.c:3
  • 1.3:通过方法名下断点
    有时我们了解具体函数运行,而不需要想看对应的行数时,可以直接设置该函数名。
    对main函数下断点
(gdb) b main

对 area函数下断点

(gdb) b area
  • 1.4:设置哪些断点
(gdb) info breakpoints 
  • 1.5:清除某些断点
    通过info breakpoints查看左边第一列Num,删除时输入所需删除的num即可,如:
(gdb) delete 1

此时再查看所有断点,发现只有两条了:

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x00000000004006b7 in area at rectangle.c:4
3       breakpoint     keep y   0x000000000040061c in main at main.c:8

3、运行态
设置好上面的断点后,进行启动运行:

(gdb) run
  • 3.1:当前运行到哪里了?
    当我们通过next或者step进行单步操作可能忘记目前运行到哪一行了
(gdb) where

输出:#0 main (argc=3, args=0x7fffffffe408) at main.c:13
即:main.c的第13行

  • 3.2:我是怎么一步步运行到这里的?
    按下c命令,我们来到第二个断点(即area函数断点)
(gdb) bt
#0  area (r1=...) at rectangle.c:4
#1  0x0000000000400691 in main (argc=3, args=0x7fffffffe408) at main.c:18

上述输出意味着调用栈的顺序,由上往下,序号递增:
0序号:当前运行的位置,与where输出一致
1序号:该函数(area)被调用的上一函数位置,即main.c的第18行:"int rect_area = area(r1);"

在运行过程中,也可以继续下断点,如此时我在main.c的19行下断点

b main.c:19
  • 3.3:当前都有哪里可调试变量
    继续按下c命令,来到刚才设置的断点(即main.c:19)
    我们想看一下main函数的上下文变量,通过info locals
(gdb) info locals
length = 10
width = 20
r1 = {length = 10, width = 20}
rect_area = 200

4、修改运行态

  • 4.1:如何查看变量的值
    如:查看rect_area变量
(gdb) p rect_area
$1 = 200

与上面用info locals的rect_area值一致

  • 4.2:我要修改这些变量的值
    有时,我们想修改这个值进行调试,可以使用express命令,如修改rect_area值为300
(gdb) set var rect_area=300

二、GDB远程调试
在实际工作中,我们常常通过给其他机器编译出一个非Debug模式的可执行文件,此时该程序在实际用户环境中产生一些问题,我们除了替换一个Debug模式的可执行文件进行调试外,还可以通过远程调试方式很方便定位问题。
依赖工具:gdb-server
一般系统不自带,进行Yum安装:yum install gdb-gdbserver

说明:
1、通过gcc -o rectangle_release main.c rectangle.c 编译出rectangle_release
2、把rectangle_release发送到实际用户机器(172.16.1.130)上运行

1、被调试机器开启远程调试服务(用户机器)

gdbserver --debug 172.16.1.130:1234 rectangle_release 10 50

上述命令解析:
--debug :开启调试输出
172.16.1.130:1234 : 指的是远程调试服务的端口为1234
rectangle_release 10 20:运行的命令及参数

2、远程机器连接到被调试机器(存在源代码机器)

$ gdb rectangle
(gdb) target remote 172.16.1.130:1234

target remote这个命令是设置远程被调试的机器

3、开始调试吧
设置断点行19

(gdb) break 19

运行 c 命令,注意不是r命令。因为gdbserver已经在运行起来。
查看当前的变量属性

(gdb) info locals
length = 10
width = 50
r1 = {length = 10, width = 50}
rect_area = 500

可以看到这些值是在被调试机器上设置的参数

三、常见问题
1、提示:warning: Source file is more recent than executable.
意味着,当前的源代码比已编译代码有变化

2、远程调试时,使用gdb必须大于 7.8 版本,否则会出现一些异常,如无法查看对应的变量内容

四、命令实用技巧:
1、加载符号文件(适用于符号文件与执行文件分离场景),特别是centos的rpm安装包

file main.debuginfo

2、通过命令的帮助学习更多技巧
如对breakpoints学习:

help breakpoints

五、示例中所使用的代码
代码目录结构:

main.c: 入口程序
rectangle.c:长方形面积计算
rectangle.h:长方形头文件及定义

编译命令,输出可执行文件rectangle:

gcc -g -o rectangle main.c rectangle.c

具体代码:
1、main.c

#include 
#include "rectangle.h"
#include 
#include 
//主程序入口
//Author:[email protected]
int main(int argc, char* args[]){
    if(argc != 3){
            printf("Please input length and width.\nUsage:./rectangle length width.\n");
            exit(1);
    }
    int length = atoi(args[1]);//字符串转换成数字
    int width = atoi(args[2]);//字符串转换成数
    printf("length:%d,width:%d.\n", length, width);
    rect r1 ;
    r1.length = length;
    r1.width = width;
    int rect_area = area(r1);
    printf("Area is %d.\n",rect_area);
    return 0;
} 

2、rectangle.h

#include 
//Author:[email protected]
//长方形函数定义
typedef struct rect{
   int length;
   int width;
} rect;

//面积计算
int area(rect r1);

3、rectangle.c

#include "rectangle.h"
//Author:[email protected]
//面积计算方法
int area(rect r1){
        return r1.length*r1.width;
}

你可能感兴趣的:(GDB调试实用技巧)