写这篇文档的目的是对前面GDB的知识做一次总览,本文为GDB调试指南,参考GDB调试手册,目前已有的篇目:

启动调试
断点设置
查看源码
单步调试
查看变量

前言

GDB是Linux下非常好用且强大的调试工具。GDB可以调试C、C++、Go、java、 objective-c、PHP等语言。对于一名Linux下工作的c/c++程序员,GDB是必不可少的工具,本篇以C语言来调试。

GDB简介

UNIX及UNIX-like下的调试工具。虽然它是命令行模式的调试工具,但是它的功能强大到你无法想象,能够让用户在程序运行时观察程序的内部结构和内存的使用情况。

一般来说,GDB主要帮助你完成下面四个方面的功能:

1、按照自定义的方式启动运行需要调试的程序。
2、可以使用指定位置和条件表达式的方式来设置断点。
3、程序暂停时的值的监视。
4、动态改变程序的执行环境。

基本命令的操作

GDB中的命令很多,但我们只需掌握其中十个左右的命令,就大致可以完成日常的基本的程序调试工作。


gdb命令拥有较多内部命令。在gdb命令提示符“(gdb)”下输入“help”可以查看所有内部命令及使用说明。


判断文件是否带有调试信息

要调试C/C++的程序,首先在编译时,要使用gdb调试程序,在使用gcc编译源代码时必须加上“-g”参数。保留调试信息,否则不能使用GDB进行调试。

有一种情况,有一个编译好的二进制文件,你不确定是不是带有-g参数,带有GDB调试,这个时候你可以使用如下的命令验证:


如果没有调试信息,则会出现:

Reading symbols from /home/minger/share/tencent/gdb/main…(no debugging symbols found)…done.

/home/minger/share/tencent/gdb/main是程序的路径。

如果带有调试功能,下面会提示:

Reading symbols from /home/minger/share/tencent/gdb/main…done.

说明可以进行GDB调试。

还有使用命令readlef查看可执行文件是否带有调试功能

readelf -S main|grep debug


如果有debug说明有调试功能,如果没有debug。说明没有带有调试功能,则不能被调试。

开始进入正题,GDB启动调试。

调试方式启动运行无参程序

以下是linux下GDB调试的一个实例,先给出一个示例用的小程序,C语言代码:
main.c

#include <stdio.h>void Print(int i){printf("hello,程序猿编码 %d\n", i);
}int main(int argc, char const *argv[]){int i = 0;for (i = 1; i < 3; i++){Print(i);}return 0;
}

编译:

gcc -g main.c -o main

下面“gdb”命令启动GDB,将首先显示GDB说明,不管它:


上面最后一行“(gdb)”为GDB内部命令引导符,等待用户输入GDB命令。

下面使用“file”命令载入被调试程序 main(这里的 main 即前面gcc 编译输出的可执行文件):

如果最后一行提示Reading symbols from /home/minger/share/tencent/gdb/main…done. 表示已经加载成功。

下面使用“r”命令执行(Run)被调试文件,因为尚未设置任何断点,将直接执行到程序结束:


调试启动带参程序

假设有以下程序,启动时需要带参数:

#include <stdio.h>int main(int argc, char const *argv[]){if (1 >= argc){printf("usage:hello name\n");return 0;}printf("hello,程序猿编码 %s\n", argv[1]);return 0;
}

编译:

gcc -g test.c -o test

这种情况如何启动调试呢?只需要r的时候带上参数即可。


调试core文件

Core Dump:Core的意思是内存,Dump的意思是扔出来,堆出来(段错误)。开发和使用Unix程序时,有时程序莫名其妙的down了,却没有任何的提示(有时候会提示core dumped),这时候可以查看一下有没有形如core.进程号的文件生成,这个文件便是操作系统把程序down掉时的内存内容扔出来生成的, 它可以做为调试程序的参考,能够很大程序帮助我们定位问题。那怎么生成Core文件呢?

生成Core方法

