Debugging a kernel can be challenging. Sometimes a straightforward approach to debugging works: you can add log_printf() statements to the kernel, or console_printf() statements to user-level code, to print state and see how it evolves. Unfortunately, sometimes these techniques don’t work. For example, your kernel might be so messed up during early boot that the kernel cannot properly initialize the console used by log_printf(). In this scenario, you might at least be able to see whether the kernel is reaching a certain point by intentionally inserting an infinite loop into your kernel code—if your kernel hangs, the line of code representing the infinite loop was reached, but if your kernel crashes, the line was not reached.
In these situations, debugging Chickadee with gdb might be helpful. For example, suppose that you want to debug Chickadee as it runs p-testzombie.cc. From a terminal window in your Linux VM, do make run-gdb-testzombie; this will compile Chickadee and launch qemu as well as gdb. The qemu window will initially be paused. In your terminal window, you’ll see the gdb command line. From that command line, you can set breakpoints in both Chickadee code and user-level code like p-testzombie.cc. For example, you can set a breakpoint in p-testzombie.cc by typing break p-testzombie.cc:SOME_LINE_NUMBER into the gdb command line. Then, if you type continue, this will tell gdb to allow qemu to proceed with executing Chickadee. Later, when one of your breakpoints is hit, control will return to gdb. At this point, you can then type things like print SOME_VAR_NAME to see the value for a C++-level variable. You can also do info registers to print all of the register values. If you only want to print one register value, you can do something like print \$rsp to just print %rsp.
gdb is very powerful. A variety of tutorials on the Internet discuss how to use gdb. For example, see this page for an overview of the most useful gdb commands.