Linux基础 30分钟GDB调试快速突破

引言 Linus心灵鸡汤

  在*nix开发中有道卡叫gdb调试,不管你怎么搞. 它依然在那丝毫不会松动.今天致敬一个 活着的传奇 Linus Torvalds

  Unix 始于上个世纪60年代,在70年代得到了迅猛的发展,

这时候的李纳斯还躺在祖父公寓的摇篮里睡大觉,如果不是后来 Unix 王国自乱阵脚,

出现阵营分裂和法律纠纷,可能 Linux 系统根本都不会出现。真实的情况是,

Unix 浪费了大把的时间和机会,似乎就是为了等待这个大鼻子、头发纷乱的芬兰小子长大,然后一决高下。

  李纳斯赢得了自己的时间,他一刻不停的磨练自己的技艺,在清晨的微光中练习算法,

在赫尔辛基的雪山上编译代码,随时随地补充的粮草和武器。

二十一年之后,李纳斯抚着雪亮的刀锋上路了,他要去追寻属于程序员的最高荣耀。[

  I simply know better than you, that‘s why I‘m your god.

                          - -  Linus Torvalds

]

前言  gdb 开始调试开始上手

1. 开启core, 采集程序崩溃的状态

  首先你跟着我做开启core崩溃状态采集. 可以通过 ulimit -c 查看 如果是0表示没有开启. 开启按照下面操作

su root

vi /etc/profile
Shift + G
i
# No core files by default 0, unlimited is oo
ulimit -S -c unlimited > /dev/null 2>&1
wq!

source /etc/profile

上面shell 操作是 在 /etc/profile 最后一行添加 上面设置全局开启 core文件调试,大小不限. 最后 立即生效.

再跟着我做, 因为生成的core文件同名会覆盖. 这里为其加上一个 core命名规则, 让其变成 [core.pid] 格式.

su root

vi /etc/sysctl.conf
Shift + G
i

# open, add core.pid
kernel.core_uses_pid = 1

wq!

sysctl -p /etc/sysctl.conf

在 /etc/sysctl.conf 文件中添加系统配置. 后面立即启用. 最后是下面状态表示core启用都搞好了.

(上面是ubuntu 15.10 环境中, 后面测试用的是centos 6.4)

2. 简单接触 GDB , 开始调试 r n p

第一个演示代码 heoo.c

#include <stdio.h>

int g_var = 0;

static int _add(int a, int b) {
    printf("_add callad, a:%d, b:%d\n", a, b);
    return a+b;
}

int main(void) {
    int n = 1;

    printf("one n=%d, g_var=%d\n", n, g_var);
    ++n;
    --n;

    g_var += 20;
    g_var -= 10;
    n = _add(1, g_var);
    printf("two n=%d, g_var=%d\n", n, g_var);

    return 0;
}

我们下面从图说起.(如果用视频说更好,文字和图意义在于查询方便.更简约)

第一个命令 gdb heoo.out 表示 gdb加载 heoo.out 开始调试. 如果需要使用gdb调试的话编译的时候 gcc 需要加上 -g命令.

其中l命令表示 查看加载源码内容. 下面将演示如何加断点.

r 表示调试的程序开始运行.

p 命令表示 打印值. n表示过程调试, 到下一步. 不管子过程如何都不进入. 直接一次跳过.

上面用的s 表示单步调试, 遇到子函数,会进入函数内部调试.

总结一下 . l 查看源码 , b 加断点, r 开始运行调试, n 下一步, s下一步但是会进入子函数. p 输出数据.

到这里gdb 基本会用了. 是不是也很容易. 直白. 小代码可以随便调试了.

看到这里基础知识普及完毕了. 后面可以不看了. 有机会再看. 好那我们接着扯.

正文 第一部分 gdb其它开发中用的命令

  开始扯一点, linux总是敲命令操作, 也很不安全. 有时候晕了. 写这样编译命令.

gcc -g -Wall -o heoo.c heoo.out

非常恐怖, heoo.c 代码删除了. heoo.out => heoo.c 先创建后生成失败退出. 原先的内容被抹掉了. 哈哈. 服务器开发, 经验不足, 熟练度不够.自己都怕自己.

1.  gdb 其它常用命令用法 c q b info

首先看 用到的调试文件 houge.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/*
 * arr 只能是数组
 * 返回当前数组长度
 */
#define LEN(arr) (sizeof(arr)/sizeof(*arr))

// 简单数组打印函数
static void _parrs(int a[], int len) {
    int i = -1;
    puts("当前数组内容值如下:");

    while(++i < len)
        printf("%d ", a[i]);
    putchar(‘\n‘);
}

// 简单包装宏, arr必须是数组
#define PARRS(arr) \
    _parrs(arr, LEN(arr))

