Debugging

You are only human

Even if you are normally a precise person, you will find that the quantity of detail involved in programs means you will make lots of mistakes

Mistakes in programs are called bugs

They are completely inevitable

But on the other hand, you can get a lot of help from the computer in avoiding, finding, and fixing them

Productivity

Some people take ten times longer than others to write programs, because most of their time is spent in debugging

To reach a reasonable level of productivity it is absolutely essential to reduce the percentage of time you spend debugging to reasonable levels

Initially, aim for less than 50%, but ultimately aim for a tiny percentage

The secrets

The secrets of debugging are, in order of importance:

Approach

The number one secret of debugging is to go about things in the right way (see aside: agile development)

The agile rules are so important, they will be repeated here to emphasize their effects on debugging

Take small steps

Don't write more than a couple of lines of code before re-compiling and re-testing

That way, as soon as there is a bug, you will know exactly where it is, in the couple of lines you just wrote, and you will have a good knowledge of those lines in your head ready to find and fix the problem

Use small functions

Small functions allow you to make good progress

A big function takes too long to get working, but a small function can be tackled in a reasonable time

A small self-contained function is also easy to test, so you know it is working

The working functions that you have already written give you a reliable base for writing the next function

It is also a good idea to handle input/output last, in separate non-auto-tested functions

Keep your programs DRY

That means Don't Repeat Yourself

If you have lots of similar bits of code, even if you copy-and-paste them, you have lots of places for bugs to hide

If, instead, you can turn repeated similar snippets into a single function with an argument or two, there is now only one copy of the code, only one place for bugs to hide

Make programs readable

Think hard about names for variables and functions

A bad name confuses you into doing the wrong thing

Don't write code that 'works by magic' - make it so that the code explains itself

Don't try to be too clever - as Kernighan once said, debugging is harder than programming, so if your program is too clever, you won't be able to debug it

Imagine you are writing the program for a lesser programmer to take over from you later

Automate testing

The coursework assignments show you a simple DIY way to add automated tests to your programs (you don't need a complex framework)

Automated tests provide huge levels of confidence that things are working and that you are making good progress

Otherwise, a bug arises, and you have to spend a lot of time finding out which function the bug is in

Change things by refactoring

As you progress with a program, it is normal to find that what you've done already needs to be changed

For a radical change, comment out most of the functions and tests, and repair each function and its tests one by one according to the new point of view

That way, the radical change doesn't need superhuman effort, and results in a confident working result

Keep it sweet and simple

Don't over-engineer programs

The KISS rule is a tough one, and needs quite a lot of experience, because it is about design, which will never become completely routine in your whole career

Just use each program as a learning experience, which teaches you how you keep your program designs simpler in the future

Compiler messages

Assuming that you are going about things in the right way, following all the agile rules, the next step is to let the compiler help

Learn to interpret compiler error messages and warning messages

And never ignore them, always track down why you are getting them, and get rid of them

And switch on all the compiler warnings you can find (-Wall as a minimum)

Reading compiler messages

There are two general rules about reading error and warning messages from the compiler

First: if the compiler spews out lots of error messages, don't panic, just read and fix the first and ignore the rest (they are probably just knock-on effects anyway)

Second: if the compiler says there is a problem in line 15, check that the problem isn't at the end of line 14, e.g. a missing semicolon (the problem can't after the place the message says, but it can be before)

Print statements

If you have a stubborn bug, you may need to find it by tracing what your program is doing

The simplest way to do that is to add printf statements

You can add printf("A\n"), printf("B\n"), etc. in various places to find out how far the program is getting before trouble strikes

Or you can add printf("n=%d\n",n) to display the value of a variable at a critical point in the program

Interval halving

Rather than just randomly sticking in print statements, you should have a strategy

Each program run should narrow down the problem, preferably to half what it was before

For example, a print statement half way through the execution of the program might tell you whether the bug happens early or late

Or a printout of some variables might tell you which of them becomes incorrect by a certain point

GDB

There is a debugging tool called gdb which can help

Probably, you should use it rarely, as a last resort, because it can eat up a lot of your time

It is also somewhat a matter of taste whether you like using it

Basic GDB

You must compile with the -g option

Suppose you have a program with a bug in it that you run with ./prog file

gdb ./prog
b main         put a breakpoint at the start
r file         run the program with its args
s or n         one step or next line
p x            print out x
q              quit

More GDB

You need to learn about GDB features one by one

Generally, the most useful commands are the ones with one-letter or two-letter abbreviations:

b   breakpoint
d   delete breakpoint
c   continue
h   help
l   list
s   step
n   next (step over rather than step into)
p   print
q   quit
r   run
bt  backtrace