GDB Basics

Now we’re going to use a sample program, map, for some GDB practice. The map program is designed to print out its own executing structure. Before you start, be sure to take a look at map.c and recurse.c which form the program. Once you feel familiar with the program, you can compile it by running make map.

For all of the homework assignments and project the starter repositories have been configured such that you can directly use VSCode to run gdb interactively. For the assignments, this is done by providing a configuration file .vscode/launch.json. For this assignment, this file looks like:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "gdb debug executable",
      "type": "cppdbg",
      "request": "launch",
      "program": "/pintos/src/map",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": false,
      "MIMode": "gdb",
      "miDebuggerPath": "/usr/bin/gdb",
      "setupCommands": [
        {
          "description": "Enable pretty-printing for gdb",
          "text": "-enable-pretty-printing",
          "ignoreFailures": true
        }
      ]
    }
  ]
}

For more information about this file format, please see the corresponding documentation.

This configuration file will show a command in the debug pane of VSCode named gdb debug executable you can use to launch a debugging session. Please have a closer look at this file to see how this is set up so you can create similar configurations yourself in the future.

While you go through the command sequence outlined below, be sure to also record and submit your answers to the six questions in the file results/answers.md. We highly recommend this site for an easy-to-read GDB refresher. Note, that from inside VSCode you have all of the supported GDB commands available to you by prefixing them with -exec in the debugger command prompt at the very bottom of the ‘Debug Console’ window.

  • Set a breakpoint at the beginning of the program’s execution (the '{' at the beginning of main) by clicking on the left margin of that line. A red dot will appear to mark the break point you created.
  • Now run GDB on the map executable by running the gdb debug executable task from the VSCode debug tab.
  • The program will run and stop at the breakpoint.

Q1: What memory address does argv store? You will see the values of all local variables shown in the ‘Variables’ pane on the left.

Q2: Describe what’s located at that memory address. (What does argv point to?) This information can be discovered by clicking on the arrow next to the argv variable.

  • Step until you reach the first call to recur. Use the step-over function (the keyboard shortcut for this is F10).

Q3: What is the memory address of the recur function (hover with your mouse over the recur function name)?

  • Step-into the first call to recur (the keyboard shortcut for this is F11).
  • Step-over until you reach the if statement.
  • Switch into assembly view (right click into the editor window, select ‘Open Disassembly View’).
  • Step-over instructions until you reach the call instruction.

Q4: What values are in all the CPU registers? This information can be collected by opening the ‘Registers’ node in the ‘Variables’ pane on the left.

  • Step-into the call instruction.
  • Switch back to C code mode (simply close the Disassembly window).
  • Now print out the current call stack (type -exec backtrace in the debugger command prompt). Hint: what does the backtrace command do?
  • Now set a breakpoint on the first statement inside the recur function which is only triggered when the argument is 0. This can be done by first creating a breakpoint and then right clicking on it. Select ‘Edit Breakpoint’. Now you can enter a C-style expression referring to variables as needed. The breakpoint will be ‘hit’ only if the expression evaluates to true.
  • Continue until the breakpoint is hit (the keyboard shortcut for this is F5).
  • Print the call stack now.

Q5: Now go up the call stack until you reach main. What is the return address to the main function (-exec up will move the debugger’s view of the stack ‘up’ by one function invocation level; the debugger will print the address and the function name of the new level)?

The debugger command up does not execute any code, i.e. the next instruction to be executed is still the one inside the call to recur(0). This command simply shifts the debugger’s ‘attention’ one stack level up.

  • Go down the call stack back to where you started off from (the command is -exec down 3)
  • Now step out until you reach main (the keyboard shortcut for stepping out one level is Shift+F11).
  • Step-over until you reach the statement return 0;. Switch back into the assembly view.

Q6: Which assembly instructions correspond to the return 0 in C?

  • Now switch back to the source layout.
  • Run the program to completion.

Next up: From Source Code to Executable.