产生coredump的条件,首先需要确认当前会话的ulimit –c,若为0,则不会产生对应的coredump,需要进行修改和设置。


即便程序core dump了也不会有core文件留下。我们需要让core文件能够产生,设置core大小为无限:

ulimit -c unlimied


更改core dump生成路径

因为core dump默认会生成在程序的工作目录,但是有些程序存在切换目录的情况,导致core dump生成的路径没有规律,

所以最好是自己建立一个文件夹,存放生成的core文件。

我建立一个 /data/coredump 文件夹,在根目录data里的coredump文件夹。


调用如下命令:

echo /data/coredump/core.%e.%p> /proc/sys/kernel/core_pattern

将更改core文件生成路径,自动放在这个/data/coredump文件夹里。

%e表示程序名, %p表示进程id


测试代码:


/*
#include <stdio.h>int main(int argc, char const *argv[]){if (1 >= argc){printf("usage:hello name\n");return 0;}printf("hello,程序猿编码 %s\n", argv[1]);return 0;
}*/#include <stdio.h>int main(int argc, char const *argv[])
{int i = 0;scanf("%d",i);printf("hello,程序猿编码 %d\n",i );return 0;
}

编译运行:


运行后结果显示段错误,该程序在主函数内部scanf的时候回崩溃,i前面应该加上&。

这个时候,进入/data/coredump文件夹可以查看生成的core


然后用gdb调试该core,命令为 gdb core.test.3591 ,显示如下


program terminated with signal 11 告诉我们信号中断了我们的程序,发生了段错误。

这个时候可以敲命令 backtrace(bt) 查看函数的调用的栈帧和层级关系。

这个一堆问号很多人遇到过,网上有些人说是没加载符号表,有人说是标准glibc版本不一致,不纠结这个问题。

可以通过如下命令调试:

gdb 可执行程序exe
进入gdb环境后
core-file core的名字
敲命令bt可以查看准确信息。

gdb 可执行程序


进入gdb环境后,core-file core的名字


敲bt命令,这是gdb查看back trace的命令,查看函数的调用的栈帧和层级关系。


可以看到最近的栈中存储的是调用了IO操作,可以看到main函数的26行出错。

到此为止,就是core文件配置生成和调试方法。

总结

至此,我们GDB启动调试方式完毕,和core文件配置生成和调试方法。后续接着讲断点设置、单步调试等。本人能力有限,欢迎留言补充。

断点设置与查看源码

前言

上篇GDB启动调试我们讲到了GDB启动调试的多种方式,在 Linux 环境软件开发中,GDB 是主要的调试工具,用来调试 C 和 C++ 程序。这一篇主要讲GDB的断点设置与查看源码。

为什么要设置断点呢?

当我们想查看变量内容,堆栈情况等等,可以指定断点。程序执行到断点处会暂停执行。break 命令用来设置断点,缩写形式为b。设置断点后,以便我们更详细的跟踪断点附近程序的执行情况。

设置断点有很多方式。下面我们举例说明下常用的几种方式。

通过行号设置断点

格式:

break [行号]

break 行号,断点设置在该行开始处,注意:该行代码未被执行

如果你的程序是用c或者c++写的,那么你可以使用“文件名:行号”的形式设置断点。示例如下:

//test.c
#include <stdio.h>void judge_sd(int num){if ((num & 1) == 0){printf("%d is even\n",num);return;}else{printf("%d is odd\n",num);return;}
}int main(int argc, char const *argv[]){judge_sd(0);judge_sd(1);judge_sd(4);return 0;
}

编译:

gcc -g test.c -o test


gdb test

break 文件名 : 行号,适用于有多个源文件的情况。

示例中的(gdb) b test.c:18是设置了断点。断点的位置是test.c文件的18行。使用r命令执行脚本时,当运行到18行时就会暂停。注意:该行代码未被执行

通过函数设置断点

格式:

break [函数名]

break 函数名,断点设置在该函数的开始处,断点所在行未被执行:

