Debugging Tips and Tools

Overview
This page is designed to provide some tips and tools for debugging your lab assignments in CSE 351. As this course is more about concepts than programming, it is always recommended to first verify your understanding of the material being assessed first. However, since we are examining many concepts beneath the software layer (and therefore abstracted away from the programmer), it is very important to make use the debugging tools to examine what's actually going on in your program.
General Tips and Tricks

Walk Through your Code

Walking through your program's code is a good way to compare your expectations against the reality of the code execution. Finding where the two diverge from each other can provide the scope in which a bug in your code or a conceptual misunderstanding may exist. Here are some things to keep in mind:

Take your time! Making sure you understand why your code leads to the erroneous behavior will often illuminate the bug or misunderstanding and is a much more efficient debugging strategy than just trying a bunch of different things randomly.
Make use of the debugging tools at your disposal, as they are often the only way to truly see the reality of the code execution.
As 351 deals heavily with your computer's memory, you will often need to examine pieces of memory (e.g., registers, the stack) using GDB and not just variable values.

Test Frequently

Debugging is especially difficult when you do not know where the bug is in your code! Most of the labs in 351 are comprised of multiple functions or parts that should be tackled one-by-one:

You should test (usually just re-compiling and running the provided test suite) after attempting/completing each one of these parts so you can isolate/restrict the source of the bugs.
The test suites will often test all of the parts in one go. You should ignore scores and error messages about the parts that you haven't attempted yet.

Collaborate

We encourage collaboration in this course! Oftentimes, bugs can be easily overlooked individually, so collaboration allows more perspectives, ideas, and just another pair of eyes when writing code or trying to understand the problem! Here are some ideas to consider:

Discuss with your partner:Review the collaboration page for policies and tips to work with one another.
Go to office hours: Office hours are a wonderful place to work with other students that may be working on a similar problem, and it is facilitated by the staff, who have experience with the material from before.
Post on the discussion board: The discussion board is a place for student discussion of course material in an asynchronous (and possibly anonymized) manner.
Please review the syllabus' academic conduct section for what differentiates collaboration and cheating.
Debugging Tools

printf debugging

Debugging with printf can be a good starting point for better understanding the state and execution of your program. Here are some places where it might be useful:

Printing out the current state of the program (e.g., local variables or parameters) at a particular point of interest or to assess the changes over time. Note that this is entirely possible with GDB as well, though.
Printing a message to mark the execution (i.e., was this code reached?) of different parts of the program to give you a better idea of how your program is operating.
Feel free to do what works well for you, but we do strongly encourage you to use a dedicated debugger like gdb instead of just relying on printf. Parts of 351 can only be done through gdb.

GDB

GDB is an immensely useful tool to help you debug your C and assembly programs.

It lets you insert breakpoints into your programs so that you can stop execution and examine the contents of memory and registers.
It supports single-stepping your program one line of source code or one line of assembly code at a time.
It leads to much more productive debugging than just using print statements (e.g., printf).

The time you spend getting familiar with GDB will be an excellent investment for this and future courses.

Resources
GDB cheat sheet: A handy dandy guide to the most commonly used GDB commands. Useful to have open while using GDB (and going through the other resources here).
Intro to GDB: This video shows you how to get started with GDB. This will be especially useful for Lab 2.
GDB Tutorial Assignment: An optional walkthrough assignment to give you some hands-on experience with GDB.
Examining Memory: Learn how to format your memory output in GDB. This will be especially useful for Lab 3.
Extensive tutorial: Looking for more details? Find them here thanks to Norman Matloff at UC Davis.
Examples

Here are some code samples for playing with GDB along with some example commands you can try:

GDB also has a text user interface which is optional, but you may find useful.

Examining Binary Files

In 351, we will deal with binary files, where the stored data uses the full range of possible character values. This is in contrast to text files, where the stored data are mostly restricted to the values that correspond to printable (e.g., ASCII or Unicode) characters. Different types of binary files serve different purposes, but the one thing they have in common is that they will look like garbage when opened in a text editor because non-printable characters will be represented in ways you wouldn't expect.

objdump

The primary types of binary files that we will examine are object files and executables. Both of these types of files are primarily used to hold machine code, so we can use the utility objdump to interpret their contents in a textual format.

Knowing the ins and outs of objdump isn't a high priority in 351, so, when applicable, you will generally be given the exact commands to run in the assignments.

xxd

xxd will print out the bytes of a file in hex format. You don't need to know much about this utility and it is primarily useful for Lab 3.

Here is an example usage. The first command creates a file called test.bin whose contents are the ASCII characters hello world followed by the bytes CA, FE, F0, and 0D(the \x you see means that the byte is specified in hex). The second command invokes xxd on the binary file that we just created.

[attu]$ echo -ne "hello world\xca\xfe\xf0\x0d" > test.bin
[attu]$ xxd test.bin
00000000: 6865 6c6c 6f20 776f 726c 64ca fef0 0d    hello world....
Generally, larger files will produce more lines, but still in this format, so you can follow the same interpretation tips described here.

There are three blocks/columns of information in the single line of output shown above:

The left-most block (the 00000000:) represents the byte address/index of the left-most byte of the line (in hex). This can be helpful for finding your place in a longer file.
The middle block (68 through 0d) shows the values (in hex) of the bytes themselves. Recall that one byte = two hex digits. The bytes have increasing address/index from left-to-right.
The last block is the ASCII decoding of the bytes (as a basic text editor would interpret the bytes). For instance, the first byte of the file, 0x68, is the ASCII character 'h'. The last four bytes show up as ., which is how xxd chooses to show non-printable ASCII characters. This can be a little bit confusing as there is a valid '.' character (0x2e) and these non-printable characters might display differently in a text editor.
GDB Text User Interface

Text User Interface (TUI) mode of GDB can nicely show your code and the value of registers as you debug! Its use is entirely optional for 351, but we wanted you to be aware of it as it may help some of you.

More info on TUI commands

Opening TUI Mode

Most TUI commands will automatically start TUI mode, but you can also explicitly open GDB in TUI mode using:

[attu]$ gdb -tui <filename>

Layout Views

Of particular interest, you can bring up the disassembly and registers view using:

(gdb) layout asm (gdb) layout regs

Note that you want to do them in that order so that the registers window is on top. Then as you execute instructions (stepi or si for assembly instructions), the assembly view will highlight the next instruction (not executed yet) and the registers view will highlight any changed registers.

Formatting Issues

Unfortunately, there are some annoying formatting issues that sometimes pop up while using TUI mode. If things start to look weird, run the following command (or press Ctrl+L) to set things straight:

(gdb) refresh