Skip to content

The edit debug cycle

Johan Manuel edited this page Mar 23, 2022 · 5 revisions

When working on SnowflakeOS, how does one go about adding new bugs to it?

It's simple, really.

First, write whatever you feel might work

If you don't know where to begin, you may consider that the code "doesn't work" as you wish it did and skip to the next section.

Then, you open your favorite terminal, which is guake, naturally, and you do one of these things:

  • make strict, if you want to take it slow and fix warnings one by one,
  • make qemu, to actually run SnowflakeOS and see how things go.

Note that before running anything, you've opened another tmux tab/terminal running tail -F serial.log; that'll show you the realtime output of SnowflakeOS's serial, i.e. all of your prints. If you don't, you'll see the whole output anyway when exiting qemu.

It doesn't work

No way!!

Once you've fixed every single error and warning, and your code looks like it should work, but doesn't for some reason, it's time to debug it. Here, you have a few options:

  • building with make clean; make qemu UBSAN=1 to catch null pointers and undefined behavior, complete with line numbers and stacktrace
  • printk, printke and printf like a crazy person: my go-to if ubsan reveals nothing
  • placing breakpoints with BREAK() and running make bochs: this gets to the bottom of the worst bugs, every time
  • adding a well-placed stacktrace_print() or while (1);: helpful for looking at the page tables, memory or what have you, with either qemu or bochs
  • remote gdb with qemu is broken for some reason, don't lose time there
  • using a tool like ripgrep or your IDE to track references: where is BREAK called from ? rg BREAK.

qemu specifics

  • q quits the whole thing
  • info mem and info tlb show paging information
  • qemu's fast, that's the main attraction, it's awesome for print-based debugging and a fast edit/run cycle

bochs specifics

  • help shows you a lot
  • info tab gets you page tables
  • print-stack does what it says, and symbols are loaded so you get the backtrace too, when the execution's in kernel
  • bt also gets you a baktrace
  • pressing enter executes the current instruction
  • double-clicking a line sets a breakpoint on it
  • c continues execution