1. Setup
Enable debugging
symbols
Edit Makefile and
include -g in your compiler options by adding the line, CFLAGS+= -g. Optionally, you can disable optimizations by removing the
line, CFLAGS+= -O2. Optimizations may get in the way of debugging.
Finally, rebuild
using make clean; make. Cleaning the project is a necessary first step because
changing compilation flags will not cause make to rebuild anything.
Enable debugging
with gdb in bochs
Add the line,
gdbstub:
enabled=1, port=1234, text_base=0, data_base=0, bss_base=0
to bochsrc to enable
gdb debugging. In this case, the port is 1234, but you can choose any open port
numbered above 1024 as long as you tell gdb to connect to the same port.
By adding this line,
you may only use the bochsrc file with a version of bochs configured with the
gdb-stub enabled. Running without this configuration will cause bochs to panic
and die; comment it out by prepending a # at the
beginning of the line.
2. Run bochs-gdb
If bochs complains
about "usb-uhci" in the bochsrc file, comment out the line by
pre-pending with #. You will need to
setup your $BXSHARE variable for debugging.
You can do this easily by hardcoding the path in the two locations it
appears in the bochsrc file. Replace it
with /usr/share/bochs/ (for romimage and vgaromimage lines).
In lab 010, run /u/318/bin/bochs-gdb (optionally with the -q option to skip
the intro menu). Bochs will load and
await a connection from gdb. If you are
not using a lab machine, you will need to run bochs with gdb-stub enabled, and
will need to build bochs from source (http://www.cs.princeton.edu/courses/archive/fall10/cos318/projects/project1/bochs_setup.html).
3. Run gdb
Run gdb kernel to
debug kernel (the ELF file generated by compiling your kernel).
Rather than running
the kernel on the local machine (this will not work), you need to debug the
instance running inside of bochs. Connect gdb to that session by invoking
target remote localhost:1234, where localhost should
be replaced with the name or IP address of the computer running bochs if it's
not running locally, and where 1234 is whichever port you've chosen.
Upon a successful
connection, bochs will break at the first instruction in the BIOS (not the bootloader nor the kernel). Notice that you won't
be able to inspect kernel data at this point because your kernel has not yet
been loaded by the bootloader.
i.e. Type "gdb
kernel", then "b 25" (set breakpoint at line 25 in kernel.c),
then "target remote localhost:1234".
4. More information
Using gdb
Refer to http://users.ece.utexas.edu/~adnan/gdb-refcard.pdf,
a search engine, and gdb's help command for more information.
If you want to debug
a user program (not part of the kernel), such as process1, load its symbol file
using add-symbol-file process1 0x4000. In this case, process1 is loaded at
address 0x4000 (specified in your Makefile). Then try setting a breakpoint, for
example: b process1.c:draw.
To streamline the
process, create a .gdbinit file in the project directory that contains
something along the lines of:
file kernel
add-symbol-file process1 0x4000 target remote :1234 set disassembly-flavor att
b kernel.c:25
There are many
front-ends for gdb which often provide graphical interfaces to aid the display
of data and source code, such as gdbtui, ddd, xcode, and kdbg. (The first and last are already installed in lab 010). Some,
such as kdbg, won't read .gdbinit and may have other limitations.
GDB Quickstart
http://www.eecs.umich.edu/~sugih/pointers/gdbQS.html
Custom gdb commands
Add the following to
.gdbinit:
define ltsk set
$count = sizeof(task)/sizeof(task[0]) set $idx = 0 printf "Entry
Type\n" while($idx < $count) set $t = task[$idx] set $idx++
if($t.task_type == KERNEL_THREAD) printf "%p \t KERNEL_THREAD\n",
$t.entry_point else printf "%p \t PROCESS\n", $t.entry_point end end
end
to create create the
ltsk command which lists the processes that your OS will run, printing the
entry point and thread type. Custom commands are particularly useful for
debugging operating systems because many data structures are used and
navigating through them by hand is tedious. For example, you can create a
command to list objects of a particular type (list all blocked programs) or
perform an analysis (how many resources are consumed?). Commands also accept
arguments.