软件调试 · 工具类 · GDB调试基础操作

目录

  • 1、引言
    • 1.1、GDB简介
    • 1.2、GDB支持的语言
  • 2、调试环境介绍
  • 3、安装GDB
  • 4、测试代码
  • 5、调试
    • 5.1、基础操作
      • 5.1.1、启动GDB
      • 5.1.2、运行程序
      • 5.1.4、清屏操作
      • 5.1.5、步进调试
        • 5.1.5.1、start命令:运行程序后,停留在主函数第一行
        • 5.1.5.2、step命令:单步执行,会进入函数内部
        • 5.1.5.3、next命令:单步执行,不会进入函数内部
        • 5.1.5.4、continue命令:继续执行程序直到下一个断点
      • 5.1.6、list命令:输出断点处周围的代码
      • 5.1.7、断点
        • 5.1.7.1、设置指定行的断点
        • 5.1.7.2、查看所有断点信息
        • 5.1.7.3、在函数入口处设置断点
        • 5.1.7.4、删除断点
      • 5.1.8 print:查看变量
        • 5.1.8.1、查看基本变量
        • 5.1.8.2、查看数组元素的值
        • 5.1.8.3、查看字符串
        • 5.1.8.4、打印指针和它们指向的值
        • 5.1.8.5、打印结构体的字段
        • 5.1.8.6、打印数组的所有元素的值
      • 5.1.9、退出GDB
    • 5.2 中级操作
      • 5.2.1 条件断点
        • 5.2.1.1 基本概念
        • 5.2.1.2 使用场景
        • 5.2.1.3 优点
        • 5.2.1.4 注意事项
        • 5.2.1.5 举例

1、引言

1.1、GDB简介

  GDB是GNU项目的一个调试器,它允许开发者在程序执行时或崩溃时查看程序内部发生的情况。

  GDB的主要功能包括启动程序、在特定条件下停止程序、检查程序停止时的状态以及在程序中更改内容以实验修复错误。

  更多信息可访问GDB的官网:GDB: The GNU Project Debugger

1.2、GDB支持的语言

  GDB支持多种编程语言,包括Ada、Assembly、C、C++、D、Fortran、Go、Objective-C、OpenCL、Modula-2、Pascal和Rust。

2、调试环境介绍

  本文所涉及到的编程环境为:

  1. wsl 2 + Ubuntu 22.04 LTS
  2. gcc version 11.4.0

在这里插入图片描述

3、安装GDB

  安装GDB前,先更新一些软件列表,其命令为:

sudo apt-get update

  更新结果:
在这里插入图片描述

  在更新完毕后,使用下面这条命令对GDB进行安装:

sudo apt-get install gdb -y

  更新结果:

在这里插入图片描述
  可以看出,安装GDB是一个十分简单的过程。安装完毕后,就可以使用GDB进行调试了。

4、测试代码

  在开始进行调试之前,需要有测试代码,本博文所用的测试代码如下所示:

#include <stdio.h>
#include <stdlib.h>// 定义一个结构体
typedef struct
{int id;char name[20];float score;
} Student;/*** @brief 打印学生信息** @param s 指向Student结构的常量指针,代表要打印的学生信息*/
void print_student(const Student *s)
{// 打印学生的学号、姓名和分数printf("学号:%d 姓名:%s 分数:%.1f\n", s->id, s->name, s->score);
}/*** @brief 比较两个学生的分数** @param a 指向第一个学生的指针* @param b 指向第二个学生的指针* @return int 比较结果,如果student_a的分数大于student_b则返回-1,*              如果小于则返回1,相等则返回0*/
int compare(const void *a, const void *b)
{// 将void指针转换为Student指针const Student *student_a = (const Student *)a;const Student *student_b = (const Student *)b;// 比较两个学生的分数if (student_a->score > student_b->score){return -1; // student_a的分数更高}else if (student_a->score < student_b->score){return 1; // student_b的分数更高}else{return 0; // 分数相同}
}/*** @brief 对学生数组进行排序** @param students 学生数组* @param len 数组中学生的数量*/
void sort_students(Student students[], int len)
{// 使用qsort函数对数组进行排序,比较函数为compareqsort(students, len, sizeof(Student), compare);
}int main(int argc, char const *argv[])
{// 基本变量int a = 10;char ch = 'A';float f = 3.14f;// 数组int arr[5] = {5, 2, 8, 1, 9};char str[] = "Hello, GDB!";// 指针int *p = &a;char *p_str = str;// 结构体Student students[3] = {{1, "张三", 85.5f},{2, "李四", 92.0f},{3, "王五", 78.0f}};// 动态内存分配int *dyn_arr = (int *)malloc(5 * sizeof(int));for (int i = 0; i < 5; ++i){dyn_arr[i] = i * i;}// 函数调用printf("基本变量:\n");printf("  整型:%d\n", a);printf("  字符型:%c\n", ch);printf("  浮点型:%.2f\n\n", f);printf("数组:");for (int i = 0; i < 5; ++i){printf("%d ", arr[i]);}printf("\n\n");printf("字符串:%s\n\n", str);printf("指针:\n");printf("整型指针指向的值:%d\n", *p);printf("字符型指针指向的字符串:%s\n\n", p_str);printf("结构体:\n");for (int i = 0; i < 3; ++i){print_student(&students[i]);}printf("\n");printf("动态内存分配的数组:\n");for (int i = 0; i < 5; ++i){printf("%d ", dyn_arr[i]);}printf("\n\n");// 排序结构体数组sort_students(students, 3);printf("排序后的结构体数组:\n");for (int i = 0; i < 3; ++i){print_student(&students[i]);}// 释放动态内存free(dyn_arr);return 0;
}

  使用gcc编译器进行编译,其命令为:

gcc -o main -Wall -std=c99 -g test.c

  记住!为了能生成调试信息,编译命令需加-g 参数

  运行结果为:

在这里插入图片描述

5、调试

5.1、基础操作

5.1.1、启动GDB

  启动GDB所使用的命令为gdb [程序名],如下所示:

在这里插入图片描述

5.1.2、运行程序

  直接运行程序的命令是 run ,效果如下所示:

在这里插入图片描述

可以看到,在程序输出相关内容之前,有三行信息:

Starting program: /home/orange/main
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

  这段GDB输出信息表示的是在使用GDB启动程序时,GDB检测到了程序使用了线程,并且已经启用了线程调试功能。

  具体来说,各部分的含义如下:

   [Thread debugging using libthread_db enabled]:这行表示GDB已经启用了线程调试功能,这是通过使用libthread_db库实现的。libthread_db是一个提供线程调试功能的库,它使得GDB能够跟踪和调试多线程程序。

   Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".:这行说明了GDB正在使用的libthread_db库的具体位置。在这个例子中,这个库位于/lib/x86_64-linux-gnu/目录下,并且库的文件名为libthread_db.so.1

  这有什么意义吗?

  事实上,如果程序是多线程的,那么就可以使用GDB来设置线程相关的断点,查看和操作各个线程的状态,这对于调试多线程程序是非常有用的。

  在程序的最后有一行这样的输出:

[Inferior 1 (process 2001) exited normally]

  这段GDB输出信息表示的是调试的程序(在这里称为“inferior”,即被调试的程序)已经正常退出了。

  具体来说含义如下:

   [Inferior 1 (process 2001) exited normally]:这行表示编号为1的被调试进程(如果同时调试多个进程,每个进程都会有一个编号)已经正常退出了。process 2001指的是这个进程的进程ID(PID),这是一个在系统中唯一标识进程的数字。exited normally意味着程序没有遇到错误或异常,而是按照预期完成了执行并退出。

5.1.4、清屏操作

  在调试过程中,输出的信息太多,扰乱了视觉,这时候可以按下Ctrl + L键进行清屏。

5.1.5、步进调试

5.1.5.1、start命令:运行程序后,停留在主函数第一行

  以本例程为例,run命令将会如果想从头到尾运行一遍程序,但更多的时候,我们更想进行步进调试,此时就需要使用start命令来进行调试,执行命令后,效果如下所示:

在这里插入图片描述
  上面所解释过的信息此处不再解释,让我们关注新出现的信息:

  1. Temporary breakpoint 1 at 0x555555555314: file test.c, line 64.

   这行表示GDB在指定的位置设置了一个临时的断点。Temporary breakpoint 1指的是这是第一个临时断点(如果设置了多个断点,它们将按设置顺序编号)。

   0x555555555314是断点设置在程序中的内存地址。这个地址是程序中main函数开始执行的地方。

   file test.c, line 64说明断点设置在test.c文件的第64行。这意味着当程序执行到test.c文件的第64行时,将会暂停。

  我们可以从源代码中看到,第64行正是主函数的函数头下一行:

在这里插入图片描述
  2. Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe3d8) at test.c:64

   这行表示程序在执行时遇到了之前设置的临时断点。Temporary breakpoint 1再次指出了这是第一个临时断点。

   main (argc=1, argv=0x7fffffffe3d8)main函数的参数。argc是传递给main函数的参数数量(在这个例子中是1),argv是一个指向参数字符串数组的指针(在这个例子中是0x7fffffffe3d8)。

   at test.c:64再次指出了断点位于test.c文件的第64行。

5.1.5.2、step命令:单步执行,会进入函数内部

  为了能够更好的进行举例,此处先提前使用一条命令break 126在第126行处设立一个断点,同时使用continue命令执行到断点处,这两条命令后面再解释,此处只需要知道程序在此处设立了一个断点并执行到断点处即可。效果如下:
在这里插入图片描述
  此时我们使用step命令即可进入函数中进行一步步调试,效果如下:

在这里插入图片描述
  可以查看到第60行(函数内的)的代码为:

在这里插入图片描述
  从上面的结果可以看到,程序进入了函数的内部。

5.1.5.3、next命令:单步执行,不会进入函数内部

  让我们使用start命令重启一下程序的调试,然后再执行continue命令执行到断点处,随后执行next命令观察跟step命令有何区别:

在这里插入图片描述
在这里插入图片描述

  从图中可以看到,使用了next命令之后直接执行函数并进入下一行,并不会进入到函数内部。此处部分代码如下图所示:

在这里插入图片描述

5.1.5.4、continue命令:继续执行程序直到下一个断点

  从上面的结果也可以看出来,continue的作用就是程序运行到程序的断点处停下来,等待程序员的进一步操作,此处不再举例。

5.1.6、list命令:输出断点处周围的代码

  使用start命令重启调试,而后直接使用list命令就能查看周围几行代码,list命令的基本用法是:

list [start[, end]]

  其中start是开始查看的行号,end是结束查看的行号。如果不指定end,GDB将默认显示从start开始的10行代码。

  举一些例子:

list 10:从第10行开始显示源代码,默认显示10行。
list 10, 20:显示从第10行到第20行的源代码。
list , 20:显示当前文件中从当前行到第20行的源代码。
list 10, :显示当前文件中从第10行到文件末尾的源代码。

  如果想在特定的文件中查看代码,可以这样做:

list [filename:]start[, end]

  举一些例子:

list test.c:10:显示test.c文件中从第10行开始的源代码。
list test.c:10, 20:显示test.c文件中从第10行到第20行的源代码。

  以本博文的例程为例,使用start命令重启调试后,直接运行list命令:

在这里插入图片描述

  查看当前文件的1~15行内容:

在这里插入图片描述

  显示test.c文件中从第50行到第75行的源代码:

在这里插入图片描述

5.1.7、断点

5.1.7.1、设置指定行的断点

  break命令可以设置断点,程序在运行到断点处会停下,等待程序员的进一步操作。
  设置指定行断点的命令格式为:

break [行号]

  比如在第66行、第67行和第68行设置断点:

在这里插入图片描述

5.1.7.2、查看所有断点信息

  查看断点信息的命令为:

info breakpoints

  具体效果为:
