Lab for GNU Debugger (GDB)

  1. Download this file and save it in your home directory on taz. Change its name to just: .emacs
  2. Download this file and save it in your home directory on taz. (Don't change the file name.)
  3. Run PuTTY to log in to taz.
  4. Type the command: emacs split.cpp The emacs editor should run and open up your file.
  5. Emacs is a very powerful editor with many commands. Because we are running it from a terminal, we cannot use the mouse to interact with emacs; therefore we will give commands using keystrokes. Several of these commands take several keystrokes, and may even require you to type something in. Pressing Ctrl-g tells emacs to cancel whatever command you are currently entering and go back to the default mode.

    Note: If emacs isn't working like you expect it to, it's possible that you are stuck in the middle of a command—you entered the first part of the command and now emacs is waiting for you to finish the command. Try pressing Ctrl-g to see if it helps.

  6. Notice the blinking cursor. Move the cursor around using the arrow keys. Notice that if you move the cursor to the bottom of the screen you can scroll down to see other parts of the file.
  7. Notice the scroll bar on the right side of the window. What happens when you scroll up?

  8. Move the scroll bar back down so that emacs fills the screen again.

  9. Press Alt-x. This keystroke tells emacs that we want to enter a command to run. Type the word shell. Notice that what you type appears in the line at the very bottom of the screen. This is the command line, used to enter commands and see status messages.
  10. Press Enter. If you entered the command correctly it will run a shell (like the command prompt you see when you first logged in) inside emacs. We can use this shell to enter commands while still running emacs. Note that you can enter commands anywhere in the buffer; however, you will avoid confusion if you only enter commands at the prompt at the end of the buffer.
  11. Although we can no longer see our file, it's still there. Emacs keeps track of several different "buffers", each with its own name and containing its own text. The name of this buffer is *shell* (including the asterisks). You can see the name in the status bar (the second to last line on the screen).

    Any buffer (including this one) can be edited like a text file—the only difference is that when we press Enter in this buffer emacs takes what we typed and passes it to the shell. Similarly, we can scroll around this buffer using the cursor. (Remember that this shell is still inside of emacs, so you cannot use the scroll bar.)

  12. We can switch back to our file using an emacs command. This command is used commonly, and so it has a keystroke assigned to it: press Ctrl-x, followed by b. Notice that the command line is asking us which buffer we want to switch to. It also tells us the the default buffer is the one for our file, split.cpp. We could type in the name of a buffer, but the default is the one we want so we can just press enter. Now we're back at our file.
  13. Use the Ctrl-x, b command to switch back to the *shell* buffer.

  14. Type the command: g++ split.cpp -o split -g
  15. Note: it's very easy to copy and paste using PuTTY. To copy something on the PuTTY window to the clipboard, simply highlight it. It will automatically be copied to the clipboard. To paste into PuTTY, click the right mouse button anywhere on the screen. The text will be pasted at the current cursor position (as if you just typed those characters).

    The -g option is important: it tells g++ to compile in debug mode. If we leave it out, we won't be able to debug our program.

    This program takes a string and a delimeter. It breaks the string into pieces every time it finds the delimeter and then prints out the pieces. For example, if you enter the string "" and the delimeter "." the program should print out "taz", "harding", and "edu".

  16. Enter the command: ./split Use the input string: "" and the delimeter "." (Don't type the quotes.) The second time it asks you for an input string, just press Enter. Write the output in the box below:

  17. Hmmm… that didn't seem to work. Let's debug!

  18. Now we are ready to run the debugger. Press Alt-x again so that we can give another command to emacs. Enter the command: gdb
  19. Look at the command line (at the bottom of the screen). What does this line say?

    Emacs is telling us how it will run gdb. By default, emacs will simply run the program gdb. (gdb is actually a program we could run outside of emacs if we wanted to.) We want to run the gdb program, but we need to give it a command line argument: the name of the program to debug. Emacs will let us enter any command line arguments here on the command line.

  20. Type the name of our program (split) and press Enter.
  21. Emacs creates yet another buffer to hold the output of the debugger. This one is named *gud-split*. The buffer contains some introductory text telling us about gdb, and a (gdb) prompt where we can type commands into the debugger.

  22. Although we are in the debugger, our program isn't actually running yet. However, before we start our program running it is important to make sure we have a breakpoint set—otherwise the debugger will run our program all the way through.
  23. Enter the command: b main What is the debugger's response?

    The b (or break) tells the debugger we want to set a breakpoint, and the main tells the debugger where to set it—the main subroutine. We could use this command to set a breakpoint on any subroutine.

  24. Now we can run our program. Enter the command: r (If we wanted to give command line arguments to our program we could enter them here like so: r one two three
  25. Emacs splits the screen into two windows: the top one contains the buffer for the debugger, the bottom one contains the buffer for our source code. (You may want to enlarge your PuTTY window so that you can see more.) This is why we are using emacs; we could run gdb just from a shell, but then we wouldn't be able to see the code at the same time.

    Notice that emacs puts an "arrow" (consisting of an equal sign and a greater than sign) at the beginning of the current line. If you look at the status bar for the split.cpp buffer, you can see the characters "L24". This means the current line is line 24.

  26. Enter the command: n (or next) This is the "step-over" command; it tells the debugger to execute the current line, including any subroutine calls in that line. The arrow in split.cpp moves down to the next line.
  27. Enter the command: n The debugger executes the output command, but we don't see any output. This is because cout is buffered and doesn't flush until it prints an end-of-line character. Don't worry—it did execute.
  28. Enter the command: n The command to read input causes cout to flush and we see our output. Not only that, but the debugger has paused and is waiting for us to type in our input. (Notice there is no (gdb) prompt.) Type in: and press Enter.
  29. The (gdb) prompt returned, so it must have worked; however, it would be nice to verify that. We can see the value of a variable by typing p (or print) and the variable name. Enter the command: p input
  30. Woah—what a mess! The problem is that a string is actually a rather complex object with several fields; the debugger is trying to show us all of them. If you look closely, you can in fact see the string "" amidst all the other output.

    The problem is that the debugger doesn't know what a string is—it doesn't know that all we really care about are the characters. However, it does know what a C-style string is. One trick we can use, then, is to call the string's c_str() function to get a C-style string.

  31. Enter the command: p input.c_str() Write the output of that command below.

  32. When at a command prompt in either the shell buffer or the debugger buffer, you can use Alt-p to recall commands you typed previously. In this way you can avoid typing the same command repeatedly.

  33. We want to step over the next line. An alternate way to do this is to press Ctrl-c, Ctrl-n. This causes emacs to interact with gdb directly and does not create any output in the buffer.
  34. Continue stepping over lines; stop when you get to line 33: print_split( input, delim );. When asked for a delimeter, enter ".". (Verify that the variable was set correctly.)
  35. Line 33 calls our subroutine. We would like to "step-into" this line so that we can step through the subroutine as it executes. To do this enter the command: s (or step) (Alternatively you can press Ctrl-c, s.)
  36. Now we've jumped to the first line of the print_split subroutine. What line number are we on?

  37. Step over the next line. This sets two integer variables
  38. Enter the command: p start Notice that int variables print out much more nicely than strings.
  39. The p command doesn't only work for variables; we can give it any expression to print out. In particular, we can give it an assignment statement, actually changing the value of a variable.

  40. We know the first pass of our loop works correctly (it prints out "taz") so let's jump ahead. Enter the command: p start=4 This changes the start variable to 4. (You can verify that by running p start again.) Now we're starting from the letter "h" in "harding".
  41. Step over the next two commands. We have executed the find function. Let's check to make sure it worked. Enter: p next
  42. Looks good, let's move ahead. It would be nice to jump down to the line that reads: cout << piece << endl; We could do that if we knew the line number.

  43. We could use the Ctrl-x, b command to switch to the buffer with our file, but that buffer is already open in the bottom window. Enter the command: Ctrl-x, o to move the cursor to the lower window.
  44. Now we can move the cursor down to the line we want to jump to. The status bar tells us the line number for that line.

  45. Use the Ctrl-x, o command to move the cursor back up to the window containing the debugger. We can use the b command and give it a line number instead of a subroutine name. Enter the command b followed by the line number.
  46. Now enter the command c (or continue) to cause the program to run to the next break point.
  47. Print out the peice variable. Write the value below.

  48. Oops, that's not right. Let's check the values of start and next.
  49. Aha—next is the position of the next delimeter. But the second argument to substr should be how many characters to include, not where to stop.

  50. Let's verify the problem; enter the command: p input.substr( start, next - start ) We get a messy string, but we can see that it contains "harding" like we want it to.
  51. You might want to view this substring as a C-style string to make it easier to read. Unfortunately, the c_str() function does not work on temporary variables.

    Every time you print a value using the p command, it is assigned to a debugger variable which looks like a dollar sign and a number. We can use that variable to reference the value again if we wish.

  52. Look at the line that printed out the return value of the substr command. Use the p command to print the call the .c_str() function on the convenience variable (for example: p $8.c_str()
  53. Wow, it didn't like that. Write just the first line of its output after that command.

  54. Because there was an error in the middle of evaluating an expression, we are now stuck in the middle of that expression. We need to get back to the program.Enter the command: return to return to our program. (Enter y when the debugger asks you to confirm.)
  55. We know what's wrong. We're ready to quit debugging. Enter the q (or quit) command to quit. (Enter y when it asks you to confirm.)
  56. Use the Ctrl-x, o command to switch from the top window to the bottom window (which contains the buffer for our file). Edit line 15 so that it reads: piece= input.substr( start, next - start );
  57. Save the file by pressing: Ctrl-x, Ctrl-s

    If you would like to use the emacs editor to make more than trivial edits to a file you should check out my emacs reference page for more tips.

  58. Now we need to recompile the program. Switch to the shell buffer either by typing Alt-x and running the shell command, or by typing Ctrl-x, b and switching to the *shell* buffer.
  59. Recompile the program. (You should be able to use command line recall to get the compile command.) Test it by running it again as we did at the beginning of the lab. Write the new output below.

  60. If it still wasn't working, we could start the debugger all over again. However, for now we'll just quit emacs by entering the command: Ctrl-x, Ctrl-c