I will introduce three useful debugging tools in Pintos.
Printf() is implemented in Pintos, we can call it from practically anywhere in the kernel.
Pintf() is very useful. It can help us to figure out when and where something gose wrong. We can use it as the normal C language.
When we read the source code, we can see many ASSERT().
Assertions are useful because they can catch problems early, before they'd otherwise be noticed. Ideally, each function should begin with a set of assertions that check its arguments for validity. (Initializers for functions' local variables are evaluated before assertions are checked, so be careful not to assume that an argument is valid in an initializer.) You can also sprinkle assertions throughout the body of functions in places where you suspect things are likely to go wrong. They are especially useful for checking loop invariants.
Pintos provides the ASSERT macro, defined in
GDB is the most important tool for us. You can run Pintos under the supervision of the GDB debugger.
GDB macro configuration file:
Enter $PINTOSDIR /src/utils
Open pintos-gdb with vim or other editor. Setting the “GDBMACROS=” as the location of the gdb-macros in your computer.
Eg:
change:
GDBMACROS = /usr/...
to:
GDBMACROS = /home/liang/pintos/src/misc/gdb-macros
Close and save it.
We can use the macros provide by pintos.
How to start debugging?
eg:
After we complied the program.
Open a terminal. Then we need to enter /src/threads/build.
Input: pintos --gdb –- run alarm-multiple
please pay attention to the space between the –-gdb and run.
And then, open another terminal. Enter /src/thread/build
input: pintos-gdb kernerl.o
then, input: debugpintos or target remote localhost:1234
Now,we can use GDB to do debug.
You can read the GDB manual by typing info gdb at a terminal command prompt. Here's a few commonly useful GDB commands for part 1:
GDB Command:c
Continues execution until Ctrl+C or the next breakpoint.
GDB Command:break function
GDB Command:break file:line
GDB Command:break *address
Sets a breakpoint at function, at line within file, or address. (Use a 0x prefix to specify an address in hex.)
Use break main
to make GDB stop when Pintos starts running.
GDB Command: p expression
Evaluates the given expression and prints its value. If the expression contains a function call, that function will actually be executed.
GDB Command: l *address
Lists a few lines of code around address. (Use a 0x prefix to specify an address in hex.)
GDB Command: bt
Prints a stack backtrace similar to that output by the backtrace program described above.
GDB Command: p/a address
Prints the name of the function or variable that occupies address. (Use a 0x prefix to specify an address in hex.)
GDB Command: diassemble function
Disassembles function.
We also provide a set of macros specialized for debugging Pintos, written by Godmar Back [email protected]. You can type help user-defined for basic help with the macros. Here is an overview of their functionality, based on Godmar's documentation:
More information:
http://web.stanford.edu/class/cs140/projects/pintos/pintos_10.html#SEC145
make breakpoint
When we need make a breakpoint in assemble program. We can't use break function, we need to use break address(e.g b 0x7c00)
Some command for debug assemble program:
GDB Command: ni (next instruction)
Used for single step.
GDB Command: si (step instruction)
Used for single step.
GDB Command: display
Register window. This command coule be used to watch the value in register.
e.g
(gdb) display /x $eax
(gdb) display /x $ebx
(gdb) display /x $ecx
(gdb) display /x $edx
If you want to watch registers, use 'p expression'.
e.g
p $eax
In this assignment, you will use the GDB to trace the loader and kernel's code to understand how Pintos is loading. If you are not already familiar with x86 assembly language, the PC Assembly Language Book is an excellent place to start.
Warning: Unfortunately the examples in the book are written for the NASM assembler, whereas we will be using the GNU assembler. NASM uses the so-called Intel syntax while GNU uses the AT&T syntax. Luckily the conversion between the two is pretty simple, and is covered in Brennan's Guide to Inline Assembly.
After you have learned the basic syntax of x86 assembly language, the next step is to learn how Pintos is loading. Make sure you have read through from "3.1.1.1 The Loader" to "3.1.1.4 Physical Memory Map" before you do the requirement.
In original Pintos, it does not have GDB supportment for tracing the BIOS and loader's 16bit code. So we have modified the gdb-macro to support this. You can optionally download our 'gdb-macro', replace the original 'src/misc/gdbmacro' or insert original 'gdbmacro' with following code inside the function named hook-stop
:
# There doesn't seem to be a good way to detect if we're in 16- or
# 32-bit mode, but we always run with CS == 8 in 32-bit mode.
if $cs != 8
# Translate the segment:offset into a physical address
printf "[%4x:%4x] ", $cs, $eip
x/i $cs*16+$eip
end
Run pintos --gdb -- -q
and connect to gdb in a new terminal. Set a breakpoint at address 0x7c00
, which is where the boot sector will be loaded(in file loader.S). Continue execution until that breakpoint. Use GDB's si (Step Instruction) command to trace into the Loader for a few more instructions, and try to guess what it might be doing. No need to figure out all the details - just the general idea of what the Loader is doing first.
After that, you can set a breakpoint in main and continue. And then, use gdb to trace through until Pintos booting complete. Try to figure out the order of modules of kernel initialization.
Download and fill the DESIGNDOC and archive DESIGNDOC into a zip file named sid_nameInPinYin_vid.zip(e.g 12330441_zhangsan_v0.zip) and upload to the ftp://my.ss.sysu.edu.cn/~wh/homework_upload/13%BC%B6%20%B2%D9%D7%F7%CF%B5%CD%B3/assignment_2/.