Today, we are going to investigate some of the more sophisticated features of the gnu debugger, gdb. Hopefully, this will be a fun exercise, but introduce some of the very sophisticated features of gdb that you can make use of in this class (for instance, while working on your projects).
I will include links to specific sections of the on-line gdb manual (the top level is linked here).
The code we will be working on in this lab is a variant of an old computer game, first invented by a student from Dartmouth in the 1970's called "Hunt the Wumpus". See Hunt the Wumpus from Wikipedia for some background on the original game.
Download the wumpus binary file into a lab03 directory. Type ./wumpus to run the game. Note that the wumpus binary was compiled on an LDAP machine, and probably won't run on another machine. If you run wumpus on a different machine, you may get a message like ./wumpus: cannot execute binary file: Exec format error. If you get that kind of message, move to an LDAP machine.
Depending on how you download, you will probably end up with an executable file that no longer has "execute" permissions. If that this the case, when you run ./wumpus, you will get a message like ./wumpus: Permission denied. If you get that message, you can fix it by adding back the execute permission, runnig the command: chmod +x wumpus.
If the code runs successfully, it will start out by printing a message like:
You are in a dark room with a single candle Where next? (l=left, r=right, f=forward, b=back):Try it out by typing a letter for a direction and hitting enter. See how far you can get. Did you die? Unless you are VERY lucky, you probably didn't make it all the way to the end of the game without getting killed. We are going to try to introduce some gdb techniques that help you get through the wumpus game.
In this version of the game, you are basically on a grid of rows and columns, where each location on the grid represents a room. Some of the rooms are deadly, and if you enter those rooms you will "die" (the game will end.) There is also a wumpus that randomly travels from room to room as well. If you end up in the same room as the wumpus, he will kill you. If you walk off the edge of the grid, you will also "die". There is one room that represents the end goal - you must get to that room to win the game.
You have probably noticed that when you are debugging gdb, that you are re-starting gdb over and over again, and that there is a series of commands that you type each time you restart gdb. One trick to make this process easier is to put all of the commands you want to execute each time you start gdb in a file. Then, you use the -x <filename> flag on the command line of gdb to run your commands when it starts gdb. See gdb manual file options under -x for specifics on the flag. You can put comments in this file by starting a line with a pound sign (#). See gdb manual command files for a complete description of what you can put in your gdb command file.
Create a file called dbg.txt. Open up that file in an editor, and put some start up gdb commands in it, like break main and run. Then try running gdb -x dbg.txt wumpus
If you get a message when you run gdb that says wumpus.c: No such file or directory, you are running on the original version of wumpus where I did not correctly provide access to the source code. There is a new version of wumpus called wumpus2 that fixes this problem. Download wumpus2, run chmod +x wumpus2, and from now on use wumpus2 wherever I refer to wumpus (sorry about that.)
While the above command is fairly simple, you still have to remember the name of the gdb command, the name of your gdb command file, the name of the flag (-x) required to invoke your gdb command file, and the name of the executable. I often find it easier to put all of this in a Makefile with a gdb target. Create a file called Makefile in your home directory (note that there is no file type associated with this file) with a gdb target that runs gdb -x as above. Test your Makefile by running make gdb.
GDB keeps track of a list of all the functions in wumpus, which can be printed with the info functions gdb sub-command. In this list of functions, do you see anything that looks like it will be called before the wumpus player is killed? Or is the list way too big to figure out what's there?
You can specify an extra argument to the info functions command that specifies a string to check for in all function names. Try the gdb command info function kill to see the list of function names that contain the letters "kill" in them. Does that make things easier?
Usually, when we set a breakpoint in gdb, we specify the line number in a C file to stop at. However, it is also possible to specify a function name as a breakpoint. This causes gdb to break before the first instruction in that function is executed. Since you don't have the source code for wumpus (and therefore no line-number information), this can be very useful.
Edit the dbg.txt file by adding a break <kill function name> command. Save your changes, and run make gdb. Then play the game until you get to the breakpoint.
Use the where command to try to figure out what function is calling the kill function. You can use the up command to get into the context of the caller, and then the list command to see the code in the caller that caused the call to the function. You can probably figure out what the problem is from here.
It would be kind of nice to make sure that whenever you get to the kill function, you run the "where" command automatically to see who is calling the kill function. Then, move up into the context of your caller and look at the code. You can do this by attaching commands to breakpoints. See gdb manual breakpoint command lists for a description of how to do this.
Edit your dbg.txt file again, and after the break <kill function> line, enter the lines
commands where up list end
... save and then run make gdb again. Now, whenever you are about to get killed, you can see who is calling you.
Can you figure out a command to add to dbg.txt file to make the code start from the beginning rather than continuing with the kill function? Try adding that to your dbg.txt file as well.
Can you find a gdb command that will alter the execution of the code by return immediately from the kill player function without finishing it? Try the help gdb sub-command, or look in the on-line manual at GDB Manual.
There is a wumpus that wanders randomly around the game. If you end up in the same room as the wumpus, he will kill you, but the game gives a warning when the wumpus gets close. Can you find the code in the game that gives the warning, and set a conditional breakpoint (using the "if" keyword in the break command) to debug the game when the wumpus gets close? You might want to move the wumpus to a different room to guarantee he won't get you. Can you figure out how to change the value of a variable in gdb?
Now, run play the game under gdb, and see if you can get to the end of the game. If you reach the end still alive, you are almost done with the lab.
Download Lab03_report.txt, and edit the file. There are five quick questions to answer in that file. Add your answers to the file, and submit the result on MyCourse under the Lab 03 Submission area.