In this tutorial I'm going to share some useful compilation and debugging tips. These tips can save you a ton of time debugging and make compilation and debugging convenient.
Useful compilation flags
G++ has many useful warning flags such as
-Wall
enables all warnings. I highly recommend using this flags.-Wextra
enables extra warnings.-Wno-sign-conversion
silences sign conversion warnings for code likex < vec.size()
-Wshadow
enables shadowing warnings, so that if you define another variable with the same name in a local scope, you will get a warning.
Using these flags can save you time debugging silly mistakes like forgetting to return a value in a function or doing a comparison like x < vec.size()-1
where vec.size()-1
can underflow into SIZE_MAX-1
.
Precompiled headers
You can use precompiled headers to substantially speed up the compilation of your code. Note that the precompiled header is only used if it is compiled with the same flags that your code is compiled with. You can store the precompiled headers in the folder bits/stdc++.h.gch and G++ will automatically find the correct precompiled header (with the correct flags). You can store multiple precompiled headers for bits/stdc++.h for different sets of flags.
sudo g++ -Wall -Wextra -Wshadow -D_GLIBCXX_ASSERTIONS -DDEBUG -ggdb3 -fmax-errors=2 -std=c++17 -o /usr/include/x86_64-linux-gnu/c++/9/bits/stdc++.h{.gch/ASSERT_DEBUG_17.gch,}
Useful debugging flags
Debugging is an important part of CP. After all, even tourist gets RTE on his submissions occasionally.
-g
enables basic debug information-ggdb3
enables extended debug information such as macros. I suggest using this generally. It slows down compilation a little bit, but has no effect on run-time, since it just adds an extra piece of data in the executable that helps the debugger map the executable file position to line numbers and other related information.-D_GLIBCXX_DEBUG
enables out of bounds checking and checks that your comparison operator is irreflexive (comp(a, a)
should return false), and many other useful things. However, this can slow down your code runtime (and compile time) substantially.-D_GLIBCXX_ASSERTIONS
enables light-weight debug checks such as out of bounds checking. I suggest always using this when compiling locally, as it has negligible runtime and compile-time performance impact.-fmax-errors=<n>
limits the number of errors displayed ton
. This stops the compiler from printing an excessive amount of error messages.-DDEBUG
is used in my debug macro (see below) to enable debugging. Add this to your compilation flags locally to enable debugging. Since this macro is not defined in CodeForces' compilation options, when your code is judged debugging will be disabled.
The _GLIBCXX_*
macros are documented in the libstdc++
documentation.
Note that the names of these macros start with an underscore _
. The flag -D<macro>
defines the macro macro
, so you can also use these macros by defining them in the first line before you include any headers.
Example:
#ifdef DEBUG
#define _GLIBCXX_DEBUG
#endif
#include <bits/stdc++.h>
How to use these flags
Add the compilation flag -D_GLIBCXX_DEBUG
to define the macro, or likewise -D_GLIBCXX_ASSERTIONS
.
Recommended compiler flags
-Wall -Wextra -Wshadow -D_GLIBCXX_ASSERTIONS -DDEBUG -ggdb3 -fmax-errors=2
AddressSanitizer and UndefinedBehaviorSanitizer
The _GLIBCXX_*
macros only do debug checks for STL containers and algorithms. So you should use std::array
instead of C arrays. For checking for C array out of bounds, use AddressSanitizer with -fsanitize=address
. For checking undefined behavior like arithmetic overflow etc use -fsanitize=undefined
. You can use them both with -fsanitize=address,undefined
.
When upsolving a problem, if you solution does not AC, CodeForces runs sanitizers on it. So you can check the output to see if the sanitizers caught any error.
Debug macro
A useful tool is to have a debug macro that can print useful information like the line number, variable name, etc. Remember to add -DDEBUG
to your local compilation flags to enable this debug macro.
#include <bits/stdc++.h>
using namespace std;
// === Debug macro starts here ===
int recur_depth = 0;
#ifdef DEBUG
#define dbg(x) {++recur_depth; auto x_=x; --recur_depth; cerr<<string(recur_depth, '\t')<<"\e[91m"<<__func__<<":"<<__LINE__<<"\t"<<#x<<" = "<<x_<<"\e[39m"<<endl;}
#else
#define dbg(x)
#endif
template<typename Ostream, typename Cont>
typename enable_if<is_same<Ostream,ostream>::value, Ostream&>::type operator<<(Ostream& os, const Cont& v){
os<<"[";
for(auto& x:v){os<<x<<", ";}
return os<<"]";
}
template<typename Ostream, typename ...Ts>
Ostream& operator<<(Ostream& os, const pair<Ts...>& p){
return os<<"{"<<p.first<<", "<<p.second<<"}";
}
// === Debug macro ends here ===
int func(vector<pair<int, string>>& vec){
dbg(vec);
return 42;
}
int main(){
vector<pair<int, string>> vec{{1, "code"}, {2, "forces"}};
dbg(func(vec));
}
Debug output
Note that the debug output is indented based on the recursion level. Also, the output is in red to aid in differentiating debug output from regular output.
Nifty isn't it?
How does the debug macro work?
(you may skip these technical details)
The Ostream
template parameter is used so that this operator<<
overload has low priority. The operator<<
template is also constrained by enable_if
so that it does not get called when std::bitset::operator<<
is intended to be called.
Debugging using gdb
To debug using gdb, this bash command is useful:
(echo "run < a.in" && cat) | gdb -q a
This automatically runs a
with input from a.in
.
Quick gdb tutorial
start
starts your code, pausing execution atmain()
run
runs your code until a breakpoint is hitbreak <function>
pauses execution when a function is called- Use
up
anddown
to move up and down the stack. jump <line_no>
can be used to jump to a line number.continue
can be used to continue executionnext
goes to the next line in the current functionstep
steps into the next line executed (so if you call a function it enters the function's code)- You can type abbreviations of these commands, such as
r
forrun
.
Adding these flags and commands to ~/.bashrc
If you use the terminal to compile, you can add these functions to your .bashrc
file in your home folder (e.g. C:\Users\my_username
on Windows). This shell script in this file is executed by bash when you open the terminal. Note that you need to restart your terminal or enter . .bashrc
for the changes to take effect.
function compile {
g++ -Wall -Wextra -Wshadow -D_GLIBCXX_ASSERTIONS -DDEBUG -ggdb3 -fmax-errors=2 -o $1{,.cpp}
}
function debug {
(echo "run < $1.in" && cat) | gdb -q $1
}
Using these bash functions:
compile a # Compiles a.cpp to a
debug a # Runs a in the debugger with input from a.in
I hope you found this tutorial useful. Feel free to ask any questions or give feedback.
EDIT: Added section on precompiled headers.
Thank you for the nice article.
Btw does compile function assumes that our
.cpp
file exists in the same directory as.bashrc
file(it seems so to me), and if it is then how to make it work such that.cpp
file can be anywhere?The compile function can be used for compiling files outside the home directory. You just need to pass the file path without the
.cpp
extension.Example:
compile my_contest/my_program
This will compile
my_contest/my_program.cpp
and produce an executablemy_contest/my_program
The
compile
function substitutes the 1st argument given for$1
in the function's code.If this is the way, then instead of passing the directory manually the better thing to use would be:
compile $(pwd)/fie_name
.You don't need to do that.
compile file_name
will compile file_name.cpp from the current directory.It wasn't working for me then that's why i asked that question in my first comment but now it does(idk what was the problem) btw thanks.
I would recommend using
start <$1.in
instead ofrun <$1.in
. Otherwise, the program will start running automatically and it might skip the important things you wanted to examine and need to restart it.You can replace the echo, cat and pipe trick. The same thing can be done with
gdb -q $1 -ex "run <$1.in"
.Other than that, I can recommend the option
gdb --tui
. It splits the gdb window into two parts, the normal interactive prompt for the commands, and a code window that shows the current position in the code (this avoids runningl
over and over and helps orientation a lot).codeforces supports C++17, why not to use it?
Yes, you could make it a bit more compact with C++17, but I made the code work on C++11 so that it can be used on other judges.
You could put these definitions inside the
#ifdef DEBUG
and cpp version of the server won't matter.Really informative! Thanks for the blog!
How can I detect a Runtime Error quickly? Using a GDB User Interface really helps but it's a little inconvenient to me (I am using Sublime text).
When you run your code outside the debugger an error message saying you indexed out of bounds will be displayed.
Hi, really nice blog. One small note:
And after all, why don't to use
ostream &
?Why should we use this flag if it silences the warnings?
The warnings are sometimes useful in cases like -1 < v.size(), but usually they create excessive warnings (which are distracting), unless you have a macro/function which returns the signed size.
i've found
valgrind
to typically be more useful to me in a contest setting thatgdb
, since you don't need to set up breakpoints or anything.Could you give me a suggestion? I wonder that in competitive programming debugging with gdb will improve efficiency or not? Now I just use IDE such as Dev Cpp, print something for debugging. Is necessary for me to learn gdb and use it in debugging for competitive programming? If you could gvie me advice, I would so grateful.
Nice blogpost! To make code look cleaner, you could also store the debug macros and functions in a separate "debug.hpp" file and link it in your code skeleton/template.
Good idea, that's exactly what I do in my code.