在这里插入图片描述

  这里解释一下输出信息:

  • Num:断点的编号。
  • Type:断点的类型,比如breakpoint、watchpoint等。
  • Disp:断点的处置方式,keep表示断点在程序重新启动时保持,del表示断点在程序停止时删除。
  • Enb:断点是否启用,y表示已启用,n表示已禁用。
  • Address:断点设置在程序中的内存地址。
  • What:断点所在的函数和文件位置。
5.1.7.3、在函数入口处设置断点

  在GDB中可以在函数入口处设置断点使用函数名。使用函数名设置断点时,GDB会在函数的第一条指令处停止执行。这是设置断点的常用方法,因为它不依赖于具体的行号,而是依赖于函数名,这样即使代码发生了变化,断点仍然有效。

  要设置一个函数入口处的断点,可以使用以下命令:

break function_name

  比如在本例中:

break print_student

  执行结果为:

在这里插入图片描述

5.1.7.4、删除断点

  删除断点可以使用delete 命令,格式为:

delete number

  number是行号,可以使用info breakpoints命令进行查看,比如要删除num 4的的断点:

在这里插入图片描述

  也可以连续删除:
在这里插入图片描述
  如果想全部删除,那么直接使用delete命令即可:

在这里插入图片描述

5.1.8 print:查看变量

5.1.8.1、查看基本变量

  在调试程序的时候,需要查看变量的值,如果是基本变量,可以直接使用以下命令格式:

print <表达式>

  举例一下,我们要观察GDB打印以下这些变量的值:

在这里插入图片描述

  那么使用start开始调试,会看到这三个变量的值都为以下这些情况:

在这里插入图片描述

  这是变量未初始化导致的,当我们在这些变量下面设置一个断点并运行到断点处,再观察输出:

在这里插入图片描述
  此时就能看到变量被初始化了。

5.1.8.2、查看数组元素的值

  除了可以打印基本变量的值,还可以打印数组元素的值,现在来看一下代码:

在这里插入图片描述

  此时在第72行处添加断点,并使用continue命令来执行到断点处,保证数组已经被初始化,而后再用print命令对数组元素的值来进行观察。

在这里插入图片描述

5.1.8.3、查看字符串

  想要观察字符串,此时还是需要设置一个断点,而后在运行到断点处,这时候在使用print命令去查看。

在这里插入图片描述

5.1.8.4、打印指针和它们指向的值

  一样是下断点,一样是运行到断点处,一样是使用print命令观察输出。此部分代码为:

在这里插入图片描述
  观察调试结果:

在这里插入图片描述

5.1.8.5、打印结构体的字段

  相关代码:

在这里插入图片描述
  调试结果:

在这里插入图片描述

5.1.8.6、打印数组的所有元素的值

  事实上,print命令不仅仅可以观察单个元素的值,还能看整个数组的值,比如在本例程中,arr、str和students都是数组,都能一次性查看所有成员的值:

在这里插入图片描述

  dyn_arr属于动态内存分配的数组,单独打印变量名得到的是内存地址,我们需要将dyn_arr的第一个元素强制转换为包含5个整数的数组,才能打印出元素的值:

在这里插入图片描述

5.1.9、退出GDB

  退出GDB的命令是q:

在这里插入图片描述


                                             未完待续

5.2 中级操作

5.2.1 条件断点

5.2.1.1 基本概念

  条件断点:在断点上附加一个布尔表达式,仅当这个表达式为真时,调试器才会中断程序。

5.2.1.2 使用场景

  当在只对程序中的某些特定情况感兴趣时,可以使用条件断点。例如,只想在数组越界或某个变量值达到特定条件时暂停程序。

5.2.1.3 优点

  提高调试效率,不必每次循环都中断,且可以针对复杂的逻辑设置条件,使得调试更加精准。

5.2.1.4 注意事项

  1、条件表达式的编写要遵循C语言或你使用的编程语言的语法规则。
  2、条件断点的计算和检查会增加调试器的开销,可能会稍微减慢程序的运行速度。

5.2.1.5 举例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/568919.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day03-菜品管理

1. 公共字段自动填充 1.1 问题分析 业务表中的公共字段&#xff1a; 序号字段名含义数据类型1create_time创建时间datetime2create_user创建人idbigint3update_time修改时间datetime4update_user修改人idbigint 而针对于这些字段&#xff0c;我们的赋值方式为&#xff1a; 在新…

