网络上已经有很多gdb调试的文章了,为什么我还要写这篇文章呢,因为本文是写给gdb新手的,目的就是通过一个简单的例子来让新手很快上手。一旦上手入门了,其他的问题就可以自己去搜索搞定了。右边是gdb的Logo,为啥是条吹泡泡的小鱼呢?我也不懂啊。

 
本文的例子基于以下代码 main.c:

 #include <stdio.h> 
2
void print_str(char* str){
printf("%s", str);
str[] = 'a';
printf("\n");
}
int main(){
char* str = "hello, world!"; print_str(str); return ;
}
编译:

gcc -g main.c -o main -Wall -O0
以上gcc各个选项的解释如下,也可以用 man gcc 来查看完整帮助:
     -g Produce debugging information in the operating system's native format (stabs, COFF, XCOFF, or DWARF 2).  GDB can work with this debugging
information. 简单来说就是在生成的可执行程序中加上gdb调试用的信息,如代码的行号和机器码的对应关系,符号表等信息。没有这类信息,在gdb里是看不到程序的函数名和变量名的
-o file
Write output to file. 定义输出文件的名称
-Wall Turns on all optional warnings which are desirable for normal code. 打开所有的警告信息,可以帮助我们通过warning来发现bug
-O 控制优化的级别, -O0Do not optimize. This is the default. 代码经过优化后,可能出现机器码和源代码对不上的问题。
运行:
./main
Segmentation fault

程序出错啦,段错误,一般是访问越界或者是尝试修改只读属性的内存造成的。

此时,可以用两种方法来调试这个程序:

1. 用gdb启动程序并调试

语法:gdb /path/program-name

本例子中,应该执行:
gdb ./main
输出如下:
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.-.el5)
Copyright (C) Free Software Foundation, Inc.
License GPLv3+: GNU GPL version or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/csd/main...done.
(gdb)

此时进入了gdb的命令行,跟shell一样,提供自动补全等功能,所以如果不计得命令的全称时,可以按tab键补全。

此时用 l [list] 命令(l是命令的简写,方括号中是命令的全称)来显示源码,也可以 l linenumber 来显示指定行号处的源码,也可以用 l 命名空间::类名::函数名来显示指定的函数,可以按tab键补全哦

(gdb) l
#include <stdio.h> void print_str(char* str){
printf("%s", str);
str[] = 'a';
printf("\n");
}
int main(){
char* str = "hello, world!";

可以看到默认显示10行源代码,想要继续看10行之后的代码,可以继续用 l 命令,也可以直接按回车,会重复上次的命令

。如果觉得显示的源代码行数不够多,可以用 set listsize number 来设置一次显示源代码的行数。设置完成后,可以用 show listsize 命令来查看设置之后 listsize 变量的值。

此时我们想在程序开始处加断点,使用 b [break] linenumber 命令在指定行处设置断点,也可以用 b 命名空间::类名::函数名来在指定的函数处设置断点

(gdb) b 9
Breakpoint 1 at 0x400522: file main.c, line 9.
 
此时,断点设置好了,gdb认为是断点1。 我们用 r [run] 命令来启动程序
(gdb) r
Starting program: /home/csd/main Breakpoint 1, main () at main.c:9
9 char* str = "hello, world!";
(gdb)

此时程序停止在了断点1处,如果我们想看str的值怎么办?用 p [print] 变量名 来查看变量的值

(gdb) p str
$ = 0x0
(gdb)

此时还没有执行给str赋值的操作。然后用 n [next] 来单步执行下一条语句,然后再打印str的值,可以看到str已经被准确初始化了。

(gdb) n
print_str(str);
(gdb) p str
$ = 0x40063b "hello, world!"
(gdb)

此时程序将要执行第11行,print_str函数,我们想单步进入这个函数,可以用 step 命令

(gdb) step
print_str (str=0x40063b "hello, world!") at main.c:
printf("%s", str);
(gdb)

可以看到程序将要执行main.c文件中的print_str函数处的第4行,同时gdb会显示函数参数的地址和值。然后我们用 n 来单步执行

(gdb) n
str[] = 'a';
(gdb) n Program received signal SIGSEGV, Segmentation fault.
0x000000000040050b in print_str (str=0x40063b "hello, world!") at main.c:
str[] = 'a';
(gdb)

单步执行到第5行时出错了,程序收到操作系统发出的段错误信号。此时我们恍然大悟,尝试修改字符串常量的值,所以出错了。

2. 利用程序出错时产生的core dump文件来调试
如果用的shell是bash,先使用命令 ulimit -c unlimited 告诉bash,允许程序

core dump 任意大小的文件。这样就允许core dump文件的产生了。

运行程序:

./csd/main
Segmentation fault (core dumped)

此时发生段错误时,在当前工作目录下,产生了core dump文件:core.32624

加载core dump文件调试的语法: gdb /path/program-name /path/core-file
执行命令:

gdb ./main core.32624
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-37.el5)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
./main: No such file or directory.
[New Thread 32624]
Core was generated by `./csd/main'.
Program terminated with signal 11, Segmentation fault.
#0 0x000000000040050b in ?? ()
(gdb)

细心的读者会发现,gdb的显示的信息中,提示没有找到./main这个文件或目录。这是由于我切换目录(/home/csd/) 到了上一级目录(/home),导致gdb找不到main程序的源文件。此时可以设置 dir [directories] 变量来设置源代码的查找目录

(gdb) dir ./csd/
Source directories searched: /home/shengdong.csd/./csd:$cdir:$cwd
(gdb)
这时再用 l 命令就可以显示出源代码了。使用命令 bt [backtrace] 来查看运行时的堆栈[当前出core时的状态]:

(gdb) bt
# 0x000000000040050b in print_str (str=0x40063b "hello, world!") at main.c:
# 0x0000000000400533 in main () at main.c:
(gdb)

可以看到程序时在main.c函数的第7行出问题的,然后用 l 7 来显示第7行上下的源代码,发现问题的所在。

另外,还有一种调试方法:

3.调试正在运行的程序
先通过 ps aux |grep main 来找到要调试程序的pid,然后执行命令:
gdb /path/program program-pid 来attach到正在运行的程序。update: 2013/11/29 此时程序是停住的。可以用 info program 查看程序是否在运行被暂停的原因。记得加完需要的断点后,执行 c  命令来让程序继续执行。
 
4.通过gdb来启动并调试程序
有时候程序一启动就出错了,根本不能用方法3来调试,此时可以用gdb来启动程序并开始调试:
 gdb --args executablename arg1 arg2 arg3
 
只要你学会了上面的命令,基本就算是gdb入门了,可以在gdb中存活下去了。gdb的强大之处,需要自己逐渐去发掘。附上一些gdb相关的链接,希望对读者有所帮助。
[1] 用GDB调试程序(一)
   陈浩写的很详细,总共七部分。四和七在页面上没有链接,可以google一下,就能找到了。网上也有人整理成了pdf的文档,方便阅读。
 
 

如果您看了本篇博客,觉得对您有所收获,请点击右下角的“推荐”,让更多人看到!

资助Jack47写作,打赏一个鸡蛋灌饼钱吧
微信打赏
支付宝打赏

新手如何在gdb中存活的更多相关文章

  1. Linux入门进阶 - 如何在Linux中使用export命令

    来自:Linux迷链接:https://www.linuxmi.com/linux-export.html Linux export命令会标记哪些值需要传递给一组子进程.这是bash shell提供的 ...

  2. 如何在Java中调用Python代码

    有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调 ...

  3. CocosCreator游戏开发---菜鸟学习之路(三)如何在CocosCreator中使用Pomelo

    PS(废话): 这段时间都在研究网易的Pomelo框架,作为新手小白,自然遇到了不少坑爹的事情.(当然也有可能是因为自己技术不过关的原因所以导致在很多基础的问题上纠结了很久.)网上也搜索了好久,但是基 ...

  4. 如何在VMware中安装Linux系统

    这篇文章主要讲述如何在VMware12中安装RHEL6.9Linux操作系统 步骤一: 打开VMware软件,在主页中点击创建新的虚拟机或者点击左上角文件,在列表中点击新建虚拟机,如图: 步骤二: 点 ...

  5. 如何在onCreate中获取View的高度和宽度

    如何在onCreate中获取View的高度和宽度 原文链接:http://mp.weixin.qq.com/s?__biz=MzAwODE1NTI2MQ==&mid=2247483676&am ...

  6. 我是如何在SQLServer中处理每天四亿三千万记录的

    首先声明,我只是个程序员,不是专业的DBA,以下这篇文章是从一个问题的解决过程去写的,而不是一开始就给大家一个正确的结果,如果文中有不对的地方,请各位数据库大牛给予指正,以便我能够更好的处理此次业务. ...

  7. 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧

    做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...

  8. 如何在latex 中插入EPS格式图片

    如何在latex 中插入EPS格式图片 第一步:生成.eps格式的图片 1.利用visio画图,另存为pdf格式的图片 利用Adobe Acrobat裁边,使图片大小合适 另存为.eps格式,如下图所 ...

  9. 如何正确的使用json?如何在.Net中使用json?

    什么是json json是一种轻量级的数据交换格式,由N组键值对组成的字符串,完全独立于语言的文本格式. 为什么要使用json 在很久很久以前,调用第三方API时,我们通常是采用xml进行数据交互,但 ...

随机推荐

  1. 如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发

    阅读目录 前言 单元测试 纠正错误,重新出发 结语 一.前言 实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发 ...

  2. Java8实战分享

    虽然很多人已经使用了JDK8,看到不少代码,貌似大家对于Java语言or SDK的使用看起来还是停留在7甚至6. Java8在流式 or 链式处理,并发 or 并行方面增强了很多,函数式的风格使代码可 ...

  3. 在Ubuntu 16.10 安装 git 并上传代码至 git.oschina.net

    1. 注册一个账号和创建项目 先在git.oschina.net上注册一个账号和新建一个project ,如project name 是"myTest". 2.安装git sudo ...

  4. iOS逆向工程之Hopper+LLDB调试第三方App

    LLDB是Low Level Debugger的简称,在iOS开发的调试中LLDB是经常使用的,LLDB是Xcode内置的动态调试工具.使用LLDB可以动态的调试你的应用程序,如果你不做其他的额外处理 ...

  5. WPF样式之画刷结合样式

    第一种画刷,渐变画刷GradientBrush (拿线性渐变画刷LinearGradientBrush(其实它涵盖在GradientBrush画刷内.现在拿他来说事.),还有一个圆心渐变画刷Radia ...

  6. Javacript实现字典结构

    字典是一种用[键,值]形式存储元素的数据结构.也称作映射,ECMAScript6中,原生用Map实现了字典结构. 下面代码是尝试用JS的Object对象来模拟实现一个字典结构. <script& ...

  7. Web安全相关(五):SQL注入(SQL Injection)

    简介 SQL注入攻击指的是通过构建特殊的输入作为参数传入Web应用程序,而这些输入大都是SQL语法里的一些组合,通过执行SQL语句进而执行攻击者所要的操作,其主要原因是程序没有细致地过滤用户输入的数据 ...

  8. Web开发安全之文件上传安全

    很长一段时间像我这种菜鸡搞一个网站第一时间反应就是找上传,找上传.借此机会把文件上传的安全问题总结一下. 首先看一下DVWA给出的Impossible级别的完整代码: <?php if( iss ...

  9. Impress.js上手 - 抛开PPT、制作Web 3D幻灯片放映

    前言: 如果你已经厌倦了使用PPT设置路径.设置时间.设置动画方式来制作动画特效.那么Impress.js将是你一个非常好的选择. 用它制作的PPT将更加直观.效果也是嗷嗷美观的. 当然,如果用它来装 ...

  10. Vim使用

    模式 ESC\Ctrl+c:退出编辑模式 ZZ\wq:命令模式下保存退出 编辑 i:进入编辑模式 I:转到行首非空字符开始编辑 s:删除当前字符进入编辑模式 a:从当前字符后开始编辑 A:从当前行末非 ...