Debugging with Bochs and gdb
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.
Connect a gdb session to bochs
Run bochs with gdb-stub enabled.
Refer here to
build bochs from source. 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 gdb, when debugging with bochs, interprets
addresses and registers incorrectly (breakpoints stop working, too), the
problem may lie with your configuration.
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.
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.
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.