Last year, I had a colleague who debugged C++ programs very impressively from the command line, which made me quite envious. I planned to write an article introducing how to use gdb for debugging in the command line, but I procrastinated for a long time. This time, I can’t delay any longer.
Installing GCC and GDB on Ubuntu (WSL)
Since we are using the command line, we should definitely try it on WSL first. After all, it’s just a single command:
sudo apt install gcc
However, there were issues with the installation of 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.
My system is Ubuntu 24.04 under WSL2, and I replaced the software source with Tsinghua’s Tuna source. I have tried many ways to solve this problem, including: installing other versions of gcc:
sudo apt install gcc-13
gcc-13 is available in the current software source, which can be checked with the following command:
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
Or some common methods, such as updating all local software:
sudo apt upgrade
Performing cleanup and auto-clean:
sudo apt clean
sudo apt autoclean
Fixing dependencies:
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.
Anyway, this problem was not resolved by the time I finished writing this article, so I decided to set it up on Windows instead. The gnu purists probably couldn’t wait to build from source at this point, but I prefer the convenience of installation on Windows.
Installing GCC and GDB on Windows
To be precise, I installed MingW-w64; this set of tools is unique to Linux, and Windows usage is merely a simulation of Linux.
I had installed gcc once when I built my computer in 2023, and I downloaded it from this site:
https://files.1f0.de/mingw/
I don’t even remember which tutorial I followed to download it; the compressed package did not include gdb, so if I insisted on using this version of gcc, I would have to compile it from source. Considering that I don’t have any large C++ projects on my computer, I decided to reinstall it.
The official MingW-w64 website includes some pre-built toolchains, so there’s no need to build it yourself:
https://www.mingw-w64.org/downloads/#mingw-w64-buildsYou can directly download the compressed package from GitHub; there is no need to download the official installer (although that installer merely downloads and unzips the selected compressed package, and you have to set the environment variables yourself).https://github.com/niXman/mingw-builds-binaries/releases
You might not be able to access the above link, feel free to ask me for the compressed package.
After a lot of hassle, I finally completed the installation:
Using GDB to Debug C++ Programs in Command Line
Taking a program I previously wrote to test the double equals operator as an example:
#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;
}
Execute the following command to compile the C++ program in debug mode:
g++ -g -o test testOP.cpp
Use gdb to debug the program:
gdb .\test.exe
If your terminal cannot use the backspace key to delete when entering commands in gdb (especially when using the built-in terminal of VS Code), it may be that the terminal has not unlocked the edit mode. You can unlock it using the following command:
set editing off
Viewing Code
<span>l line_number</span>
displays 10 lines of code starting from this line
(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 {
Entering<span>l 0</span>
has the same effect. l is an abbreviation for list; gdb has many uses where single words are abbreviated to a letter. If you are unsure, feel free to use it; when an abbreviation is ambiguous, gdb will ask you.
For example, if I enter<span>d b</span>
, gdb won’t know what my b refers to.
(gdb) d b
Ambiguous delete command "b": bookmark, breakpoints.
gdb automatically remembers commands and will repeat the command when you press enter without any input. In some cases, it will intelligently modify the command. For example, as long as you keep hitting enter, gdb will continuously update the parameters of the<span>list</span>
command, displaying the complete code:
(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
Breakpoints
<span>b line_number</span>
sets a breakpoint at that line; b is an abbreviation for breakpoint
(gdb) b 20
Breakpoint 2 at 0x7ff70ac6145d: file testOP.cpp, line 20.
<span>b source_file:function_name</span>
sets a breakpoint at the first line of that function
(gdb) b testOP.cpp:operator==
Breakpoint 3 at 0x7ff70ac62a3b: file testOP.cpp, line 11.
<span>info b</span>
displays information about breakpoints
(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
<span>d breakpoint_number</span>
deletes a breakpoint; d is an abbreviation for 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
<span>d breakpoints</span>
deletes all breakpoints (breakpoints cannot be abbreviated)
(gdb) d breakpoints
Delete all breakpoints? (y or n) Y
(gdb) info b
No breakpoints or watchpoints.
Debugging
<span>r</span>
runs the program until the first breakpoint; r is an abbreviation for 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};
<span>n</span>
runs the program line by line without entering functions (stepping into); n is an abbreviation for 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]
<span>s</span>
runs the program statement by statement, entering functions; s is an abbreviation for 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]
Exiting GDB
<span>exit</span>
exits the gdb command line