AJAX(一):初识AJAX、http协议、配置环境、发送AJAX请求、请求时的问题

一、什么是AJAX 1.AJAX 就是异步的JS和XML。通过AJAX 可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据。AJAX 不是新的编程语言&#xff0c;而是一种将现有的标准组合在一起使用的新方式。 2.XML 可扩展标记语言。XML被设计用来传输和…

Redis入门级详解(一)

一、Redis入门介绍 1、什么是Redis? Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。…

JavaSe复习(上半)

制表符&#xff1a; \t 经常忘记&#xff0c;这个东西可以让表格更规整 long类型的数据后面要加一个大写的L&#xff0c;告诉编译器 键盘录入 Scanner sc new Scanner(System.in); int i sc.nextInt(); 逻辑异或^ JDK12的case新特性&#xff1a;case1 -> 执行方法&am…

约数个数(数论,蓝桥杯)

题目描述&#xff1a; 给定一个数n&#xff0c;再给出n个数&#xff0c;现在要求你求出这些数的乘积的约数个数总和&#xff0c;结果对1e97取模。 取值范围&#xff1a;1<n<100; 1<ni<2e9; 分析步骤&#xff1a; 第一&#xff1a;要求约数的个数&#xff0c;我们有…

USB HOST移植

一、USB简介 USB有USB1.0/1.1/2.0/3.0多个版本&#xff0c;标准USB由4根线组成,VCC&#xff0c;GND&#xff0c;D&#xff0c;D-&#xff0c;其中D和D-是数据线&#xff0c;采用差分传输。 在USB主机上,D-和D都是接了15K的电阻到地,所以在没有设备接入的时候,D、D-均是低电平。…

Vue js封装接口

天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.安装axios npm install axios -g 2.在src下新建一个Api文件夹,再创建一个js文件 import axios from axios let configuration {url:"http://localhost:9090" } /*** 请求项目数据的请求体*/ async function h…

QGraphicsView(平移/缩放/旋转)

简述 Graphics View提供了一个平台&#xff0c;用于大量自定义 2D 图元的管理与交互&#xff0c;框架包括一个事件传播架构&#xff0c;支持场景 Scene 中的图元 Item 进行精确的双精度交互功能。Item 可以处理键盘事件、鼠标按下、移动、释放和双击事件&#xff0c;同时也能跟…

地推网推拉新是什么意思?地推网推拉新区别最全介绍

地推是相对于网推的&#xff0c;主要是通过线下推广&#xff0c;来吸引客户注册等拉新操作&#xff0c;有时会发一些小礼物之类的&#xff0c;在之前地推是最早获取新用户的方式&#xff0c;是最有效的拉新方式&#xff0c;直到现在地推仍是各类互联网公司和品牌商家&#xff0…

论文阅读之PeriodicLoRA: Breaking the Low-Rank Bottleneck in LoRA Optimization(2024)

文章目录 论文地址主要内容主要贡献模型图技术细节实验结果 论文地址 PeriodicLoRA: Breaking the Low-Rank Bottleneck in LoRA Optimization 主要内容 这篇文章的主要内容是介绍了一种名为PeriodicLoRA&#xff08;PLoRA&#xff09;的参数高效微调&#xff08;Parameter-…

sdrangel下载与部署for windows

下载 https://github.com/f4exb/sdrangel 在其目录下新建build文件 部署 打开cmake软件,修改如下 点击下面Configure,选择对应vs版本 点击finish,按照提示报错信息配置即可 ARCH_OPT=SSE4_2 DEBUG_OUTPUT

qt table 简易封装,样式美化,以及 合并表格和颜色的区分 已解决

在需求中&#xff0c; 难免会使用 table 进行渲染窗口&#xff0c;做一个简单的封装。美化表格最终效果&#xff01;&#xff01;&#xff01; 代码部分 // 显示 20行 20列CCendDetailsInfoTableWidget* table new CCendDetailsInfoTableWidget(20,10);for (int i 0; i < …