#define _INT_OLD (23)

/*
 * 主函数,简单测试
 * 测试 core文件,
 * 测试 宏调试
 * 测试 堆栈内存信息
 */
int main(void) {
    int i;
    int a[_INT_OLD];
    int* ptr = NULL;    

    // 来个随机数填充值吧
    srand((unsigned)time(NULL));
    for(i=0; i<LEN(a); ++i)
        a[i] = rand()%222;

    PARRS(a);

    //全员加double, 包含一个错误方便测试
    for(i=1; i<=LEN(a); ++i)
        a[i] <<= 1;
    PARRS(a);

    // 为了错,强制错
    *ptr = 0;

    return 0;
}

同样需要仔细看下面图中使用的命令. 首先对前言部分加深一些. 看下面

这个图是前言的补充, c跳过直到下一个断点处, q表示程序退出.

在 houge.c 中我们开始调试. 一运行段错误, 出现了我们的 core.pid 文件

通过 gdb houge.out core.27047 开始调试. 马上定位出来了错误原因.

2. 调试 内存堆栈信息

刚开始 print a , 在main中当做数组处理.打印的信息多. 后面在_add函数中, a就是个形参数组地址.

主要看 info args  查看当前函数参数值

info locals 看当前函数栈上值信息, info registers 表示查看寄存器值.

后面查看内存信息 需要记得东西多一些. 先看图

x /23dw a 意思是  查看 从a地址开始 23个 4字节 有符号十进制数 输出.

关于x 更加详细见下面

用gdb查看内存格式:
    x /nfu ptr

说明
x 是 examine 的缩写
n表示要显示的内存单元的个数

f表示显示方式, 可取如下值
x 按十六进制格式显示变量。
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
a 按十六进制格式显示变量。
i 指令地址格式
c 按字符格式显示变量。
f 按浮点数格式显示变量。

u表示一个地址单元的长度
b表示单字节,
h表示双字节,
w表示四字节,
g表示八字节

Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),
t(binary), f(float), a(address), i(instruction), c(char) and s(string).
Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes)

ptr 表示从那个地址开始

这个命令常用于监测内存变化.调试中特别常用.

3. gdb 设置条件断点

很简单 b 17 if i == 8. 在17行设置一个断点,并且只有i==8的时候才会触发.

4. gdb 删除断点

gdb 删除有d 后面跟断点索引1,2,3..

clear 行数或名称. 删除哪一行断点. 看下面演示

到这里 介绍的gdb调试技巧基本都够用了. 感觉用图形ide,例如vs调试也就用到这些了.

估计gdb调试突破20min过去了.够用了.  后面可以不用看了.

正文 第二部分 gdb 多线程多进程调试

  到这里实战中用的机会少了, 也就老鸟会用上些. 这部分可以调试,不好调试. 一般一调估计小半天就走了. 好,那我们处理最后10min.

1. 首先对上面正文第一部分加深 gdb调试宏

首先看上面命令 

  macro expand 宏(参数) => 得到宏导出内容.

  info macro 宏名 => 宏定义内容

如果你需要用到上面gdb功能, 查看和导出宏的话.还需要gcc 支持,生成的时候加上 -ggdb3如下

gcc -Wall -ggdb3 -o houge.out houge.c

就可以使用了. 扩展一下 对于 gcc 编译的有个过程叫做 预编译 gcc -E -o *.i *.c.

这时候处理多数宏,直接展开, 也可以查看最后结果. 也算也是一个黑科技.

2. 开始多线程调试

首先看测试用例 dasheng.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

// 声明一个都用的量
static int _old;

// 线程跑的函数
static void* _run(void* arg) {
    int piyo = 10;
    int n = *(int*)arg;
    int i;

    //设置线程分离
    pthread_detach(pthread_self());

    for(i=0; i<n; ++i) {
        printf("n=%d, i=%d\n", n, i);
        ++_old;
        printf("n=%d, piyo = %d, _old=%d\n", n, piyo, _old);
    }

    return NULL;
}

#define _INT_PTX (3)

int main(void) {
    int i, rt, j;
    pthread_t tx[_INT_PTX];

    puts("main beign");    

    for(i=0; i<_INT_PTX; ++i) {
        // &i 是有问题的, 但是这里为了测试, 可以乱搞
        rt = pthread_create(tx+i, NULL, _run, &i);
        if(rt < 0) {
            printf("pthread_create create error! rt = %d, i=%d\n", rt, i);
            break;
        }
    }

    //CPU忙等待
    for(j=0; j<1000000000; ++j)
        ;
    puts("end");    

    return 0;
}

编译命令

gcc -Wall -g -o dasheng.out dasheng.c -lpthread

那先看下面测试图

上面 info threads 查看所有运行的线程信息. *表示当前调试的线程.

