去年我有个同事在命令行下非常帅气地调试C++程序,确实羡慕到我了,我就计划写篇文章介绍用gdb在命令行下调试,结果一拖就拖了很久,这次还是不能再拖了。
在Ubuntu(WSL)下安装gcc和gdb
都用命令行了,那肯定先在wsl上试试啊,毕竟一句话的事:
sudo apt install gcc
然后就没有然后了,gcc的安装始终在报错:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:
The following packages have unmet dependencies:
libasan8 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
libatomic1 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
libcc1-0 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
libhwasan0 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
libitm1 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
liblsan0 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
libquadmath0 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
libtsan2 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
libubsan1 : Depends: gcc-14-base (= 14.2.0-4ubuntu2~24.04) but 14.2.0-4ubuntu2 is to be installed
E: Unable to correct problems, you have held broken packages.
我的系统是 WSL2 下的 Ubuntu 24.04,软件源被我替换成了清华的tuna源,我尝试过很多办法解决这个问题,包括:
安装其他版本的gcc:
sudo apt install gcc-13
gcc-13在当前软件源是可用的,通过以下命令可以查询到:
apt-cache policy gcc-13
gcc-13:
Installed: (none)
Candidate: 13.3.0-6ubuntu2~24.04
Version table:
13.3.0-6ubuntu2~24.04 500
500 https://mirrors.tuna.tsinghua.edu.cn/ubuntu noble-updates/main amd64 Packages
500 https://mirrors.tuna.tsinghua.edu.cn/ubuntu noble-security/main amd64 Packages
500 http://archive.ubuntu.com/ubuntu noble-updates/main amd64 Packages
500 http://security.ubuntu.com/ubuntu noble-security/main amd64 Packages
13.2.0-23ubuntu4 500
500 https://mirrors.tuna.tsinghua.edu.cn/ubuntu noble/main amd64 Packages
500 http://archive.ubuntu.com/ubuntu noble/main amd64 Packages
或者一些常规的办法,比如更新所有本地的软件:
sudo apt upgrade
执行清理和自动清理:
sudo apt clean
sudo apt autoclean
修复依赖:
sudo apt --fix-broken install
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
0 upgraded, 0 newly installed, 0 to remove and 180 not upgraded.
反正这个问题在我文章写完的时候还没解决,我就先在Windows上搞这个了。gnu的原教旨主义者这时候估计就摩拳擦掌准备从源代码构建了,但我比较懒,我觉得Windows安装会更方便一点。
在Windows上安装gcc和gdb
准确来说我安装的是MingW-w64,gcc这一套东西都是Linux特有的,Windows的使用仅仅是模拟Linux。
我电脑在2023年装机的时候装过一次gcc,是从这个网站下载的:https://files.1f0.de/mingw/
我都不记得当时看哪个教程下载的,压缩包里没有gdb,如果我坚持用这个版本的gcc就只能从源代码编译了。考虑到我电脑上没什么C++大项目,我直接重新装一个。
MingW-w64的官网包含一些预构建的工具链,不必非得在自己电脑构建一遍:
https://www.mingw-w64.org/downloads/#mingw-w64-builds
直接在github下压缩包即可,官方的安装器都不需要下(虽然那个安装器也不过是把选好的压缩包下载到本地解压了,环境变量都要自己搞)
https://github.com/niXman/mingw-builds-binaries/releases
上面链接你可能打不开,可以找我要压缩包。
反正一通折腾总算安装完了:
使用gdb在命令行下调试C++程序
以我之前写的一个测试双等于号的程序为例子:
#include <iostream>
using namespace std;
struct S
{
public:
S(int num) : num(num) {};
bool operator==(S s)
{
return this->num == s.num;
}
private:
int num;
};
int main()
{
S s1{1};
S s2{1};
cout << (s1 == s2) << endl;
}
执行以下命令,在debug模式下编译C++程序:
g++ -g -o test testOP.cpp
使用gdb debug程序:
gdb .\test.exe
如果你的终端在gdb输入命令时,无法使用backspace键删除(尤其是使用VS Code自带的终端),可能是终端没有解锁编辑模式,使用以下命令解锁即可:
set editing off
查看代码
l 行号
显示从此行开始的10行代码
(gdb) l 1
1 #include <iostream>
2
3 using namespace std;
4
5 struct S
6 {
7 public:
8 S(int num) : num(num) {};
9 bool operator==(S s)
10 {
这里输入l 0
也是一样的效果。l是list的缩写,gdb有很多将单个单词缩写成一个字母的用法,如果你无法确定,放心大胆地用,当缩写有歧义时,gdb会询问你。
比如这里我输入d b
,gdb就不知道我的b指的是什么。
(gdb) d b
Ambiguous delete command "b": bookmark, breakpoints.
gdb会自动记忆命令,并在你不进行任何输入而直接按下回车时重复命令,某些情况下,会智能对命令进行修改。比如这里,只要你一直敲击回车,gdb就会不断更新list
命令的参数,打出完整的代码:
(gdb) l 1
1 #include <iostream>
2
3 using namespace std;
4
5 struct S
6 {
7 public:
8 S(int num) : num(num) {};
9 bool operator==(S s)
10 {
(gdb)
11 return this->num == s.num;
12 }
13
14 private:
15 int num;
16 };
17
18 int main()
19 {
20 S s1{1};
(gdb)
21 S s2{1};
22 cout << (s1 == s2) << endl;
23 }
(gdb)
End of the file was already reached, use "list ." to list the current location again
断点
b 行号
在那一行打断点,b是breakpoint的缩写
(gdb) b 20
Breakpoint 2 at 0x7ff70ac6145d: file testOP.cpp, line 20.
b 源文件:函数名
在该函数的第一行打上断点
(gdb) b testOP.cpp:operator==
Breakpoint 3 at 0x7ff70ac62a3b: file testOP.cpp, line 11.
info b
查看断点的信息
(gdb) info b
Num Type Disp Enb Address What
2 breakpoint keep y 0x00007ff70ac6145d in main() at testOP.cpp:20
3 breakpoint keep y 0x00007ff70ac62a3b in S::operator==(S) at testOP.cpp:11
d 当前要删除断点的编号
删除一个断点,d是delete的缩写
(gdb) d 2
(gdb) info b
Num Type Disp Enb Address What
3 breakpoint keep y 0x00007ff70ac62a3b in S::operator==(S) at testOP.cpp:11
d breakpoints
删除所有的断点(breakpoints不能简写)
(gdb) d breakpoints
Delete all breakpoints? (y or n) Y
(gdb) info b
No breakpoints or watchpoints.
调试
r
运行程序直到第一个断点,r是run的缩写
(gdb) b 21
Breakpoint 4 at 0x7ff70ac6146e: file testOP.cpp, line 21.
(gdb) r
Starting program: D:\Codes\leetcode\3133\test.exe
[New Thread 27792.0x40c0]
[New Thread 27792.0x6c60]
[New Thread 27792.0x2d54]
Thread 1 hit Breakpoint 4, main () at testOP.cpp:21
21 S s2{1};
n
逐行运行,不进入函数(进栈),n是next的缩写
(gdb) r
Starting program: D:\Codes\leetcode\3133\test.exe
[New Thread 5928.0x2170]
[New Thread 5928.0x6c68]
[New Thread 5928.0x6a3c]
Thread 1 hit Breakpoint 4, main () at testOP.cpp:21
21 S s2{1};
(gdb)
(gdb) n
22 cout << (s1 == s2) << endl;
(gdb)
1
23 }
(gdb)
0x00007ff70ac612ef in __tmainCRTStartup ()
(gdb)
Single stepping until exit from function __tmainCRTStartup,
which has no line number information.
[Thread 5928.0x2170 exited with code 0]
[Thread 5928.0x6c68 exited with code 0]
[Thread 5928.0x6a3c exited with code 0]
[Inferior 1 (process 5928) exited normally]
s
逐语句运行,会进入函数,s是step的缩写
(gdb) s
S::S (this=0x5ffec8, num=1) at testOP.cpp:8
8 S(int num) : num(num) {};
(gdb)
main () at testOP.cpp:22
22 cout << (s1 == s2) << endl;
(gdb)
S::operator== (this=0x5ffecc, s=...) at testOP.cpp:11
11 return this->num == s.num;
(gdb)
12 }
(gdb)
1
main () at testOP.cpp:23
23 }
(gdb)
0x00007ff70ac612ef in __tmainCRTStartup ()
(gdb)
Single stepping until exit from function __tmainCRTStartup,
which has no line number information.
[Thread 28652.0x6ed0 exited with code 0]
[Thread 28652.0x5db0 exited with code 0]
[Thread 28652.0x6eec exited with code 0]
[Inferior 1 (process 28652) exited normally]
退出gdb
exit
退出gdb命令行