我们这里使用一台Ubuntu24虚拟机,vscode使用ssh连接到这台机器,然后就可以开始调试了
命令
g++ -o server ./server2.cpp ./common/common.cpp -I . g++ client.cpp -o client
经历了1和2后我们现在已经有了能够运行的代码,那么如何在linux上使用gdb调试他呢?不能一直用print然后看输出来调试吧, 我们要有单步 堆栈和变量!本节就是总结在linux上的调试方法(针对单文件 项目的我还没学)![]()
我们使用g++在linux上进行调试,首先我们要先编译出一个debug版本,这里从基础说起
通用知识
如何编译一个程序
我们的目录结构是
├── common
│ ├── common.cpp
│ └── common.h
└── server.cpp
其中的结构是server.cpp include了common.h,common.cpp又是common.h的实现
g++ A.cpp B.cpp -o output -I .
这里涉及到一些编译原理的内容,因为编译器实际上并不会单独处理头文件(头文件不是编译单元),他只处理.cpp,头文件只是一种便利的声明,在被include到文件后会被展开,如
// header.h
void functionA();
void functionB();
// impletion.cpp
#include "header.h"
void functionA(){
实现
}
void functionB(){
实现
}
// main.cpp
#include "header.h"
int main(){
}
实际上主main和impletion会被展开成
// impletion.cpp
void functionA();
void functionB();
void functionA(){
实现
}
void functionB(){
实现
}
// main.cpp
void functionA();
void functionB();
int main(){
}
如果你自己每个文件都手敲声明的话比较麻烦,所以c++允许你写到一个单独的文件里然后include他们。但是对编译器来说,在头文件被展开后就不单独看了;到了编译这一步头文件已经被展开到各个实际的文件里了。所以回到刚才的话题,我们要传给g++的参数是server.cpp和common.cpp,即头文件的实现文件和主文件。
-o表示你要输出的二进制的文件名,如output,-I表示搜索头文件的根目录,编译器在展开头文件的阶段会找include提到的文件名;我们的头文件放在当前文件夹的common下,就从当前文件夹开始搜索,写common也行,但是以后可能还有别的文件夹,直接从当前文件夹开始方便以后扩展。
如何编译出debug版本的程序
主要是两点
-
添加调试信息 -g
-
关闭大部分优化 -O0
g++ A.cpp B.cpp -o output -I . -g -O0
-O(大写)后面跟的数字是优化等级,可以是0 1 2 3 s等等,代表不优化/基础/通用/激进/体积
我们一般的release版本优化的等级就是-O2, debug版本自然要比2低,这里直接选0, 防止优化过度导致调试的时候断点乱跳/缺失信息
额外的警告
为了开启所有警告还要加上
-
-Wall 所有常见警告(不是所有警告 是所有常见)
-
-Wextra 额外警告
-
-Wpedantic 迂腐的 严格的检查是否符合指定的c++标准,无扩展无非标准写法,有的话警告
g++ -std=c++17 A.cpp B.cpp -o output -I . -g -O0 -Wall -Wextra -Wpedantic
最终的命令变成了这样
kisaragi@ubuntu24:~/Documents/Epoll$ g++ -std=c++17 server2.cpp common/common.cpp -g -O0 -Wextra -Wall -Wpedantic -o testDebug
编译成功 我们得到了testDebug二进制文件
古法 直接使用GDB
打开你的终端,使用gdb 二进制文件 命令来调试
可以通过file 二进制文件 来查看二进制的信息
正常的应该是with debug_info, not stripped,表示有调试信息,符号未被剥离,这里我们自己编的,应该不会错
启动命令,正式进入到gdb的界面中
但是还没显示代码,按ctrl+x后按a键打开代码显示,效果如下,看起来就清晰多了
gdb里的操作都是通过在(gdb)后面打命令实现的,如next, step,break等等;
gdb有很多命令,对于习惯windows调试的我们来说,首先要找出怎么打断点,怎么继续,怎么进入函数, 怎么查看堆栈
流程基础操作
F5继续-->continue
F10下一行-->next
F11进入-->step
我们尝试在main上打一个断点,打断点的命令是break 函数名或者行号,我们直接berak main(),就在main函数上打了个断点,
代码旁边也会显示一个小小的标志,现在可以直接将程序运行起来, 命令是run,等待一会,程序就会断在main第一行了
B+旁边的>角标就是当前行的指示器,我们输入两个next,让他运行到190行,然后看看threads的内容,查看变量的内容是
也可以直接通过start来启动,他的作用和在main上打个断点然后run是一样的
查看变量
print 变量
可以看到目前还没有内容,这是对的,因为刚初始化,监视变量也很简单,使用watch 变量 就可以进行监视,watch threads,当threads被改变时就会断住。
想要删除断点/监视也很简单,使用 info breakpoints查看所有断点
这里可以看到有我们在main里打的,还有刚刚watch的变量,然后我们就可以通过disable 编号 或者 delete 编号来编辑了,如这里删除3号断点,使用delete 3,再查看就没有了
断点操作
break 行号/函数名() 打断点
info breakpoints 查看断点
delete 断点
现在我们随便进一个函数看看,比如threads.push_back()的push_back方法,先运行到这一行,然后输入step就可以进入了
进出函数
step 相当于f11进入
finish 相当于shift+11出函数
成功进入,不想看了就输入finish,就可以进入上一级,相当于shift+f11, 如果你已经step进入很深了,需要多几次才能出去。也可以直接在外面打断点然后continue,和我们在windows上使用都是一样的
调用堆栈
bt或backtrace
常用的就这些命令,玩够了,打quit直接退出
使用vscode
直接使用vscode的remote ssh插件连接到你的服务器,打开文件夹(关于这个插件的使用可以网上搜教程)
终端里tree一下(tree .),复制文件夹结构
├── common
│ ├── common.cpp
│ └── common.h
└── server.cpp
在你的源码目录下新建一个.vscode文件夹,里面新建个launch.json,然后打开ai:
我想要用gdb调试程序,需要/不需要编译,给我写一个vscode里的launch.json,操作系统是xxx,
文件夹结构如下:
├── common
│ ├── common.cpp
│ └── common.h
└── server.cpp
ai直接拿捏,这里因为我说需要编译, ai把tasks.json也补上了,都拷贝进来就好了
然后打开你的源代码,在vscode里按F5,选择使用g++或类似的选项(使用gdb?可能),ai的配置写的没错就可以顺利启动了
这时直接就可以看到堆栈,断点,监视,变量等信息,比gdb要现代化的多,但他的信息其实也是从gdb里取的,只是换了种展示形式;现在你可以像在windwos里一样调试程序了
本节完
3 个帖子 - 2 位参与者