Don't underestimate the value of @func{printf}. The way
@func{printf} is implemented in Pintos, you can call it from
practically anywhere in the kernel, whether it's in a kernel thread or
-an interrupt handler, almost regardless of what locks are held (but see
-@ref{printf Reboots} for a counterexample).
+an interrupt handler, almost regardless of what locks are held.
@func{printf} is useful for more than just examining data.
It can also help figure out when and where something goes wrong, even
when the kernel crashes or panics without a useful error message. The
-strategy is to sprinkle calls to @func{print} with different strings
+strategy is to sprinkle calls to @func{printf} with different strings
(e.g.@: @code{"<1>"}, @code{"<2>"}, @dots{}) throughout the pieces of
code you suspect are failing. If you don't even see @code{<1>} printed,
then something bad happened before that point, if you see @code{<1>}
functions that were running at the time of the panic. You can also
insert a call to @func{debug_backtrace}, prototyped in
@file{<debug.h>}, to print a backtrace at any point in your code.
+@func{debug_backtrace_all}, also declared in @file{<debug.h>},
+prints backtraces of all threads.
The addresses in a backtrace are listed as raw hexadecimal numbers,
which are difficult to interpret. We provide a tool called
call A), then it's a good sign that you're corrupting a kernel
thread's stack, because the backtrace is extracted from the stack.
Alternatively, it could be that the @file{kernel.o} you passed to
-@command{backtrace} does not correspond to the kernel that produced
+@command{backtrace} is not the same kernel that produced
the backtrace.
-Sometimes backtraces can be confusing without implying corruption.
+Sometimes backtraces can be confusing without any corruption.
Compiler optimizations can cause surprising behavior. When a function
has called another function as its final action (a @dfn{tail call}), the
calling function may not appear in a backtrace at all. Similarly, when
The backtrace output would then look something like this:
@example
-0xc0106eff: debug_panic (../../lib/debug.c:86)
-0xc01102fb: file_seek (../../filesys/file.c:405)
-0xc010dc22: seek (../../userprog/syscall.c:744)
-0xc010cf67: syscall_handler (../../userprog/syscall.c:444)
-0xc0102319: intr_handler (../../threads/interrupt.c:334)
-0xc010325a: ?? (threads/intr-stubs.S:1554)
-0x804812c: ?? (??:0)
-0x8048a96: ?? (??:0)
-0x8048ac8: ?? (??:0)
+0xc0106eff: debug_panic (lib/debug.c:86)
+0xc01102fb: file_seek (filesys/file.c:405)
+0xc010dc22: seek (userprog/syscall.c:744)
+0xc010cf67: syscall_handler (userprog/syscall.c:444)
+0xc0102319: intr_handler (threads/interrupt.c:334)
+0xc010325a: intr_entry (threads/intr-stubs.S:38)
+0x0804812c: (unknown)
+0x08048a96: (unknown)
+0x08048ac8: (unknown)
@end example
(You will probably not see exactly the same addresses if you run the
so: (typing the command on a single line, of course):
@example
-backtrace grow-too-big 0xc0106eff 0xc01102fb 0xc010dc22 0xc010cf67
-0xc0102319 0xc010325a 0x804812c 0x8048a96 0x8048ac8
+backtrace tests/filesys/extended/grow-too-big 0xc0106eff 0xc01102fb
+0xc010dc22 0xc010cf67 0xc0102319 0xc010325a 0x804812c 0x8048a96
+0x8048ac8
@end example
The results look like this:
@example
-0xc0106eff: ?? (??:0)
-0xc01102fb: ?? (??:0)
-0xc010dc22: ?? (??:0)
-0xc010cf67: ?? (??:0)
-0xc0102319: ?? (??:0)
-0xc010325a: ?? (??:0)
-0x804812c: test_main (../../tests/filesys/extended/grow-too-big.c:20)
-0x8048a96: main (../../tests/main.c:10)
-0x8048ac8: _start (../../lib/user/entry.c:9)
+0xc0106eff: (unknown)
+0xc01102fb: (unknown)
+0xc010dc22: (unknown)
+0xc010cf67: (unknown)
+0xc0102319: (unknown)
+0xc010325a: (unknown)
+0x0804812c: test_main (...xtended/grow-too-big.c:20)
+0x08048a96: main (tests/main.c:10)
+0x08048ac8: _start (lib/user/entry.c:9)
+@end example
+
+You can even specify both the kernel and the user program names on
+the command line, like so:
+
+@example
+backtrace kernel.o tests/filesys/extended/grow-too-big 0xc0106eff
+0xc01102fb 0xc010dc22 0xc010cf67 0xc0102319 0xc010325a 0x804812c
+0x8048a96 0x8048ac8
+@end example
+
+The result is a combined backtrace:
+
+@example
+In kernel.o:
+0xc0106eff: debug_panic (lib/debug.c:86)
+0xc01102fb: file_seek (filesys/file.c:405)
+0xc010dc22: seek (userprog/syscall.c:744)
+0xc010cf67: syscall_handler (userprog/syscall.c:444)
+0xc0102319: intr_handler (threads/interrupt.c:334)
+0xc010325a: intr_entry (threads/intr-stubs.S:38)
+In tests/filesys/extended/grow-too-big:
+0x0804812c: test_main (...xtended/grow-too-big.c:20)
+0x08048a96: main (tests/main.c:10)
+0x08048ac8: _start (lib/user/entry.c:9)
@end example
Here's an extra tip for anyone who read this far: @command{backtrace}
@menu
* Using GDB::
* Example GDB Session::
-* Debugging User Programs::
* GDB FAQ::
@end menu
@code{struct}) in which @var{element} is the @struct{list_elem} member
that links the elements.
-Example: @code{dumplist all_list thread all_elem} prints all elements of
+Example: @code{dumplist all_list thread allelem} prints all elements of
@struct{thread} that are linked in @code{struct list all_list} using the
-@code{struct list_elem all_elem} which is part of @struct{thread}.
+@code{struct list_elem allelem} which is part of @struct{thread}.
@end deffn
@deffn {GDB Macro} btthread thread
@struct{list_elem} field used inside @struct{thread} to link the threads
together.
-Example: @code{btthreadlist all_list all_elem} shows the backtraces of
+Example: @code{btthreadlist all_list allelem} shows the backtraces of
all threads contained in @code{struct list all_list}, linked together by
-@code{all_elem}. This command is useful to determine where your threads
+@code{allelem}. This command is useful to determine where your threads
are stuck when a deadlock occurs. Please see the example scenario below.
@end deffn
+@deffn {GDB Macro} btthreadall
+Short-hand for @code{btthreadlist all_list allelem}.
+@end deffn
+
@deffn {GDB Macro} btpagefault
Print a backtrace of the current thread after a page fault exception.
Normally, when a page fault exception occurs, GDB will stop
-with a message that might say:
+with a message that might say:@footnote{To be precise, GDB will stop
+only when running under Bochs. When running under QEMU, you must
+set a breakpoint in the @code{page_fault} function to stop execution
+when a page fault occurs. In that case, the @code{btpagefault} macro is
+unnecessary.}
@example
Program received signal 0, Signal 0.
backtrace. Use @code{btpagefault} instead.
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}).
+process. In this case, you may wish to also load the user program's
+symbol table using the @code{loadusersymbols} macro, as described above.
@end deffn
@deffn {GDB Macro} hook-stop
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
+Project 3, the situation will change if you use the @func{get_user} and
@func{put_user} strategy to verify user memory accesses
(@pxref{Accessing User Memory}).
+
+@c ----
+@c Unfortunately, this does not work with Bochs's gdb stub.
+@c ----
+@c If you don't want GDB to stop for page faults, then issue the command
+@c @code{handle SIGSEGV nostop}. GDB will still print a message for
+@c every page fault, but it will not come back to a command prompt.
@end deffn
@node Example GDB Session
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
+named @code{allelem}, 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}
+(gdb) @strong{btthreadlist all_list allelem}
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
Knowing where threads are stuck can be tremendously useful, for instance
when diagnosing deadlocks or unexplained hangs.
-@node Debugging User Programs
-@subsection Debugging User Programs
+@deffn {GDB Macro} loadusersymbols
-You can also use GDB to debug a user program running under
-Pintos. Start by issuing this GDB command to load the
-program's symbol table:
+You can also use GDB to debug a user program running under Pintos.
+To do that, use the @code{loadusersymbols} macro to load the program's
+symbol table:
@example
-add-symbol-file @var{program}
+loadusersymbols @var{program}
@end example
@noindent
where @var{program} is the name of the program's executable (in the host
-file system, not in the Pintos file system). After this, you should be
+file system, not in the Pintos file system). For example, you may issue:
+@smallexample
+(gdb) @strong{loadusersymbols tests/userprog/exec-multiple}
+add symbol table from file "tests/userprog/exec-multiple" at
+ .text_addr = 0x80480a0
+(gdb)
+@end smallexample
+
+After this, you should be
able to debug the user program the same way you would the kernel, by
placing breakpoints, inspecting data, etc. Your actions apply to every
user program running in Pintos, not just to the one you want to debug,
-so be careful in interpreting the results. Also, a name that appears in
+so be careful in interpreting the results: GDB does not know
+which process is currently active (because that is an abstraction
+the Pintos kernel creates). Also, a name that appears in
both the kernel and the user program will actually refer to the kernel
name. (The latter problem can be avoided by giving the user executable
name on the GDB command line, instead of @file{kernel.o}, and then using
-@code{add-symbol-file} to load @file{kernel.o}.)
+@code{loadusersymbols} to load @file{kernel.o}.)
+@code{loadusersymbols} is implemented via GDB's @code{add-symbol-file}
+command.
+
+@end deffn
@node GDB FAQ
@subsection FAQ
print out more debug information, such as the exact type of fault that
occurred. It's not very hard. You start by retrieving the source
code for Bochs 2.2.6 from @uref{http://bochs.sourceforge.net} and
-extracting it into a directory. Then read
-@file{pintos/src/misc/bochs-2.2.6.README} and apply the patches needed.
-Then run @file{./configure}, supplying the options you want (some
-suggestions are in the patch file). Finally, run @command{make}.
-This will compile Bochs and eventually produce a new binary
-@file{bochs}. To use your @file{bochs} binary with @command{pintos},
+saving the file @file{bochs-2.2.6.tar.gz} into a directory.
+The script @file{pintos/src/misc/bochs-2.2.6-build.sh}
+applies a number of patches contained in @file{pintos/src/misc}
+to the Bochs tree, then builds Bochs and installs it in a directory
+of your choice.
+Run this script without arguments to learn usage instructions.
+To use your @file{bochs} binary with @command{pintos},
put it in your @env{PATH}, and make sure that it is earlier than
-@file{/usr/class/cs140/`uname -m`/bochs}.
+@file{@value{localpintosbindir}/bochs}.
Of course, to get any good out of this you'll have to actually modify
Bochs. Instructions for doing this are firmly out of the scope of