后面 l _run 表示查看 _run附近代码. 当然还有 l 16 查看16行附近文件内容.

gdb多线程切换 测试如下

thread 3表示切换到第三个线程, info threads 第一列id 就是 thread 切换的id.

上面测试线程 就算你切换到 thread 3. 其它线程还是在跑的. 我们用下面命令 只让待调试的线程跑. 其它线程阻塞.

set scheduler-locking on  开始多线程单独调试. 不用了 设置 set scheduler-locking off 关闭. 又会回到你调试这个, 其它线程不阻塞.

总结 多线程调试常用就这三个实用命令

info threads

thread id

set scheduler-locking on/off

分别是查看,切换,设置同步调试.到这里多线程调试基本完毕了.

3. 开始gdb 多进程调试

首先看 liaobude.c 测试代码

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

// 声明一个都用的量
static int _old;

// 线程跑的函数
static void _run(int n) {
    int piyo = 10;
    int i;

    ++n;
    for(i=0; i<n; ++i) {
        printf("n=%d, i=%d\n", n, i);
        ++_old;
        printf("n=%d, piyo = %d, _old=%d\n", n, piyo, _old);
    }
}

#define _INT_PTX (3)

int main(void) {
    int i;
    pid_t rt;

    puts("main beign");    

    for(i=0; i<_INT_PTX; ++i) {
        // &i 是有问题的, 但是这里为了测试, 可以乱搞
        rt = fork();
        if(rt < 0) {
            printf("fork clone error! rt = %d, i=%d\n", rt, i);
            break;
        }
        if(rt == 0) {
            _run(i);
            exit(EXIT_FAILURE);
        }
    }

    //等待子进程结束
    while((rt = waitpid(-1, NULL, WNOHANG))>=0) {
        if(rt < 0 && errno == EINTR)
            continue;
    }    

    puts("end");    

    // 这里继续等待
    for(i=0; i<190; ++i){
        printf("等待 有缘人[%d]!\n", i);
        sleep(1);
    }    

    return 0;
}

编译命令

gcc -Wall -g -o liaobude.out liaobude.c

其实对多进程调试, 先介绍一个 常用的, 调试正在运行的程序. 首先让 ./liaobude.out 跑起来.

再通过 ps -ef 找到需要调试的进程. 复制进程文件描述符pid.

这时候启动gdb.

attach pid

gdb就把pid那个进程加载进来了. 加载的进程会阻塞到当前正在运行的地方. 直到使用命令控制. 这个功能还是非常猛的.

最后介绍 进程调试的有关命令(需要最新的gdb才会支持). 多进程的调试思路和多线程调试流程很相似.

GDB可以同时调试多个程序。
只需要设置follow-fork-mode(默认值:parent)和detach-on-fork(默认值:on)即可。

   设置方法:set follow-fork-mode [parent|child]   set detach-on-fork [on|off]

   查询正在调试的进程:info inferiors
   切换调试的进程: inferior <infer number>
    

具体的意思有

set follow-fork-mode [parent|child]   set detach-on-fork [on|off]

parent                   on               只调试主进程(gdb默认)
 child                      on               只调试子进程
 parent                   off              同时调试两个进程,gdb跟主进程,子进程block在fork位置
 child                      off              同时调试两个进程,gdb跟子进程,主进程block在fork位置

更加详细的 gdb 多进程调试demo 可以参照  http://blog.csdn.net/pbymw8iwm/article/details/7876797

使用方式和线程调试思路是一样的. 就是gdb 的命令换了字符. 工作中多进程调试遇到少.

遇到了很少用gdb调试. 会用下面2种调试好办法

2) 写单元测试

3) 打日志检测日志,分析

到这里 gdb30分钟内容讲解完毕. 多试试写写练一练, gdb基本突破没有问题.

后记

  错误是难免的, 有问题可以随时交流. 拜~~, 周六下午愉快. 希望明天仍然是个好天气~~

时间: 2024-07-29 03:13:57

Linux基础 30分钟GDB调试快速突破的相关文章

【干货】零基础30分钟让你拥有一个完整属于自己的短视频APP系统

目录 一.附言: 1 二.购买域名和购买服务器: 2 三.搭建服务器环境: 5 四.配置APP前端部分: 8 1.工具以及文件准备: 9 2.配置后端接口地址 11 3.配置APP启动图和启动图标 12 五.搭建APP后端部分 13 步骤,一步步输入并且回车. 13 1. 登录方才已经安装好的宝塔软件以及创建通信网站. 15 2. 导入数据库测试是否连通. 20 六.搭建APP后台管理端部分 24 七.打包APP下载并测试 31 一.附言: [干货]30分钟让你拥有一个完整属于自己的短视频APP

linux下如何使用gdb调试

gdb是linux下非常好用的一个调试工具,虽然它是命令行模式的调试工具,但是它的功能强大到你无法想象,这里简单介绍下gdb下常用的命令. 首先编译生成可执行文件(这里的test.c是一个简单的求前n项和的程序). gcc -g test.c -o test(-g选项告诉gcc在编译程序时加入调试信息).接下来可以这样. gdb test 然后你就会看到出现好多信息在屏幕上,大致说的是gdb的一些版本信息说明之类的,但是它对你调试程序没用呀,所以,你可以加上-q选项,不输出它们. gdb -q

iMX6Q调试篇-Linux程序开发使用gdb调试

平台: OKMX6Q-S2平台 (ARM Cortex-A9 i.MX6Q) 环境: Linux 一. cmd程序 1.编写一个简单地崩溃程序dest.c,如下: #include "stdio.h" int main() { int *p=NULL; *p=1; //给指针赋值 return 1; } 2.编译 arm-fsl-linux-gnueabi-gcc -g -o 11 dest.c 注意:一定要在命令中加上 -g参数,这样使用gdb调试时才能看到具体是程序的哪一行出了错误

VS2017创建Linux项目实现远程GDB调试

vs2017新增linux for C++的模块,尝试安装了一下环境. 首先,安装VS2017,安装时注意选择以下模块: 安装完成后,需要配置Linux服务端的部分,我的配置过程如下: 第一步,安装VMware虚拟机,此过程不再细细描述了. 第二步,安装Linux操作系统.我下载的是CentOS的min版本的安装包,里面只有基础的功能(不包括UI服务器部分).需要注意的是网卡默认是关闭的,在安装时注意开启下,否则还得另外花时间去改配置文件. 第三步,在Linux端部署需要的服务(安装时注意以ro

linux下如何用GDB调试c++程序

原文地址:http://blog.csdn.net/wfdtxz/article/details/7368357 GDB 是GNU开源组织发布的一个强大的UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像VC.BCB等IDE的调试,但如果你是在 UNIX平台下做软件,你会发现GDB这个调试工具有比VC.BCB的图形化调试器更强大的功能.所谓“寸有所长,尺有所短”就是这个道理. 一般来说,GDB主要帮忙你完成下面四个方面的功能: 启动你的程序,可以按照你的自定义的要求随心所欲的运行

DAX基础入门 – 30分钟从SQL到DAX — PowerBI 利器

看到漂漂亮亮的PowerBI报表,手痒痒怎么办?! 有没有面对着稀奇古怪的DAX而感到有点丈八金刚摸不着头脑或者干瞪眼?! 有没有想得到某个值想不出来DAX怎么写而直跳脚!? 看完这篇文章,你会恍然大悟,捂脸偷笑.呼呼呼~ 前言: 这篇文章对于具有一点SQL查询基础人会十分容易理解,譬如:掌握SELECT,SUM,GROUP BY等. 注:此文不涉及到Filter Context(筛选上下文)的介绍. 正文: 对于对SQL有一定了解的人来说,咋看DAX,怎么都不习惯. 但是,如果理解以下几个后,

gdb调试之linux pc和linux arm环境下

Linux PC应用程序gdb调试: 1.查看core文件参数 [email protected]:~/File_unzip/cp_module$ ulimit -a core file size          (blocks, -c) 0        //产生core文件数目,不会产生core文件 data seg size           (kbytes, -d) unlimited scheduling priority             (-e) 0 file size 

Eclipse+CDT+GDB调试android NDK程序(转)

Eclipse+CDT+gdb调试android ndk程序 先介绍一下开发环境,在这个环境下,up主保证是没有问题的. ubuntu 11.10 eclipse 3.7(indego) for java jdk 6 android sdk 2.2 andrid ndk r7 当然,在windows环境下通过cygwin等工具也是可以实现gdb调试的,我也确实实现过.但是性能实在太低,卡的根本没法用.Linux下直接用gdb调试本地方法是很流畅的. 再确定安装并配置好开发环境之后,就可以开始了.

Linux下C/C++程序调试基础(GCC,G++,GDB,CGDB,DDD)

在写程序的时候,经常会遇到一些问题,比如某些变量计算结果不是我们预期的那样,这时我们需要对程序进行调试.本文主要介绍调试C/C++在Linux操作系统下主要的调试工具. 在Linux下写程序,C/C++主要的编译器有GCC/G++,ICC等,像我等穷码农,最喜欢GCC了,很大原因是他免费!所以,我们以GCC/G++为例介绍主要的调试工具. 分以下几个内容介绍: 1.调试之前的工作 2.选择调试工具 3.调试步骤 点我,请帮我投一票! 调试之前的工作 编译器在编译阶段需要产生可供调试的代码,才能被