Basic Debugging
Authors: Benjamin Qi, Aaron Chew, Aryansh Shrivastava, Owen Wang
How to identify errors within your program or avoid them in the first place.
Prerequisites
Resources | ||||
---|---|---|---|---|
AryanshS | Some parts were taken from here. | |||
LCPP | How to add print statements. |
Printing Variables
Basic Print Statements
The most basic way that you might debug is adding a print statement. This is
great and serves the purpose for the most part. For instance, we can write the
below to check the value of x
at a point in our code.
C++
#include <iostream>using namespace std;int x = 10; // pretend this variable is importantvoid dbg() { cout << "x = " << x << endl; }int main() {dbg(); // x = 10x = 5000;dbg(); // x = 5000}
Java
public class Main {static int x = 10; // pretend this variable is importantpublic static void main(String[] args) {dbg(); // x = 10x = 5000;dbg(); // x = 5000}static void dbg() {System.out.print("x = ");System.out.println(x);}}
Python
x = 10 # pretend this variable is importantdef dbg():print("x =", x)dbg() # x = 10x = 5000dbg() # x = 5000
Such print statements are great on a basic level, and we can comment or define them out of our main code when we need to compile and execute a more final version of our code.
Standard Error Stream
However, as great as print statements are, they are annoying to work with and efficiently separate from the actual parts of our code. This is important for example when we want an online judge (OJ) to read our output.
The standard error stream is a quick fix to this. Instead of printing in the standard output stream, we can print in a whole new stream called the standard error stream instead.
C++
#include <iostream>using namespace std;int x = 10;void dbg() { cerr << "x = " << x << endl; }int main() {dbg();x = 5000;dbg();}
Java
public class Main {static int x = 10;public static void main(String[] args) {dbg();x = 5000;dbg();}static void dbg() {System.err.print("x = ");System.err.println(x);}}
Python
import sysx = 10def dbg():print("x =", x, file=sys.stderr)dbg() # outputs 10x = 5000dbg() # now outputs 5000
Try running this program and you might be confused about the difference. The content in the error stream appears right alongside that in the standard output stream. But this is the beauty of it! And the best thing about it is, if we submit this program to an OJ, it won't notice the output in the error stream at all!
Warning!
Printing too much content to the error stream can cause TLE when submitting to an OJ.
C++
Debugging Macro
Resources | ||||
---|---|---|---|---|
GCC |
Here's a handy macro that prints the line number (__LINE__
) and the name of
the variable (#v
) in addition to the value of the variable (v
).
#include <iostream>using namespace std;#define dbg(v) \cout << "Line(" << __LINE__ << ") -> " << #v << " = " << (v) << endl;int x = 10; // pretend this variable is importantint main() {dbg(x); // Line(10) -> x = 10x = 5000;dbg(x); // Line(12) -> x = 5000}
Debugging STL Data Structures
As C++ does not contain built-in print functions for many of its built-in data structures, it would be good to have some prewritten code to print them. This template is rather easy to use. It includes support for basically all of the needed data structures in competitive programming. Here's how you would use it:
#include <iostream>#include <vector>#include "debugging.h"using namespace std;int main() {vector<int> arr{1, 2, 3, 4};cout << arr << endl; // just feed it into cout like any other variable}
Warning!
You are not allowed to use pre-written code for USACO contests, so this template should only be used for other online contests.
Java
Python
Assertions & Warnings
Resources | ||||
---|---|---|---|---|
LCPP | Includes | |||
GCC | Talks about |
Use assert(cond)
to check that a condition cond
holds. If it doesn't, then the program is terminated. For example, the following code snippet
#include <cassert>#include <iostream>using namespace std;int main() {assert(false);cout << "OK" << endl;}
produces:
prog: main.cpp:6: int main(): Assertion `false' failed. /tmp/program/run.sh: line 1: 408 Aborted ./prog Command exited with non-zero status 134
If you want to disable all asserts (say, before submitting) you can add
#define NDEBUG
to the start of your program.
#define NDEBUG#include <cassert>#include <iostream>using namespace std;int main() {assert(false);cout << "OK" << endl; // OK}
Stress Testing
If your code is getting WA, one option is to run your buggy solution against another solution that you're relatively confident is correct on randomly generated test cases until you find a difference. Usually, small test cases will suffice. See the video for details.
Resources | ||||
---|---|---|---|---|
Errichto | Using a script for stress testing. | |||
Errichto | Contains some parts from the above videos. |
Option 1: A Single Program
Write a single program including the two solutions and an input generator. If using C++, you should wrap each solution in a separate namespace to avoid compilation errors, like so:
#include <bits/stdc++.h>using namespace std;namespace Solution1 {int solve(int x) { return x < 10; }} // namespace Solution1namespace Solution2 {
Of course, you can only use this option if both solutions are written in the same language.
Option 2: Three Programs
Another option is to write three different programs, one for the correct program, one for the incorrect program, and one for generating inputs. Then you can use the script below to find an input on which the correct and incorrect programs differ. If you want to learn how to write these scripts yourself, see this module.
C++
Scripts
Java
Here is an script to test a Java program with input and output files. You will
need to put the .java
, this script, and the input and output files (1.in
,
1.out
, etc.) in the same directory:
Java testing script
Debuggers
C++
Resources | ||||
---|---|---|---|---|
LCPP | ||||
Microsoft | ||||
Jetbrains |
Java
Resources | ||||
---|---|---|---|---|
Microsoft | ||||
Jetbrains |
Python
Resources | ||||
---|---|---|---|---|
Microsoft | ||||
Jetbrains |
In essence, a debugger is a tool to "trace code" for you. Using a debugger varies from language to language and even from IDE to different IDE, so we will only go over the basics of a debugger.
Pros of using a debugger:
- No need to write print statements so you save time.
- You can step through the code in real time.
- C++: Print the line where a runtime error was triggered.
Cons of using a debugger:
- It is not much different from just printing the values out at various points in your program.
- Most competitive programmers do not use debuggers.
- You cannot see the overall "output" of your program at each stage. For example, if you wanted to see every single value of
i
in the program, you could not using a debugger.
Breakpoints
A debugger allows you to pause a code in its execution and see the values as a given point in the debugger. To do this, set a "breakpoint" at a certain line of code. When the code runs to that breakpoint, it will pause and you will be able to inspect all the different variables at that certain instance.
There are two more useful and common operations. Once you are at the breakpoint,
you may want to see what happens after the current line is executed. This would
be the "Step Over" button that will allow you to move to the next line. Say you
are at a line with the following code: dfs(0, -1)
, if you click "step over" the
debugger will ignore showing you what happens in this function and go to the
next line. If you click "step in," however, you will enter the function and be
able to step through that function.
Module Progress:
Join the USACO Forum!
Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!