License : Creative Commons Attribution 4.0 International (CC BY-NC-SA 4.0)
Copyright : Hervé Frezza-Buet, CentraleSupelec
Last modified : April 19, 2024 10:22
Link to the source : debug.md

Table of contents

Debugging

As already mentioned, bugs are errors that occur at execution time. Everything compiled fine (no syntax or typing errors), linkage is ok, so you program can run. Then, when you execute it, it fails.

There are different kinds of failures

These errors are the most difficult to solve. Beware of bugs occurring randomly, they are very hard to track. Here are some hints to debug, but try to avoid such situations by strong typing, clean and elegant conception, the use of smart pointers and STL collections, etc…

Unitary tests

Be sure to develop, beside your main development, unitary tests that display elementary behaviors and computation. Avoid plugging together large amounts of codes before being able to check the first result. Programming elementary tests, providing each class with the ability to be nicely displayed on the screen takes time when you write it, but this saves a lot of development time at the end.

Be sure to compile and test frequently during your development. Avoid (if you can) to spend hours of code writing without any execution trials.

Commenting and printing

You can add prints in your code, to detect which part is failing (the first expecting print that is not displayed tells you where something went wrong).

Another solution is to comment some parts of the code so that you get a non-crashing execution. Then, uncomment progressively until you get the line producing the crash.

Those methods are both easy and efficient.

Debugging step by step

When the previous method fails, you have to execute step by step your binary file, pause the execution, read the memory… This is what debuggers offer.

To do so, the binary has to keep trace of your variable names and types, i.e. to store some pieces of information as some kind of comments that are not needed for the execution. This is what the -g compiling option does. The compiler can process code optimization (e.g use the -O3 compiling flag), but they are incompatible with the -g flag. This is why you may see “Debug” versus “Release” compilings in some integrated compiling tools.

Graphical debuggers exist, try ddd my-executable-binary (sudo apt install ddd on ubuntu). It adds a GUI to the command line gnu debugger gdb.

Memory debuggers

Many bugs come from memory handling mistakes : freeing non allocated memory, accessing elements in collections that are above the size of the collection, freeing twice a pointer, … This is what the STL collections and the smart pointer aim at avoiding, but errors can still be made.

One solution for this is to change the memory management library and use a verbose one. It is much slower, but it keeps trace of what your program does, and reports memory management errors. Such kind of tools also offer profiling information (telling you which parts of your code take time).

For example, the software valgrind does this (see https://www.valgrind.org/).

Exercises

You can find tutorials on debugging in our former lectures (in French).

Hervé Frezza-Buet,