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 <stdio.h>
#include "rectangle.h"
#include <stdlib.h>
#include <string.h>
//主程序入口
//Author:lailaiji@163.com
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 <stdio.h>
//Author:lailaiji@163.com
//长方形函数定义
typedef struct rect{
   int length;
   int width;
} rect;

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

3、rectangle.c

#include "rectangle.h"
//Author:lailaiji@163.com
//面积计算方法
int area(rect r1){
        return r1.length*r1.width;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335