Debugging with Compiler Optimizations
Compiler optimizations are turned on by flags such as
-O2. For example, your
Makefile might
have the line:
CFLAGS += -O2
Here are some optimizations that may get in the way of debugging:
-
Function inlining
- Your callstack will omit the inlined function
- Breakpoints at the function may not work
-
Storing variables in registers
- print var-name won't work
- Watching the variable won't work
- You'll have to figure out what register it's stored in
-
Ommiting the frame pointer
-
Result: the %ebp register is not pushed
onto the stack; the debugger won't be able to trace the call stack
-
Backtraces may not be accurate (press Ctrl+C if the debugger stops
responding)
- You may have to determine the call stack by hand
-
Use frame addr to specify the
stack frame manually
-
Instruction reordering
- Line numbers become mistmatched when stepping through code
Bochs vs Real Computers
Question: Why won't my code work on a real machine
when it works perfectly in bochs?
Answer: Your code has bugs.
In general, testing your code on multiple computers is a good practice
because each has a slightly different setup; there may be machine-specific
settings which hide your bugs.
If you find that your code works in bochs, but not on other machines,
consider these differences that are unique to bochs:
-
Bochs initializes memory to 0 on startup. Uninitialized data, if you
expect it to be 0, will not always be so
-
The timer interrupt is triggered at exact intervals; it is
deterministic (real machines have jitters)
-
Initial register values may differ (on bootup, %dl
will, however, point to the boot device)
-
The boot device may differ
Once you get past loading your kernel, your primary challenge from this list
will be dealing with uninitialized data (pointers).