Skip to content

Instantly share code, notes, and snippets.

@petewarden
Last active January 22, 2024 00:34
Show Gist options
  • Save petewarden/6332fe0f352d078dc11dd6c75cbca024 to your computer and use it in GitHub Desktop.
Save petewarden/6332fe0f352d078dc11dd6c75cbca024 to your computer and use it in GitHub Desktop.

GDB Tips and Tricks for Debugging Pintos

As I get more familiar with the Pintos environment, I want to save notes on techniques I've found improved my quality of life when debugging through the assignments. Please don't add any comments that discuss project solutions, but feel free to contribute your own favorite ways of improving the debugging process, since I'm hoping we can create a trail of breadcrumbs for future students.

Save GDB Command History Between Sessions

One of the first things I like to do is ensure I can use the up arrow to access commands from a previous gdb session, since debugging tends to involve a lot of repetition!

echo 'set history save on' >> ~/.gdbinit

Automatically run debugpintos when Starting GDB

Most of the time the first thing you'll want to do when starting a gdb session is enter the debugpintos command to connect to your pintos session. To save on typing you can add this to your command line instead, using the -ex flag:

pintos-gdb -ex "debugpintos" kernel.o

If you wanted to get really fancy you could create a new alias to do this, but I find having it in my shell history is easy enough.

Displaying Contents of Lists

The list data structure is heavily used by the kernel so it's handy to be able to view all values with the dumplist macro.

dumplist &ready_list thread elem
dumplist &all_list thread allelem 

Stack Traces of All Threads

The first thing that threw me was a typo in E.5.2 in the debugging tools chapter. it suggests the following command to list all threads stack traces:

btthreadlist all_list allelem

This fails with Attempt to take address of value not located in memory., but that's just because an & got missed out. Luckily the correct form is listed elsewhere in the document:

btthreadlist &all_list allelem

We're now passing in the address of all_list instead of the value, but I was puzzled to see the error Attempt to assign to an unmodifiable value. when running it.

After looking at the macro code, I guessed that something had changed in the register protections in gdb since that code was written or I was in a weird state because of the page fault. I switched to using my local copy of the macros (by running gdb -x ~/pintos/src/misc/gdb-macros kernel.o instead of just pintos-gdb kernel.o and then experimented with the implementation of btthread. I saw in btpagefault that $eip seemed the most critical so I made the following changes:

peteward@myth58:~/pintos$ git diff src/misc/gdb-macros
diff --git a/src/misc/gdb-macros b/src/misc/gdb-macros
index 3babb52..0c388a7 100644
--- a/src/misc/gdb-macros
+++ b/src/misc/gdb-macros
@@ -45,18 +45,13 @@ define btthread
        bt
    else
        set $saveEIP = $eip 
-       set $saveESP = $esp 
-       set $saveEBP = $ebp 
 
-       set $esp = ((struct thread *)$arg0)->stack
-       set $ebp = ((void**)$esp)[2]
-       set $eip = ((void**)$esp)[4]
+       set $myESP = ((struct thread *)$arg0)->stack
+       set $eip = ((void**)$myESP)[4]
 
        bt
 
        set $eip = $saveEIP
-       set $esp = $saveESP
-       set $ebp = $saveEBP
    end
 end
 document btthread

This executed without any errors, but didn't always show useful information for each thread, so I'm not sure it's the complete solution.

Inspect Functions in the Stack after a Page Fault

The btpagefault that automatically executes after a page fault displays the call stack for the crashing process, but it doesn't let you jump up and down the stack to inspect locals or view the source.

I found setting $eip would let me work around this, and allow me to look at variables and code at all levels.

set $saveeip = $eip
set $eip = ((void**)$esp)[1] 

Then you can run the usual commands:

bt
frame 4
list

Once you're done, you can restore the original stack pointer with:

set $eip = $saveeip

Run with a Local Copy of the Macros

I've been modifying the macro code a bit, so to use my local copy instead of the main one in AFS I invoke gdb with:

gdb -x ~/pintos/src/misc/gdb-macros kernel.o

Inspecting Function Arguments, Locals, and Code

When gdb is stopped, you're almost always within a function. You can jump up and down the call stack using the frame command (for example frame 3 to jump to the #3 function listed by bt). You can get a lot of useful context about the function you're in from these commands.

To show the values of local variables, run info locals.

To show the values of the function input arguments, run info args.

To look at the lines of source code around the currently executing line, run list.

More Resources

https://www.scs.stanford.edu/24wi-cs212/pintos/pintos_10.html

https://cs162.org/static/proj/pintos-docs/docs/development/gdb/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment