调试利器GDB

https://blog.csdn.net/qq_39654127/article/details/87953661

目录

1、初探GDB

2、使用GDB 进行断点调试

3、函数调用栈的查看

4、调试中的小技巧

5、小结

1、初探GDB
什么是GDB?

      - GNU项目中的调试器(gnu debuger)

      - 能够跟踪程序的执行,也能够恢复程序崩溃前的状态

为什么需要GDB?

      - 软件不是一次性开发完成的(是软件就有bug,是程序就有问题)

      - 调试是软件开发过程中不可或缺的技术(调试工具很重要)

         binutils工具集属于静态分析工具,目标是可执行程序文件(事后分析)

         GDB是动态分析工具,目标是进程

GDB的常规应用

      - 自定义程序的启动方式(指定影响程序运行的参数如:命令行参数)

      - 设置条件断点(在条件满足时暂停程序的执行)

      - 回溯检查导致程序异常结束的原因(Core Dump)

      - 动态改变程序执行流(定位问题的辅助方式)

GDB的启动方式

      - 直接启动

          gdb

          gdb test.out

          gdb test.out core

      - 动态连接

          gdb test.out pid //动态跟踪这一进程

GDB应用示例

实验分析

初步体验gdb的调试 func.c  test.c

fun.c

#include <stdio.h>
 
int* g_pointer;
 
void func()
{
    *g_pointer = (int)"D.T.Software";
 
    return;
}
#include <stdio.h>
#include <unistd.h>
 
extern int* g_pointer;
extern void func();
 
void test_1()
{
    printf("test_1() : %p\n", test_1);
}
 
void test_2()
{
    printf("test_2() : %p\n", test_2);
}
 
void test_3()
{
    printf("test_3() : %p\n", test_3);
}
 
int main(int argc, char *argv[])
{
    typedef void(TFunc)();
    TFunc* fa[] = {test_1, test_2, test_3};
    int i = 0;
    
    printf("main() : begin...\n");
    
    for(i=0; i<argc; i++)
    {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    
    for(i=0; i<100; i++)
    {
        fa[i%3]();
        sleep(argc > 1);//argc > 1休眠1s
    }
 
    printf("g_pointer = %p\n", g_pointer);
 
    func();
    
    printf("main() : end...\n");
 
    return 0;
}
编译运行出现段错误,使用gdb调试分析错误

调试1

               

           

 直接定位了错误之处

调试2

                                    直接定位了错误之处,比静态分析工具强大多

演示设置命令行参数

演示gdb动态链接到一个进程

2、使用GDB 进行断点调试
断点类型

      - 软件断点:由非法指令异常实现(软件实现)

      - 硬件断点:由硬件特性实现(数量有限)

      - 数据断点:由硬件特性实现(数量有限)

 

        软件断点适用于运行于内存中的程序

        硬件断点适用于运行于Flash中的程序

        数据断点用于监视一段内存,若这段内存被访问(被读,被写)程序立即停下

软件断点的相关操作

      - 通过函数名设置断点

                 break func_name [ if var = value ]

                 tbreak func_name [ if var = value ]

      - 通过文件名行号设置断点

                 break file_name:line_num [ if var = value ]

                 tbreak file_name:line_num [ if var = value ]

          (break设置的断点总是有效的,tbreak设置一次有效断点,若指明条件就为条件断点)

调试时的常用操作

硬件断点及其应用

      - 当代码位于只读存储器(Flash)时,只能通过硬件断点调试

      - 硬件断点需要硬件支持,数量有限

      - GDB中通过 hbreak 命令支持硬件断点

      - hbreak 与 break 使用方式完全一致

实验分析

使用gdb进行断点调试

             我们可以尝试跳过43行func()的调用,若程序执行正常确定func函数有问题

接下来进行第二次调试

第三次调试

               整个过程没有修改源代码...就成功定位错误,并解决问题

 

GDB中支持数据断点的设置

      - watch 命令用于监视变量是否被改变(本质为硬件断点)

      - watch命令的用法:watch var_name

GDB中的内存查看

      - GDB中可以检查任意内存区域中的数据

      - 命令语法:x /Nuf expression

              N - 需要打印的单元数

              u - 每个单元的大小

               f - 数据打印的格式

示例:判断系统大小端

实验分析

变量断点和内存查看 test.c

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
 
int g_var = 0;
 
void* thread_func(void* args)
{
    sleep(5);
    
    g_var = 1;
}
 
int main()
{
    int i = 0;
    pthread_t tid = 0;
    
    pthread_create(&tid, NULL, thread_func, NULL);
    
    for(i=0; i<10; i++)
    {
        printf("g_var = %d\n", g_var);
        
        sleep(1);
    }
}


下面调试分析 定位哪一行代码修改了全局变量

                                这样就可以得知11行代码改写了数据

3、函数调用栈的查看
函数调用栈的查看(backtrace和frame)

      - backtrace   查看函数调用的顺序(函数调用栈的信息)

      - frame N      切换到栈编号为N的上下文中

      - info frame   查看当前函数调用的栈帧信息

深入info命令

实验分析

函数调用栈的查看 frame.c

#include <stdio.h>
 
 
int sum(int n)
{
    int ret = 0;
    
    if( n > 0 )
    {
        ret = n + sum(n-1);
    }
    
    return ret;
}
 
 
int main()
{
    int s = 0;
    
    s = sum(10);
    
    printf("sum = %d\n", s);
    
    return 0;
}


下面调试分析

 

                         ebp向后读4个字节是之前ebp指针指向的位置

                         ebp向前读4个字节就是esp返回的地址

4、调试中的小技巧


实验分析

#include <stdio.h>
 
int g_var = 1;
 
struct ST
{
    int i;
    int j;
};
 
int func()
{
    struct ST st[5] = {0};
    int i = 0;
    
    for(i=0; i<5; i++)
    {
        st[i].i = i;
        st[i].j = i * i;
    }
    
    for(i=0; i<5; i++)
    {
        printf("st[%d].i = %d\n", i, st[i].i);
        printf("st[%d].j = %d\n", i, st[i].j);
    }
}
 
int main()
{
    static c_var = 2;
    
    func();
    
    return 0;
}
演示断点处自动打印 

 

演示符号查看

5、小结
GDB是GNU项目中的调试器,能够跟踪或改变程序的执行

GDB能够根据Core Dump回溯检查导致程序异常结束的原因

GDB同时支持软件断点,硬件断点和数据断点

watch 用于监视变量是否被改变,x用于查看内存中的数据

GDB支持函数调用栈的查看(backtrace,info frames)

GDB支持运行时对程序中的符号进行查看(whatis,ptype)

GDB是嵌入式开发中必须掌握的重要工具
————————————————
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值