Lab 8: gdb Debugging

Due by the end of class

gdb Background

In this part of the lab you will learn about the gdb debugger, a powerful interactive debugger for many compiled languages. We will be focusing on its uses in C.

Before we discuss gdb further, it's important to note that it only works if certain debugging information has been added to the executable. This information is typically not included in the final production code because it slows down execution and gives outsiders a lot of information about how the program works. To compile a program with gdb information included, use the -ggdb flag when invoking gcc. Below is an example.

gcc -ggdb program.c -o executable

gdb can be used in one of two major fashions: at run time (gdb is started before there is a problem) or post-mortem (gdb is run to determine why a program has died). Run-time debugging is desirable whenever possible, but post-mortem debugging is useful when you have a process that fails in some unexpected way and leaves a core dump.

These two major modes of operation are invoked in different ways. To debug a program at run time, you can either start the program from within gdb or attach gdb to a program that is already running. To start a program within gdb, you would type the following on the command line. Italicized items are placeholders for information specific to a given use (like the actual name of the program executable). User input is given in green.

gdb program
(gdb) run

Note that you might want to set some breakpoints or watchpoints (discussed later) before running the program. The second common form of run time debugging is attaching gdb to a process that is already running. This requires you to know the PID of the running process you are interested in.

gdb program pid

After attaching to the program, it can be debugged as if you had started gdb and run the program from there.

To invoke gdb for post-mortem debugging, you must have both the executable you wish to debug and the core file from where it failed. (You can force a core dump with Ctrl+\ (control backslash), but it is often more useful to attach gdb to the running process instead.)

gdb program corefile

Core files can also be loaded at run time with the core-file command, and executables can be loaded with the exec-file command. On many Linux and Unix systems, a core file is generated whenever you get a segmentation fault. On the version of Ubuntu in the lab, it doesn't generate a core file for security reasons (there is a limit of 0 bytes on the size of the file that can be generated). You can temporarily allow the system to generate core files with the following command.

ulimit -c unlimited

For this lab, you will not need to generate a core. Instead, open the program in gdb using one of the other methods. Once you have a program open in gdb, here is a brief list of commands you may find useful for debugging.

Command syntax Description
break
break linenumber
break filename:linenumber
break function
These commands set breakpoints, places where the debugger should suspend operation of the debugger and ask for further input. Play with their arguments a little to see how flexible break is.
bt
where
The bt and where commands are functionally identical. They cause gdb to print a backtrace, a listing of the function call stack of the current program. A stack trace is very useful for determining where and why a program died. You should play with this command on programs that have crashed and programs that are running normally to get a feel for how it works.
list line
list function
Usually when you use gdb, you will have the source code available. If you don't want to look directly at it, the list command displays the code near a particular line in the source file or near the beginning of a function.
help
help topic
help command
gdb includes a comprehensive help system. The bare command help will generate a list of high-level topics which can be researched in more depth by giving arguments to the help command. If you think gdb should be able to do some particular thing, it probably can.
cont This command continues execution where it was last stopped, either by break, watch, Ctrl+C, a signal, or some other action.
info category This command prints information about some part of your program or gdb configuration. You may find the categories locals, variables, functions, and display useful.
next Execute the current line of code and proceed to the next, treating subroutine calls as one line.
print expression print is one of the most powerful commands in gdb. It is used to print the value of an arbitrary expression, such as a simple variable, a calculation, or a function call. The power of print stems from the fact that expression can be a complex C or C++ expression and it will be executed just as if your program had done it. For instance, if the variable x is equal to 5 and you know that it should be 6, the command print x = 6 will print 6, but will also change the value of the variable x. This can be used to both determine why a section of code is failing and to prevent it from doing so. It can also test corner cases and bounds in your code that may not be easily reached through test cases.
step Like next, except step enters subroutines (functions), treating the first line of a called subroutine as the next line of code.
watch expression watch is like break, except that it will stop the program's execution whenever the value of the given expression changes, rather than at a specific point in program flow. Watchpoints can make your program run significantly slower if they have to be constantly checked, so set them carefully.

Remember that the source code line printed to the screen when the program stops is the next line to be executed and has not yet run.

Instructions

Here you will find a program called trouble.exe. The source code for the main program (but not for the magic() function) is code.c. You will need to have the source code in the same directory as trouble.exe to use all the features of the debugger. This program contains a few bugs that either prevent the program from giving output or make it crash. Your task is to avoid the crashes and retrieve some information. If you use gdb correctly, you should get the program to prompt you for your username and then print a line like the following. (Please enter your normal Windows username without the @otterbein.edu at the end.)

Secret token: sttKpZpCS97rQ

This line is different for each student. The line above corresponds to my username (wittman1), which you can use for testing purposes. Copy this output line (and only this line!) and paste it into a file called token.txt which is the only thing you'll turn in for this lab. Use the commands listed above as well as other gdb commands you may learn by looking through the help to make the program execute correctly.

Your goal is to find out what the bugs are and temporarily fix or bypass them inside gdb so that execution can reach the point where the line is printed. There are essentially three bugs in the executable. You should not use gcc to compile any code. Instead, all the work in this lab should be done inside gdb.

Note that you will need to make trouble.exe executable before you can run it from gdb. To do so, type:

chmod +x trouble.exe

Additional gdb Resources

Here are some additional resources on gdb which you may find helpful.

Turn In

Upload only the file token.txt to Brightspace. Do not include any object files or executables. No source code, executables, or makefile are required (or should have been created) for this lab.

All work must be done individually. Never look at someone else's code. Please refer to the course policies if you have any questions about academic integrity. If you have trouble with the assignment, I am always available for assistance.