+You may also use @code{btpagefault} for page faults that occur in a user
+process. In this case, you may also wish to load the user program's
+symbol table (@pxref{Debugging User Programs}).
+@end deffn
+
+@deffn {GDB Macro} hook-stop
+GDB invokes this macro every time the simulation stops, which Bochs will
+do for every processor exception, among other reasons. If the
+simulation stops due to a page fault, @code{hook-stop} will print a
+message that says and explains further whether the page fault occurred
+in the kernel or in user code.
+
+If the exception occurred from user code, @code{hook-stop} will say:
+@example
+pintos-debug: a page fault exception occurred in user mode
+pintos-debug: hit 'c' to continue, or 's' to step to intr_handler
+@end example
+
+In Project 2, a page fault in a user process leads to the termination of
+the process. You should expect those page faults to occur in the
+robustness tests where we test that your kernel properly terminates
+processes that try to access invalid addresses. To debug those, set a
+break point in @func{page_fault} in @file{exception.c}, which you will
+need to modify accordingly.
+
+In Project 3, a page fault in a user process no longer automatically
+leads to the termination of a process. Rather, you may have to page in
+the page containing the address the process was trying to access, either
+because it was swapped out or because this is the first time it's
+accessed. In either case, you will reach @func{page_fault} and need to
+take the appropriate action there.
+
+If the page fault did not occur in user mode while executing a user
+process, then it occurred in kernel mode while executing kernel code.
+In this case, @code{hook-stop} will print this message:
+@example
+pintos-debug: a page fault occurred in kernel mode
+@end example
+followed by the output of the @code{btpagefault} command.
+
+Before Project 3, a page fault exception in kernel code is always a bug
+in your kernel, because your kernel should never crash. Starting with
+Project 3, the situation will change if you use @func{get_user} and
+@func{put_user} strategy to verify user memory accesses
+(@pxref{Accessing User Memory}).
+@end deffn
+
+@node Example GDB Session
+@subsection Example GDB Session
+
+This section narrates a sample GDB session, provided by Godmar Back.
+This example illustrates how one might debug a Project 1 solution in
+which occasionally a thread that calls @func{timer_sleep} is not woken
+up. With this bug, tests such as @code{mlfqs_load_1} get stuck.
+
+This session was captured with a slightly older version of Bochs and the
+GDB macros for Pintos, so it looks slightly different than it would now.
+Program output is shown in normal type, user input in @strong{strong}
+type.
+
+First, I start Pintos:
+
+@smallexample
+$ @strong{pintos -v --gdb -- -q -mlfqs run mlfqs-load-1}
+Writing command line to /tmp/gDAlqTB5Uf.dsk...
+bochs -q
+========================================================================
+ Bochs x86 Emulator 2.2.5
+ Build from CVS snapshot on December 30, 2005
+========================================================================
+00000000000i[ ] reading configuration from bochsrc.txt
+00000000000i[ ] Enabled gdbstub
+00000000000i[ ] installing nogui module as the Bochs GUI
+00000000000i[ ] using log file bochsout.txt
+Waiting for gdb connection on localhost:1234
+@end smallexample
+
+@noindent Then, I open a second window on the same machine and start GDB:
+
+@smallexample
+$ @strong{pintos-gdb kernel.o}
+GNU gdb Red Hat Linux (6.3.0.0-1.84rh)
+Copyright 2004 Free Software Foundation, Inc.
+GDB is free software, covered by the GNU General Public License, and you are
+welcome to change it and/or distribute copies of it under certain conditions.
+Type "show copying" to see the conditions.
+There is absolutely no warranty for GDB. Type "show warranty" for details.
+This GDB was configured as "i386-redhat-linux-gnu"...
+Using host libthread_db library "/lib/libthread_db.so.1".
+@end smallexample
+
+@noindent Then, I tell GDB to attach to the waiting Pintos emulator:
+
+@smallexample
+(gdb) @strong{debugpintos}
+Remote debugging using localhost:1234
+0x0000fff0 in ?? ()
+Reply contains invalid hex digit 78
+@end smallexample
+
+@noindent Now I tell Pintos to run by executing @code{c} (short for
+@code{continue}) twice:
+
+@smallexample
+(gdb) @strong{c}
+Continuing.
+Reply contains invalid hex digit 78
+(gdb) @strong{c}
+Continuing.
+@end smallexample
+
+@noindent Now Pintos will continue and output:
+
+@smallexample
+Pintos booting with 4,096 kB RAM...
+Kernel command line: -q -mlfqs run mlfqs-load-1
+374 pages available in kernel pool.
+373 pages available in user pool.
+Calibrating timer... 102,400 loops/s.
+Boot complete.
+Executing 'mlfqs-load-1':
+(mlfqs-load-1) begin
+(mlfqs-load-1) spinning for up to 45 seconds, please wait...
+(mlfqs-load-1) load average rose to 0.5 after 42 seconds
+(mlfqs-load-1) sleeping for another 10 seconds, please wait...
+@end smallexample
+
+@noindent
+@dots{}until it gets stuck because of the bug I had introduced. I hit
+@key{Ctrl+C} in the debugger window:
+
+@smallexample
+Program received signal 0, Signal 0.
+0xc010168c in next_thread_to_run () at ../../threads/thread.c:649
+649 while (i <= PRI_MAX && list_empty (&ready_list[i]))
+(gdb)
+@end smallexample
+
+@noindent
+The thread that was running when I interrupted Pintos was the idle
+thread. If I run @code{backtrace}, it shows this backtrace:
+
+@smallexample
+(gdb) @strong{bt}
+#0 0xc010168c in next_thread_to_run () at ../../threads/thread.c:649
+#1 0xc0101778 in schedule () at ../../threads/thread.c:714
+#2 0xc0100f8f in thread_block () at ../../threads/thread.c:324
+#3 0xc0101419 in idle (aux=0x0) at ../../threads/thread.c:551
+#4 0xc010145a in kernel_thread (function=0xc01013ff , aux=0x0)
+ at ../../threads/thread.c:575
+#5 0x00000000 in ?? ()
+@end smallexample
+
+@noindent
+Not terribly useful. What I really like to know is what's up with the
+other thread (or threads). Since I keep all threads in a linked list
+called @code{all_list}, linked together by a @struct{list_elem} member
+named @code{all_elem}, I can use the @code{btthreadlist} macro from the
+macro library I wrote. @code{btthreadlist} iterates through the list of
+threads and prints the backtrace for each thread:
+
+@smallexample
+(gdb) @strong{btthreadlist all_list all_elem}
+pintos-debug: dumping backtrace of thread 'main' @@0xc002f000
+#0 0xc0101820 in schedule () at ../../threads/thread.c:722
+#1 0xc0100f8f in thread_block () at ../../threads/thread.c:324
+#2 0xc0104755 in timer_sleep (ticks=1000) at ../../devices/timer.c:141
+#3 0xc010bf7c in test_mlfqs_load_1 () at ../../tests/threads/mlfqs-load-1.c:49
+#4 0xc010aabb in run_test (name=0xc0007d8c "mlfqs-load-1")
+ at ../../tests/threads/tests.c:50
+#5 0xc0100647 in run_task (argv=0xc0110d28) at ../../threads/init.c:281
+#6 0xc0100721 in run_actions (argv=0xc0110d28) at ../../threads/init.c:331
+#7 0xc01000c7 in main () at ../../threads/init.c:140
+
+pintos-debug: dumping backtrace of thread 'idle' @@0xc0116000
+#0 0xc010168c in next_thread_to_run () at ../../threads/thread.c:649
+#1 0xc0101778 in schedule () at ../../threads/thread.c:714
+#2 0xc0100f8f in thread_block () at ../../threads/thread.c:324
+#3 0xc0101419 in idle (aux=0x0) at ../../threads/thread.c:551
+#4 0xc010145a in kernel_thread (function=0xc01013ff , aux=0x0)
+ at ../../threads/thread.c:575
+#5 0x00000000 in ?? ()
+@end smallexample
+
+@noindent
+In this case, there are only two threads, the idle thread and the main
+thread. The kernel stack pages (to which the @struct{thread} points)
+are at @t{0xc0116000} and @t{0xc002f000}, respectively. The main thread
+is stuck in @func{timer_sleep}, called from @code{test_mlfqs_load_1}.
+
+Knowing where threads are stuck can be tremendously useful, for instance
+when diagnosing deadlocks or unexplained hangs.