同样可以将断点设置在函数处:

b judge_sd


设置条件断点

如果按上面的方法设置断点后,每次执行到断点位置都会暂停。有时候非常讨厌。我们只想在指定条件下才暂停。这时候根据条件设置断点就有了用武之地。设置条件断点的形式,就是在设置断点的基本形式后面增加 if条件。示例如下:

break test.c:6 if num>0


当在num>0时,程序将会在第6行断住。

查看断点

语法:

info breakpoints

可以使用info breakpoints查看断点的情况。包含都设置了那些断点,断点被命中的次数等信息。示例如下:


它将会列出所有已设置的断点,每一个断点都有一个标号,用来代表这个断点。

删除断点

语法:

delete breakpoint

对于无用的断点我们可以删除。删除的命令格式为 delete breakpoint 断点编号。info breakpoint命令显示结果中的num列就是编号。删除断点的示例如下:


查看源码

断点设置完后,当程序运行到断点处就会暂停。暂停的时候,我们可以查看断点附近的代码。查看代码的子命令是list,缩写形式为l。


指定行号查看代码

语法:

list first,last

例如,要列出6到21行之间的源码:

列出指定文件的源码

前面执行l命令时,默认列出test.c的源码,如果想要看指定文件的源码呢?可以

list 【文件名加行号或函数名】


总结

本文介绍了GDB调试中的断点设置、源码查看。断点设置可以便于我们后期观察变量,堆栈等信息,为进一步的定位与调试做准备。源码查看可以通过指定行号或者方法名来查看相关代码。

单步调试与查看变量

前言

前面两篇已经对GDB启动调试,GDB调试断点设置与查看源码我们已经了解了GDB基本的启动调试,设置断点,查看源码等,如果这些内容你还不知道,建议先回顾一下前面的内容。

断点附近的代码你了解后,这时候你就可以使用单步执行一条一条语句的去执行。可以随时查看执行后的结果。接下来你可能会想知道程序运行的一些情况,就需要查看变量的值。下面介绍单步调试与设置变量。

单步调试

居然是调试代码,还是老规矩,先上代码:

//test.c
#include <stdio.h>void judge_sd(int num){if ((num & 1) == 0){printf("%d is even\n",num);return;}else{printf("%d is odd\n",num);return;}
}int main(int argc, char const *argv[]){judge_sd(0);judge_sd(1);judge_sd(4);return 0;
}

编译:

gcc -g test.c -o test

程序的功能比较简单,这里不多做解释。断点附近的代码你了解后,这时候你就可以使用单步执行一条一条语句的去执行。可以随时查看执行后的结果。单步执行有两个命令,分别是step和next。我们可能打了多处断点,或者断点打在循环内,这个时候,可以使用continue命令。这三个命令的区别在于:

1、next命令(可简写为n)用于在程序断住后,继续执行下一条语句。
2、step命令(可简写为s),它可以单步跟踪到函数内部。
3、continue命令(可简写为c)或者fg,它会继续执行程序,直到再次遇到断点处。

单步进入-step

step 一条语句一条语句的执行。它有一个别名,s。它可以单步跟踪到函数内部。

先用list(可简写为l)将源码列出来,例如:

先启动调试,然后把源码列出来。


从上面的过程可以看到,在5行设置断点,运行程序,可见,step命令进入到了被调用函数中judge_sd。使用step命令也会在这个方法中一行一行的单步执行。但是如果没有该函数源码,需要跳过该函数执行,可使用finish命令,继续后面的执行。

单步执行-next

next命令示例:


next命令(可简写为n)用于在程序断住后,继续执行下一条语句。上面的信息在5行处打断点,然后运行到6行,然后输入 运行n 2,则会单步执行两行。可见,使用next命令只会在本方法中单步执行。

继续执行到下一个断点-continue

我们可能打了多处断点,或者断点打在循环内,这个时候,想跳过这个断点,甚至跳过多次断点继续执行该怎么做呢?可以使用continue命令。它的作用就是从暂停处继续执行。命令的简写形式为c。继续执行过程中遇到断点或者观察点变化依然会暂停。示例代码如下:

跳过执行–skip


根据上面的信息可以看到,使用skip之后,将不会进入judge_sd函数。好处就是skip可以在step时跳过一些不想关注的函数或者某个文件。

如果想删除skip,使用skip delete [num] 。

查看变量

现在你已经会设置断点,查看断点附近的代码,并可以单步执行和继续执行。接下来你可能会想知道程序运行的一些情况,如查看变量的值。print命令正好满足了你的需求。以帮助我们进一步定位问题。

格式:

print[变量名]

print(可简写为p)打印变量内容。示例代码如下:

//test.c
#include <stdio.h>
#include <stdlib.h> //malloc,free,randint main(int argc, char const *argv[])
{int input;int i ;printf("Please enter the length of the string:");scanf("%d",&input);char *buf = (char *) malloc(input + 1);//字符最后包含'\0'if (buf == NULL){printf("malloc failed!\n");return -1;}//随机生成字符串for ( i = 0; i < input; i++){buf[i] = rand()%26 +'a';}buf[i] = '\0';printf("A randomly generated string: %s\n",buf);free(buf);return 0;
}

编译:

gcc -g test.c -o test

先用list(可简写为l)将源码列出来,例如:


print命令的简写形式为p,使用它打印出变量的值。

打印出的变量i的值为80。

当然,多个函数或者多个文件会有同一个变量名,这个时候可以在前面加上文件名或者函数名来区分:

p 'testfile.c'::i
p 'sum'::i

在看看指针。


注意到了没有,如果使用上面的方式打印指针指向的内容,那么打印出来的只是指针地址而已。那怎么打印出指针指向的内容呢?

需要解引用,如下:


仅仅使用*只能打印第一个值,如果要打印多个值,后面跟上@并加上要打印的长度。
或者@后面跟上变量值:如下:


另外值得一提的是,$可表示上一个变量,在调试链表时时经常会用到的,它有next成员代表下一个节点,则可使用下面方式不断打印链表内容,举例:

p *linkNode  #这里显示linkNode节点内容
p *$.next #这里显示linkNode节点下一个节点的内容

设置变量

使用print命令查看了变量的值,如果感觉这个值不符合预期,想修改下这个值,再看下执行效果。这种情况下,我们该怎么办呢?通常情况下,我们会修改代码,再重新执行代码。使用gdb的set命令,一切将变得更简单。

set命令可以直接修改变量的值。

设置观察点

设置观察点的作用就是:当被观察的变量发生变化后,程序就会暂停执行,并把变量的原值(Old)和新值(New)都会显示出来。设置观察点的命令是watch。

watch num

这个时候,让程序继续运行,如果num的值发生变化,则会打印相关内容,如:

Hardware watchpoint 3: num
Old value = 1
New value = 10

总结

通过上面的例子演示,我相信读者已经对于通过GDB调试C/C++程序有了基本的理解,如果你想获取更多的调试技巧请参考官方网站的GDB调试手册,还有GDB官方网站的手册。

参考:GDB TutorialA Walkthrough with Examples


欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料,网盘资料有如下:

GDB调试指南(入门,看这篇够了)相关推荐

  1. groovy if 判断字符串_Groovy快速入门看这篇就够了

    原标题:Groovy快速入门看这篇就够了 来自:刘望舒(微信号:liuwangshuAndroid) 前言 在前面我们学习了和两篇文章,对Gradle也有了大概的了解,这篇文章我们接着来学习Groov ...

  2. React入门看这篇就够了

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所 ...

  3. 【软件开发底层知识修炼】十九 GDB调试从入门到熟练掌握超级详细实战教程学习目录

    本文记录之前写过的5篇关于GDB快速学习的文章,从第一篇开始学习到最后一篇,保证可以从入门GDB调试到熟练掌握GDB调试的技巧. 学习交流加 个人qq: 1126137994 个人微信: liu112 ...

  4. GDB调试指南-单步调试

    前言 前面通过<启动调试>,<断点设置>,<变量查看>,我们已经了解了GDB基本的启动,设置断点,查看变量等,如果这些内容你还不知道,建议先回顾一下前面的内容.在启 ...

  5. .NET Core实战项目之CMS 第五章 入门篇-Dapper的快速入门看这篇就够了

    写在前面 上篇文章我们讲了如在在实际项目开发中使用Git来进行代码的版本控制,当然介绍的都是比较常用的功能.今天我再带着大家一起熟悉下一个ORM框架Dapper,实例代码的演示编写完成后我会通过Git ...

  6. Vue开发入门看这篇文章就够了

    摘要: 很多值得了解的细节. 原文:Vue开发看这篇文章就够了 作者:Random Fundebug经授权转载,版权归原作者所有. 介绍 Vue 中文网 Vue github Vue.js 是一套构建 ...

  7. 动态规划入门看这篇就够了,万字长文!

    今天是小浩算法 "365刷题计划" 动态规划 - 整合篇.大家应该期待已久了吧!奥利给! 01 PART 动态规划是啥 我们把要解决的一个大问题转换成若干个规模较小的同类型问题,当 ...

  8. java 调试 gdb_android gdb 调试实例演示(有源代码篇)

    android ndk代码的调试本身还是有点麻烦的,因为本身google android的sdk 主要是面向广大的java程序员的,所以后来发布的 ADT 集成开发环境对java的代码调试 支持还是很 ...

  9. 【软件开发底层知识修炼】十四 快速学习GDB调试一 入门使用

    前面几篇文章学习了链接器相关的内容.现在开始来学习GDB调试.我们的目的是通过这几篇文章将GDB调试完全学会. 文章目录 1 为什么需要GDB 2 GDB 的常规应用 3 GDB调试程序实例 4 总结 ...

最新文章

  1. HTML5 虚拟键盘出现挡住输入框的解决办法
  2. python 制作gif-利用Python如何制作好玩的GIF动图详解
  3. Balluff推出刀具识别系统
  4. MySQL数据库优化技巧大全
  5. 鸿蒙系统替代安卓,华为鸿蒙2.0可以替代安卓吗,华为鸿蒙2.0优势在哪
  6. Django基础核心技术之Model模型的介绍与设计
  7. 51nod 正整数分组
  8. mysql 崩溃恢复_超详细的MySQL数据库InnoDB崩溃恢复机制总结
  9. 让你开回家过年!特斯拉计划春节前开始交付国产Model 3
  10. 同等质量下那种图片格式小_最实用的Window小工具合集,总有一款适合你!
  11. 一拖再拖忍无可忍,谷歌披露影响开发人员的 GitHub 高危0day漏洞
  12. matlab给图像加云,matlab怎么给图像加雾
  13. oracle如何判断奇数偶数_图解面试题:如何分析中位数?
  14. 《大数据之路:阿里巴巴大数据实践》-第1章 总述
  15. excel常用函数之截取指定字符
  16. 掌握 3 个搜索技巧,在 GitHub 上快速找到实用软件资源
  17. text to image(八):《Image Generation from Scene Graphs》
  18. 上岸重庆邮电大学软件工程学院学硕总结
  19. Docker Nginx 如何重新加载配置
  20. 管家婆商品库存盘点功能

热门文章

  1. #51CTO学院四周年#我在51cto学院中成长的故事
  2. 会计学03.会计计量属性与会计等式
  3. java基础语法day20(Map、模拟斗地主发牌)
  4. Properties和IO流集合的方法
  5. C# StringBuilder 的使用
  6. sqlite3数据库API-执行sql语句(三)
  7. mybatis源码分析4 - sqlSession读写数据库完全解析
  8. html label标签
  9. 音视频测试资源和工具tool
  10. 错误跟踪系统Sentry到底是何方神圣?