【Linux基础开发工具 (七)】Git 版本管理全流程与 GDB / CGDB 调试技巧


🎬 个人主页艾莉丝努力练剑
专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


文章目录

  • 前情提示
    • [1 Git的文件冲突:两人改同一个文件造成远程和本地仓库不同步](#1 Git的文件冲突:两人改同一个文件造成远程和本地仓库不同步)
    • [2 工作区](#2 工作区)
    • [3 三板斧](#3 三板斧)
    • [4 调试的本质是什么](#4 调试的本质是什么)
  • [6 ~> Git版本控制器](#6 ~> Git版本控制器)
  • [6.5 在Gitee上面创建项目](#6.5 在Gitee上面创建项目)
    • [6.6 三板斧(add / commit / push)](#6.6 三板斧(add / commit / push))
      • [6.6.1 git add](#6.6.1 git add)
      • [6.6.2 git commit](#6.6.2 git commit)
      • [6.2.3 git push](#6.2.3 git push)
      • [6.2.4 配置免密码提交](#6.2.4 配置免密码提交)
      • [6.2.5 其它](#6.2.5 其它)
  • [7 ~> 调试器 - gdb / cgdb使用](#7 ~> 调试器 - gdb / cgdb使用)
    • [7.1 样例展示:什么样的程序才能调试?](#7.1 样例展示:什么样的程序才能调试?)
      • [7.1.1 代码演示](#7.1.1 代码演示)
      • [7.1.2 代码运行](#7.1.2 代码运行)
      • [7.1.3 什么样的程序才能调试------一个既定事实](#7.1.3 什么样的程序才能调试——一个既定事实)
    • [7.2 准备阶段:加上 -g 选项进入debug模式](#7.2 准备阶段:加上 -g 选项进入debug模式)
      • [7.2.1 回顾与新工具概念导入](#7.2.1 回顾与新工具概念导入)
      • [7.2.2 程序的两种发布方式](#7.2.2 程序的两种发布方式)
      • [7.2.3 见一见:调试工具gdb](#7.2.3 见一见:调试工具gdb)
      • [7.2.4 发现:使用gdb调试,必须在源代码生成二进制程序的时候,加上 `-g` 选项](#7.2.4 发现:使用gdb调试,必须在源代码生成二进制程序的时候,加上 -g 选项)
      • [7.2.5 演示添加 `-g` 之后的效果](#7.2.5 演示添加 -g 之后的效果)
    • [7.3 怎么证明确实存在调试信息呢?](#7.3 怎么证明确实存在调试信息呢?)
      • [7.3.1 readelf -S cmd.exe-debug](#7.3.1 readelf -S cmd.exe-debug)
      • [7.3.2 readelf -S cmd.exe-debug | grep -i debug](#7.3.2 readelf -S cmd.exe-debug | grep -i debug)
    • [7.4 常见指令使用(表格形式呈现):调试命令速查表](#7.4 常见指令使用(表格形式呈现):调试命令速查表)
    • [7.5 cgdb(半图形化界面)](#7.5 cgdb(半图形化界面))
      • [7.5.1 安装cgdb](#7.5.1 安装cgdb)
      • [7.5.2 下载演示](#7.5.2 下载演示)
    • [7.6 断点](#7.6 断点)
      • [7.6.1 断点的本质](#7.6.1 断点的本质)
      • [7.6.2 gdb的使用](#7.6.2 gdb的使用)
      • [7.6.3 cgdb的使用](#7.6.3 cgdb的使用)
      • [7.6.4 打断点和删除断点](#7.6.4 打断点和删除断点)
      • [7.6.5 直接删除断点不是一个很好的选择](#7.6.5 直接删除断点不是一个很好的选择)
      • [7.6.6 "使能"的操作](#7.6.6 “使能”的操作)
      • [7.6.7 深刻理解调试是在干什么](#7.6.7 深刻理解调试是在干什么)
      • [7.6.8 在一个测试周期,断点编号是线性连续的](#7.6.8 在一个测试周期,断点编号是线性连续的)
      • [7.6.9 小细节:鼠标不小心滚动之后不在gdb区域了,怎么办?](#7.6.9 小细节:鼠标不小心滚动之后不在gdb区域了,怎么办?)
      • [7.6.10 调试命令小合集](#7.6.10 调试命令小合集)
      • [7.6.11 查看变量](#7.6.11 查看变量)
      • [7.6.12 单步调试](#7.6.12 单步调试)
      • [7.6.13 大范围查找和小范围精确](#7.6.13 大范围查找和小范围精确)
    • [7.7 三种常见的技巧 - 加餐环节](#7.7 三种常见的技巧 - 加餐环节)
      • [7.7.1 监视变量变化:watch](#7.7.1 监视变量变化:watch)
      • [7.7.2 确定问题原因并且验证原因:set var](#7.7.2 确定问题原因并且验证原因:set var)
      • [7.7.3 条件断点(也是断点)](#7.7.3 条件断点(也是断点))
        • [7.7.3.1 添加条件断点](#7.7.3.1 添加条件断点)
        • [7.7.3.2 给已经存在的断点新增条件](#7.7.3.2 给已经存在的断点新增条件)
        • [7.7.3.3 注意](#7.7.3.3 注意)
  • 结尾

前情提示

1 Git的文件冲突:两人改同一个文件造成远程和本地仓库不同步

2 工作区

3 三板斧

4 调试的本质是什么



6 ~> Git版本控制器

前言(看过Git专栏的uu可以跳过这部分啦)

艾莉丝已经把【Git版本控制器】这个板块内容都整理成博客啦,所以Linux主线专栏这里我们就寥寥几笔,直接带过。

专栏链接: 详解Git原理与使用

艾莉丝把此专栏的链接都挂在下面了,大家可以根据实际需要乘坐【传送门】哦!

【Git:基本操作】深度解析Git:从初始Git到熟悉基本操作

【Git:分支管理】Git
分支管理完全指南:从创建、合并到冲突解决

【Git:深度解析Git远程操作和标签管理】从克隆到推送:Git
远程操作与标签管理完全指南

【Git:多人协作】Git多人协作实战:从同分支到多分支工作流

【Git:企业级开发模型】Git企业级Git工作流实战:基于Git
Flow的分支模型与开发流程

6.1 入题:小故事

不知道你工作或学习时,有没有遇到这样的情况:我们在编写各种文档时,为了防止文档丢失,更改失误,失误后能恢复到原来的版本,不得不复制出一个副本,比如:

"报告-vl"

"报告-v2"

"报告-v3"

"报告-确定版"

"报告-最终版"

"报告-究极进化版"

...

每个版本有各自的内容,但最终会只有一份报告需要被我们使用。

但在此之前的工作都需要这些不同版本的报告,于是每次都是复制粘贴副本,产出的文件就越来越多,文件多不是问题,问题是:随着版本数量的不断增多,你还记得这些版本各自都是修改了什么吗?

文档如此,我们写的项目代码,也是存在这个问题的!!!

6.2 版本控制器

为了能够更方便我们管理这些不同版本的文件,便有了版本控制器。所谓的版本控制器,就是能让你了解到一个文件的历史,以及它的发展过程的系统。通俗的讲就是一个可以记录工程的每一次改动和版本迭代的一个管理系统,同时也方便多人协同作业。

目前最主流的版本控制器就是Git。Git可以控制电脑上所有格式的文件,例如doc、excel、dwg、dgn、rvt等等。对于我们开发人员来说,Git最重要的就是可以帮助我们管理软件开发项目中的源代码文件!

6.3 Git的历史发展

同生活中的许多伟大事物一样,Git诞生于一个极富纷争大举创新的年代。

Linux内核开源项目有着为数众多的参与者。绝大多数的Linux内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991一2002年间)。到2002年,整个项目组开始启用一个专有的分布式版本控制系统BitKeeper来管理和维护代码。

到了2005年,开发BitKeeper的商业公司同Linux内核开源社区的合作关系结束,他们收回了Linux内核社区免费使用BitKeeper的权力。这就迫使Linux开源社区(特别是Linux的缔造者LinusTorvalds)基于使用BitKeeper时的经验教训,开发出自己的版本系统。他们对新的系统制订了若干目标:

  1. 速度
  2. 简单的设计
  3. 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
  4. 完全分布式
  5. 有能力高效管理类似Linux内核一样的超大规模项目(速度和数据量)

自诞生于2005年以来,Git日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统。

6.4 Git的安装

bash 复制代码
yum install git	# CentOs版本

$ git --version	# 查看git的当前版本
git version 1.8.3.1

6.5 在Gitee上面创建项目

这个部分比较长,全部展开来讲太冗余了,毕竟已经写过对应的博客了,而且那里更为详细!因此,本文这里就不再赘述了,详见上面挂着的链接,uu们根据自己的实际需要自行前往。

6.6 三板斧(add / commit / push)

大家之前用过"小乌龟"------TortoiseGit------往gitee上面上传过代码------

6.6.1 git add

将代码放到刚才下载好的目录中------

bash 复制代码
git add [文件名]

将需要用git管理的文件告知git。

6.6.2 git commit

提交改动到本地------

bash 复制代码
git commit -m "XXX"

最后的"."表示当前目录。

提交的时候应该注明提交日志,描述改动的详细内容。

6.2.3 git push

同步到远端服务器上。

需要填入用户名密码。同步成功后,刷新Gitee页面就能看到代码改动了。

6.2.4 配置免密码提交

链接: git本地免密码和账号pull、push

6.2.5 其它

git log/status/pull

.ignore

首次使用问题,需要现场看


7 ~> 调试器 - gdb / cgdb使用

7.1 样例展示:什么样的程序才能调试?

7.1.1 代码演示

样例代码如下所示------

bash 复制代码
  1 #include<stdio.h>
  2 
  3 int Sum(int s,int e)
  4 {
  5     int result = 0;
  6     int i = s;                                                                                                                                                      
  7     for(;i <= e;i++)
  8     {
  9         result += i;
 10     }
 11 
 12     return result;
 13 }
 14 
 15 int main()
 16 {
 17     int start = 1;
 18     int end = 100;
 19     printf("I will begin\n");
 20     int n = Sum(start,end);
 21     printf("running done,result is:[%d-%d]=%d\n",start,end,n);
 22 
 23     return 0;
 24 }

7.1.2 代码运行

bash 复制代码
[Alice@VM-4-17-centos mycmd]$ vim Makefile
[Alice@VM-4-17-centos mycmd]$ make
gcc -o cmd.exe mycmd.c                                                                                                                                                 
[Alice@VM-4-17-centos mycmd]$ ./cmd.exe
I will begin
running done,result is:[1-100]=5050

7.1.3 什么样的程序才能调试------一个既定事实

再看一下其它的描述------

7.2 准备阶段:加上 -g 选项进入debug模式

7.2.1 回顾与新工具概念导入

我们已经熟悉了不少Linux基础开发工具了,像apt / yum,,vim,gcc / g++,make / makefile,git,以及我们今天在这篇博客要介绍并且解决的工具------调试工具:gdb / cgdb

7.2.2 程序的两种发布方式

程序的发布方式有两种:debug 模式和 release 模式,Linux gcc / g++ 出来的二进制程序,默认是 release 模式。

7.2.3 见一见:调试工具gdb

我们首当其冲就是来见一见gdb------

bash 复制代码
[Alice@VM-4-17-centos mycmd]$ gdb cmd.exe
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 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/>...
Reading symbols from /home/Alice/118/linux-git/mycmd/cmd.exe...(no debugging symbols found)...done.
(gdb) quit

7.2.4 发现:使用gdb调试,必须在源代码生成二进制程序的时候,加上 -g 选项

要使用gdb调试,必须在源代码生成二进制程序的时候,加上 -g 选项,如果没有添加,程序无法被编译。

如下图,描述的就很清楚了------

bash 复制代码
$ gcc mycmd.c -o mycmd # 默认模式,不⽀持调试
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically 
linked, interpreter /lib64/ld-linux-x86-64.so.2, 
BuildID[sha1]=82f5cbaada10a9987d9f325384861a88d278b160, for GNU/Linux 
3.2.0, not stripped
$ gcc mycmd.c -o mycmd -g # debug模式
$ file mycmd
mycmd: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically 
linked, interpreter /lib64/ld-linux-x86-64.so.2, 
BuildID[sha1]=3d5a2317809ef86c7827e9199cfefa622e3c187f, for GNU/Linux 
3.2.0, with debug_info, not stripped

7.2.5 演示添加 -g 之后的效果

我们来看一下添加-g之后的效果------

bash 复制代码
[Alice@VM-4-17-centos mycmd]$ vim Makefile
[Alice@VM-4-17-centos mycmd]$ make
gcc -o cmd.exe-debug mycmd.c -g
[Alice@VM-4-17-centos mycmd]$ ./cmd.exe-debug
I will begin
running done,result is:[1-100]=5050
[Alice@VM-4-17-centos mycmd]$ ll
total 32
-rwxrwxr-x 1 Alice Alice 8440 Dec  1 00:49 cmd.exe
-rwxrwxr-x 1 Alice Alice 9696 Dec  1 01:07 cmd.exe-debug
-rw-rw-r-- 1 Alice Alice  290 Dec  1 01:07 Makefile
-rw-rw-r-- 1 Alice Alice  345 Dec  1 00:14 mycmd.c
[Alice@VM-4-17-centos mycmd]$ ./cmd.exe
I will begin
running done,result is:[1-100]=5050
[Alice@VM-4-17-centos mycmd]$ ./cmd.exe-debug
I will begin
running done,result is:[1-100]=5050

7.3 怎么证明确实存在调试信息呢?

上面我们说 -g 给形成的可执行程序内部添加调试信息,那怎么证明确实存在调试信息呢

我们来看看Linux是怎么读取一个二进制文件的格式的------

7.3.1 readelf -S cmd.exe-debug

bash 复制代码
[Alice@VM-4-17-centos mycmd]$ readelf -S cmd.exe-debug
There are 35 section headers, starting at offset 0x1d20:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       0000000000000078  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400330  00000330
       0000000000000044  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000400374  00000374
       000000000000000a  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400380  00000380
       0000000000000020  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             00000000004003a0  000003a0
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004003b8  000003b8
       0000000000000060  0000000000000018  AI       5    23     8
  [11] .init             PROGBITS         0000000000400418  00000418
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400440  00000440
       0000000000000050  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000400490  00000490
       00000000000001e2  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         0000000000400674  00000674
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000400680  00000680
       0000000000000045  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         00000000004006c8  000006c8
       000000000000003c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000400708  00000708
       0000000000000114  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600e10  00000e10
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600e18  00000e18
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000600e20  00000e20
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000600e28  00000e28
       00000000000001d0  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000600ff8  00000ff8
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000038  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000601038  00001038
       0000000000000004  0000000000000000  WA       0     0     1
  [25] .bss              NOBITS           000000000060103c  0000103c
       0000000000000004  0000000000000000  WA       0     0     1
  [26] .comment          PROGBITS         0000000000000000  0000103c
       000000000000002d  0000000000000001  MS       0     0     1
  [27] .debug_aranges    PROGBITS         0000000000000000  00001069
       0000000000000030  0000000000000000           0     0     1
  [28] .debug_info       PROGBITS         0000000000000000  00001099
       000000000000010e  0000000000000000           0     0     1
  [29] .debug_abbrev     PROGBITS         0000000000000000  000011a7
       000000000000008b  0000000000000000           0     0     1
  [30] .debug_line       PROGBITS         0000000000000000  00001232
       0000000000000059  0000000000000000           0     0     1
  [31] .debug_str        PROGBITS         0000000000000000  0000128b
       00000000000000d1  0000000000000001  MS       0     0     1
  [32] .symtab           SYMTAB           0000000000000000  00001360
       0000000000000690  0000000000000018          33    51     8
  [33] .strtab           STRTAB           0000000000000000  000019f0
       00000000000001e2  0000000000000000           0     0     1
  [34] .shstrtab         STRTAB           0000000000000000  00001bd2
       0000000000000148  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

7.3.2 readelf -S cmd.exe-debug | grep -i debug

bash 复制代码
[Alice@VM-4-17-centos mycmd]$ readelf -S cmd.exe-debug | grep -i debug
  [27] .debug_aranges    PROGBITS         0000000000000000  00001069
  [28] .debug_info       PROGBITS         0000000000000000  00001099
  [29] .debug_abbrev     PROGBITS         0000000000000000  000011a7
  [30] .debug_line       PROGBITS         0000000000000000  00001232
  [31] .debug_str        PROGBITS         0000000000000000  0000128b

7.4 常见指令使用(表格形式呈现):调试命令速查表

开始:gdb binFile

退出:Ctrl + d 或 quit调试命令

命令 作用 样例
代码查看命令
list/l 显示源代码,从上次位置开始,每次列出10行 list/l 10
list/l 函数名 列出指定函数的源代码 list/l main
list/l 文件名:行号 列出指定文件的源代码 list/l mycmd.c:1
程序执行控制
r/run 从程序开始连续执行 run
n/next 单步执行,不进入函数内部(逐过程 F10) next
s/step 单步执行,进入函数内部(逐语句 F11) step
continue/c 从当前位置开始连续执行程序 continue
finish 执行到当前函数返回,然后停止 finish
until X行号 执行到指定行号 until 20
断点管理
break/b [文件名:]行号 在指定行号设置断点 break 10
break/b 函数名 在函数开头设置断点 break test.c:10
info break/b 查看当前所有断点的信息 info break
delete/d breakpoints 删除所有断点 delete breakpoints
delete/d breakpoints n 删除序号为n的断点 delete breakpoints 1
disable breakpoints 禁用所有断点 disable breakpoints
enable breakpoints 启用所有断点 enable breakpoints
info/i breakpoints 查看当前设置的断点列表 info breakpoints
变量操作
print/p 表达式 打印表达式的值 print start+end
p 变量 打印指定变量的值 p x
set var 变量=值 修改变量的值 set var i=10
display 变量名 跟踪显示指定变量的值(每次停止时) display x
undisplay 编号 取消对指定编号的变量的跟踪显示 undisplay 1
堆栈和局部变量
backtrace/bt 查看当前执行栈的各级函数调用及参数 backtrace
info/i locals 查看当前栈帧的局部变量值 info locals
其他命令
quit 退出GDB调试器 quit

7.5 cgdb(半图形化界面)

前面的gdb虽然是调试,但是是黑屏,不是那么方便。

这时候就要用到cgdb------半图形化界面------一半代码一半调试信息。

7.5.1 安装cgdb

上面的基本调试还是麻烦,虽然是黑屏,但是还是想看到代码调试(半图形化界面)。

艾莉丝推荐uu们安装cgdb:

Centos版本:

bash 复制代码
sudo yum install -y cgdb

Ubuntu版本:

bash 复制代码
sudo apt-get install -y cgdb

7.5.2 下载演示

7.6 断点

7.6.1 断点的本质

7.6.2 gdb的使用

(gdb) list(l):查看代码,如(gdb) l 0等等,简写成l。

不用花太多时间在l这个命令上面------cgdb有可视化界面(直接看就可以了)。

(gdb) r:即run,运行。

(gdb) b [文件名.后缀名]:[行号] / (gdb) b 21:b + 行号,在21行打一个断点。

(gdb):用指令查看

我们都演示一下------

dart 复制代码
[Alice@VM-4-17-centos linux-git]$ ll
total 36
-rwxrwxr-x 1 Alice Alice 9688 Dec  1 12:35 cmd.exe
drwxrwxr-x 2 Alice Alice 4096 Nov 30 18:03 Jindutiao-2
-rw-rw-r-- 1 Alice Alice   68 Dec  1 12:35 Makefile
drwxrwxr-x 2 Alice Alice 4096 Dec  1 05:14 mycmd
-rw-rw-r-- 1 Alice Alice  345 Dec  1 11:57 mycmd.c
-rw-rw-r-- 1 Alice Alice  848 Nov 30 17:41 README.en.md
-rw-rw-r-- 1 Alice Alice  937 Nov 30 17:41 README.md
[Alice@VM-4-17-centos linux-git]$ gdb cmd.exe
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 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/>...
Reading symbols from /home/Alice/118/linux-git/cmd.exe...done.
(gdb) l
8	    {
9	        result += i;
10	    }
11	
12	    return result;
13	}
14	
15	int main()
16	{
17	    int start = 1;
(gdb) 
18	    int end = 100;
19	    printf("I will begin\n");
20	    int n = Sum(start,end);
21	    printf("running done,result is:[%d-%d]=%d\n",start,end,n);
22	
23	    return 0;
24	}
(gdb) l 0
1	#include<stdio.h>
2	
3	int Sum(int s,int e)
4	{
5	    int result = 0;
6	    int i = s;
7	    for(;i <= e;i++)
8	    {
9	        result += i;
10	    }
(gdb) l 1
1	#include<stdio.h>
2	
3	int Sum(int s,int e)
4	{
5	    int result = 0;
6	    int i = s;
7	    for(;i <= e;i++)
8	    {
9	        result += i;
10	    }
(gdb) r
Starting program: /home/Alice/118/linux-git/cmd.exe 
I will begin
running done,result is:[1-100]=5050
[Inferior 1 (process 32157) exited normally]
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.3.x86_64
(gdb) l 0
1	#include<stdio.h>
2	
3	int Sum(int s,int e)
4	{
5	    int result = 0;
6	    int i = s;
7	    for(;i <= e;i++)
8	    {
9	        result += i;
10	    }
(gdb) 
11	
12	    return result;
13	}
14	
15	int main()
16	{
17	    int start = 1;
18	    int end = 100;
19	    printf("I will begin\n");
20	    int n = Sum(start,end);
(gdb) 
21	    printf("running done,result is:[%d-%d]=%d\n",start,end,n);
22	
23	    return 0;
24	}
(gdb) b 20
Breakpoint 1 at 0x4005cd: file mycmd.c, line 20.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004005cd in main at mycmd.c:20
(gdb) r
Starting program: /home/Alice/118/linux-git/cmd.exe 
I will begin

Breakpoint 1, main () at mycmd.c:20
20	    int n = Sum(start,end);
(gdb) s
Sum (s=1, e=100) at mycmd.c:5
5	    int result = 0;
(gdb) n
6	    int i = s;
(gdb) n
7	    for(;i <= e;i++)
(gdb) n
9	        result += i;
(gdb) 
7	    for(;i <= e;i++)
(gdb) 
9	        result += i;
(gdb) 
7	    for(;i <= e;i++)
(gdb) p result
$1 = 3
(gdb) p &result
$2 = (int *) 0x7fffffffe40c
(gdb) n
9	        result += i;
(gdb) n
7	    for(;i <= e;i++)
(gdb) n
9	        result += i;
(gdb) display result
1: result = 6
(gdb) 
(gdb) 
(gdb) 
(gdb) 
(gdb) 
(gdb) n
7	    for(;i <= e;i++)
1: result = 10
(gdb) n
9	        result += i;
1: result = 10
(gdb) n
7	    for(;i <= e;i++)
1: result = 15
(gdb) n
9	        result += i;
1: result = 15
(gdb) n
7	    for(;i <= e;i++)
1: result = 21
(gdb) n
9	        result += i;
1: result = 21
(gdb) n
7	    for(;i <= e;i++)
1: result = 28
(gdb) 
9	        result += i;
1: result = 28
(gdb) 
7	    for(;i <= e;i++)
1: result = 36
(gdb) 
9	        result += i;
1: result = 36
(gdb) 
7	    for(;i <= e;i++)
1: result = 45
(gdb) 
9	        result += i;
1: result = 45
(gdb) 
7	    for(;i <= e;i++)
1: result = 55
(gdb) 
9	        result += i;
1: result = 55
(gdb) 
7	    for(;i <= e;i++)
1: result = 66
(gdb) 
9	        result += i;
1: result = 66
(gdb) 
7	    for(;i <= e;i++)
1: result = 78
(gdb) 
9	        result += i;
1: result = 78
(gdb) quit
A debugging session is active.

	Inferior 1 [process 32288] will be killed.

Quit anyway? (y or n) y

7.6.3 cgdb的使用


7.6.4 打断点和删除断点

Linux操作系统下,打断点和删除断点------

dart 复制代码
[Alice@VM-4-17-centos linux-git]$ gdb ./process_bar
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 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/>...
Reading symbols from /home/Alice/118/linux-git/process_bar...(no debugging symbols found)...done.
(gdb) l
No symbol table is loaded.  Use the "file" command.
(gdb) list
No symbol table is loaded.  Use the "file" command.
(gdb) r
Starting program: /home/Alice/118/linux-git/./process_bar 
I will begin
running done,result is:[1-100]=5050
[Inferior 1 (process 12969) exited normally]
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.3.x86_64
(gdb) b
No default breakpoint address now.
(gdb) b mycmd.c:20
No symbol table is loaded.  Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (mycmd.c:20) pending.
(gdb) b 21
No symbol table is loaded.  Use the "file" command.
(gdb) info b
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   <PENDING>  mycmd.c:20
(gdb) b 21
No symbol table is loaded.  Use the "file" command.
(gdb) breakpoint 21
Undefined command: "breakpoint".  Try "help".
(gdb) d 1
(gdb) info b
No breakpoints or watchpoints.
(gdb) quit

Windows操作系统下,打断点和删除断点------

再按一次F9或者点一下左侧边界就能删除断点了,但这个操作我们不推荐。

7.6.5 直接删除断点不是一个很好的选择

因为断点体现了调试的痕迹,出现类似的问题方便复现。那么,我们怎么做比较好呢?

直接删除断点不是一个很好的习惯,既然如此,我们就禁用断点------因为 断点是可以"使能"的------enable(打开)或者disable(关闭)的,也就是说,可以启用断点也可以禁用断点。因此,我们就禁用断点------不删,只是这个断点不起作用了------需要时再打开。

7.6.6 "使能"的操作

Linux中就是前面介绍的断点是可以 "使能" 的------

禁用第三个断点------

bash 复制代码
(gdb) disable 3

要启用第三个断点------

bash 复制代码
(gdb) enable 3

7.6.7 深刻理解调试是在干什么

7.6.8 在一个测试周期,断点编号是线性连续的

什么意思?简而言之,run程序之后,新建1、2、3三个断点,如果删掉了第三个断点,再新建断点的时候,断点编号不会再从3开始,而是从4开始------前提是在一个测试周期内!如果退出调试,再进入,编号也就又重新计算啦。

bash 复制代码
(gdb) b	21
Breakpoint 2 at 0x4005df: file mycmd.c, line 21.
(gdb) b	18
Breakpoint 3 at 0x4005bc: file mycmd.c, line 18.
(gdb) info b
Num     Type           Disp Enb Address            What
1	breakpoint     keep y   0x00000000004005a8 in Sum at mycmd.c:12
2	breakpoint     keep y   0x00000000004005df in main at mycmd.c:21
3	breakpoint     keep y   0x00000000004005bc in main at mycmd.c:18
(gdb) d	3
(gdb) info b
Num     Type           Disp Enb Address            What
1	breakpoint     keep y   0x00000000004005a8 in Sum at mycmd.c:12
2	breakpoint     keep y   0x00000000004005df in main at mycmd.c:21
(gdb) b	19
Breakpoint 4 at 0x4005c3: file mycmd.c, line 19.
(gdb) info b
Num     Type           Disp Enb Address            What
1	breakpoint     keep y   0x00000000004005a8 in Sum at mycmd.c:12
2	breakpoint     keep y   0x00000000004005df in main at mycmd.c:21
4	breakpoint     keep y   0x00000000004005c3 in main at mycmd.c:19

如下图所示------

7.6.9 小细节:鼠标不小心滚动之后不在gdb区域了,怎么办?

这样就可以退回到gdb屏幕了------

7.6.10 调试命令小合集

b(breakpoint):打断点。

"使能"------

enable:使用某个断点

disable:禁用某个断点

c(continue):从一个断点运行到下一个断点,如果是最后一个断点,就运行到程序结束。

n、s:小范围进行找问题------即小范围精确(先用断点 b 划分大范围)------

s(step):逐语句(相当于F11)------能够调试得足够细,是单步调试的一种,可以进入函数内部。

n(next):逐过程(相当于F10)------单步执行,不进入函数内部,把函数当成一个语句(或者说命令)来执行。

p(print):打印,用来查看函数结果、变量的值。

上面这些命令使用时我们并没有让程序"跑(run)"起来(打断点、常显示)。

我们可以边调试边打断点(情形:陷入循环,想要跳出)

bash 复制代码
(gdb)b 12 --> c(跳出)

until:直接跳转到(超范围了默认就是跑完函数了)


finish:执行当前函数返回并停止(把该函数执行完)。

VS其实会有一些功能上的重叠(也不能算是冗余),比如说 [监视] 和 [局部变量] ,局部变量我们用 [局部变量] 查看会更方便一点。


bt(backtrace):查看当前函数的调用栈(先进后出后进先出)。

info local:查看局部变量(类似于Windows中的 [局部变量]

7.6.11 查看变量

我们可以查看变量------

这里注意一个小细节,"取消常显示"的操作和断点的新建、删除很类似------常显示也是有1、2、3......这样的编号的,所以也要通过编号来取消!那么,大胆假设、小心求证------取消常显示再新建常显示也是同理,和断点编号一样也是线性的。

经过验证,发现结果和我们的猜想吻合,说明事实的确如此------取消常显示再新建常显示也是同理,和断点编号一样也是线性的。

7.6.12 单步调试

n、s:小范围进行找问题(先用断点 b 划分大范围)------

7.6.13 大范围查找和小范围精确

7.7 三种常见的技巧 - 加餐环节

对比上面的命令,其实下面的命令还是更有价值一点。

7.7.1 监视变量变化:watch

执行时监视一个表达式(如变量)的值。如果监视的表达式在程序运行期间的值发生变化,GDB会暂停程序的执行,并通知使用者

bash 复制代码
(gdb) l main
11 
12 return result;
13 }
14 
15 int main()
16 {
17 int start = 1;
18 int end = 100;
19 printf("I will begin\n");
20 int n = Sum(start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3: file mycmd.c, line 20.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000000011c3 in main at mycmd.c:20
(gdb) r
Starting program: /home/whb/test/test/mycmd 
I will begin
Breakpoint 1, main () at mycmd.c:20
20 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:5
5 {
(gdb) n
6 int result = 0;
(gdb) watch result
Hardware watchpoint 2: result
(gdb) c
Continuing.

Hardware watchpoint 2: result
Old value = -6896
New value = 0
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) c
Continuing.
Hardware watchpoint 2: result
Old value = 0
New value = 1
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) c
Continuing.
Hardware watchpoint 2: result
Old value = 1
New value = 3
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) c
Continuing.
Hardware watchpoint 2: result
Old value = 3
New value = 6
Sum (s=1, e=100) at mycmd.c:7
7 for(int i = s; i <= e; i++)
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
 breakpoint already hit 1 time
2 hw watchpoint keep y result
 breakpoint already hit 4 times
(gdb) d 2
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
 breakpoint already hit 1 time
(gdb) finish
Run till exit from #0 Sum (s=1, e=100) at mycmd.c:7
0x00005555555551d2 in main () at mycmd.c:20
20 int n = Sum(start, end);
Value returned is $1 = 5050

因此,watch常用于监视指针

7.7.2 确定问题原因并且验证原因:set var

set var:可以直接在调试期间修改变量

更改一下标志位(比如flag,+1 / -1这种),假设我们想得到+-result

我们举个例子,比如set var

c 复制代码
// mycmd.c
#include <stdio.h>
int flag = 0; // 故意错误 
//int flag = -1; 
//int flag = 1;
int Sum(int s, int e)
{
 int result = 0;
 for(int i = s; i <= e; i++)
 {
 result += i;
 }
 return result*flag;
}
int main()
{
 int start = 1;
 int end = 100;
 printf("I will begin\n");
 int n = Sum(start, end);
 printf("running done, result is: [%d-%d]=%d\n", start, end, n);
 return 0;
}
bash 复制代码
(gdb) l main
15 
16 return result*flag;
17 }
18 
19 int main()
20 {
21 int start = 1;
22 int end = 100;
23 printf("I will begin\n");
24 int n = Sum(start, end);
(gdb) b 24
Breakpoint 1 at 0x11ca: file mycmd.c, line 24.
(gdb) r
Starting program: /home/whb/test/test/mycmd 
I will begin
Breakpoint 1, main () at mycmd.c:24
24 int n = Sum(start, end);
(gdb) n
25 printf("running done, result is: [%d-%d]=%d\n", start, end, 
n);
(gdb) n
running done, result is: [1-100]=0 # 这⾥结果为什么是0?
26 return 0;
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/whb/test/test/mycmd 
I will begin
Breakpoint 1, main () at mycmd.c:24
24 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:9
9 {
(gdb) n
10 int result = 0;
(gdb) n
11 for(int i = s; i <= e; i++)
(gdb) 
13 result += i;
(gdb) 
11 for(int i = s; i <= e; i++)
(gdb) 
13 result += i;
(gdb) until 14
Sum (s=1, e=100) at mycmd.c:16
16 return result*flag;
(gdb) p result
$1 = 5050
(gdb) p flag
$2 = 0
(gdb) set var flag=1 # 更改flag的值,确认是否是它的原因
(gdb) p flag
$3 = 1
(gdb) n
17 }
(gdb) n
main () at mycmd.c:25
25 printf("running done, result is: [%d-%d]=%d\n", start, end, 
n);
(gdb) n
running done, result is: [1-100]=5050 # 是它的原因
26 return 0;

7.7.3 条件断点(也是断点)

条件断点的类型和普通断点一样,也是breakpoint,多了个条件------"stop only . . .([条件])"

7.7.3.1 添加条件断点
bash 复制代码
(gdb) l main
11 
12 return result;
13 }
14 
15 int main()
16 {
17 int start = 1;
18 int end = 100;
19 printf("I will begin\n");
20 int n = Sum(start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3: file mycmd.c, line 20.
(gdb) r
Starting program: /home/whb/test/test/mycmd 
I will begin
Breakpoint 1, main () at mycmd.c:20
20 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:5
5 {
(gdb) n
6 int result = 0;
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb) display i
1: i = 1
(gdb) n
7 for(int i = s; i <= e; i++)
1: i = 1
(gdb) n
9 result += i;
1: i = 2
(gdb) n
7 for(int i = s; i <= e; i++)
1: i = 2
(gdb) n
9 result += i;
1: i = 3
(gdb) 
7 for(int i = s; i <= e; i++)
1: i = 3
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
 breakpoint already hit 1 time
(gdb) b 9 if i == 30 # 9是⾏号,表⽰新增断点的位置
Breakpoint 2 at 0x555555555186: file mycmd.c, line 9.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
 breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c:9
 stop only if i == 30
(gdb) finish
Run till exit from #0 Sum (s=1, e=100) at mycmd.c:7
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
1: i = 30
(gdb) finish
Run till exit from #0 Sum (s=1, e=100) at mycmd.c:9
0x00005555555551d2 in main () at mycmd.c:20
20 int n = Sum(start, end);
Value returned is $1 = 5050
7.7.3.2 给已经存在的断点新增条件
bash 复制代码
(gdb) l main
11 
12 return result;
13 }
14 
15 int main()
16 {
17 int start = 1;
18 int end = 100;
19 printf("I will begin\n");
20 int n = Sum(start, end);
(gdb) b 20
Breakpoint 1 at 0x11c3: file mycmd.c, line 20.
(gdb) r
Starting program: /home/whb/test/test/mycmd 
I will begin
Breakpoint 1, main () at mycmd.c:20
20 int n = Sum(start, end);
(gdb) s
Sum (s=32767, e=-7136) at mycmd.c:5
5 {
(gdb) n
6 int result = 0;
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb) 
7 for(int i = s; i <= e; i++)
(gdb) 
9 result += i;
(gdb) 
7 for(int i = s; i <= e; i++)
(gdb) 
9 result += i;
(gdb) 
7 for(int i = s; i <= e; i++)
(gdb) b 9 # 我们在第9⾏新增⼀个断点,⽤来开始测试
Breakpoint 2 at 0x555555555186: file mycmd.c, line 9.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c:9
(gdb) n
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
(gdb) condition 2 i==30 #给2号断点,新增条件i==30
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551c3 in main at mycmd.c:20
 breakpoint already hit 1 time
2 breakpoint keep y 0x0000555555555186 in Sum at mycmd.c:9
 stop only if i==30
 breakpoint already hit 2 times
(gdb) n
7 for(int i = s; i <= e; i++)
(gdb) n
9 result += i;
(gdb) c
Continuing.
Breakpoint 2, Sum (s=1, e=100) at mycmd.c:9
9 result += i;
(gdb) p i
$1 = 30
(gdb) p result
$2 = 435
7.7.3.3 注意

条件断点添加常见两种方式:1、新增;2、给已有断点追加(注意两者的语法有区别,不要写错了!),

新增:b行号 / 文件名------

bash 复制代码
行号 / 函数名 if i == 30(条件)

给已有断点追加条件:condition 2 i == 30,其中2是已有断点编号,没有if。

cgbd分屏操作ESC进入代码屏,按 i 回到gdb屏


结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾

【Linux基础开发工具 (六)】Linux中的第一个系统程序------进度条Linux:详解回车、换行与缓冲区

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
2501_941982052 小时前
企业微信客户联系API中 添加客户 与 发送欢迎语 的异步处理优化
服务器·数据库·php
shandianchengzi2 小时前
【记录】ARM|Ubuntu 24 快速安装 arm-none-eabi-gdb 及 QEMU 调试实战
linux·arm开发·ubuntu·arm·qemu
悬镜安全2 小时前
悬镜安全率先通过国家工信安全中心SBOM标准认证
安全
weixin_307779132 小时前
Jenkins SSH Build Agents 插件详解:远程构建的利器
运维·开发语言·架构·ssh·jenkins
yuguo.im2 小时前
Elasticsearch 的倒排索引原理
大数据·elasticsearch·搜索引擎
cike_y2 小时前
JavaWeb之HttpServletResponse
java·开发语言·安全·java安全
学困昇2 小时前
Linux 进程概念与内存管理详解(含冯诺依曼体系结构、环境变量、调度算法)
linux·c语言·开发语言·网络·数据结构·c++
靈龍靈2 小时前
ELBK部署
运维·ci/cd·jenkins
为什么要内卷,摆烂不香吗2 小时前
sed 流编辑器练习自用
linux·运维·编辑器