Using gdb for assembly language

Here is a quick primer on using gdb (a debugger) with the RISC-V assembly language assignments. Note that you must have the full set of RISC-V development tools available under Linux, whether that is through WSL, a virtual machine, or a native installation.

Starting the debugger

Open two terminal windows in your project directory. In the first, type:

make fordebug

This will compile and run your code, but tell to stop and wait for the debugger. This is the window where you will see your program’s output (and possibly type input).

In the second window, run:

make debug

This will launch the debugger and connect it to the first window. It is helpful to have a fairly large window because the debugger will display a lot of information. You should see the current contents of all of the registers and the source code immediately surrounding the next instruction that is about to run. The program will be paused at this point.

Getting to the function you want to debug

Typically you will be debugging a single function, and you probably want to jump directly to it. To do this, set a breakpoint on the function and then instruct the debugger to continue running the program at full speed until it finishes (or crashes) or reaches the breakpoint. If the function you are debugging is called “process” you would type this into the debugger window:

break process
continue

The program will run at full speed and then stop at the beginning of your function. It will refresh the display with the latest values of the registers and show you the source code of the first lines of your function. You can also look in the other window to see any output (or type any input if necessary).

Testing within a function

From here you need to decide what to test and what to look for. Here are the most common commands you will use:

The difference between these two mainly shows up when the next instruction is a call instruction. step will take you into the function being called so you can debug it. next will run it at full speed and stop when it returns.

In addition, you will likely want to add additional breakpoints to focus your debugging. Rather than stepping repeatedly, you can set a breakpoint on an interesting line and then use continue to fast-forward to that line. To add a breakpoint at line 72 in the current file you would use:

Note that gdb will show a red ! in front of lines with breakpoints. You can use db to refresh the display and see that the breakpoint has been added.

A useful technique is to put a breakpoint in the middle of a loop, maybe after an interesting value has been computed or loaded from memory. Then you can use continue repeatedly to run another iteration of the loop and stop at that line. This allows you to ask high-level questions about the loop, such as “am I loading the right values from memory”, although it requires you to first figure out what the right values are.

Getting test inputs

The code for some assignments requires input from the keyboard. When the automatic tests are run, that input is normally supplied automatically, but when you are debugging it you are not connected to the testing system and may need to supply the inputs yourself.

Start by running the tests normally:

make

Presumably a test fails and execution stops. Scroll back up until you see where that test was launched (note that there may be multiple runs of your program with different inputs, so you want the most recent one that failed). You will see a line similar to this:

python3 lib/inout-stepper.py inputs/piledriver2.input inputs/piledriver2.expected qemu-riscv64 ./a.out

Here’s the breakdown:

You want to replicate the input that caused your program to fail, so pick that part out, in this case it is the file inputs/piledriver2.input. Print out the contents of that file:

cat inputs/piledriver2.input

Then you can select the output from the terminal and copy it. Now when you run your code in the debugger, you can paste that input into the window with the program running (not the window with the debugger running) at the appropriate time.