AndrewAPrice wrote:
rdos wrote:
Basically, you need to implement the debugging interface that your debugger requires.
Building my own GDBstub doesn't actually seem that difficult (see
this journey of someone embedded a GDBstub into a Gameboy emulator). The protocol is a bit overwhelming but then they say just a few basic commands are needed.
It's the same complexity as the Watcom interface. The protocol is not that hard to support. However, you also need a debug framework. In my experience, the debug framework has similar complexity as the debug protocol.
AndrewAPrice wrote:
rdos wrote:
It will cause int 1. You also need to support hardware & software breakpoints.
I am on x86. Is there a reason to support both? Other than hardware breakpoints are limited to
the 4 debug registers.
I think you should support both. You also need to prioritize which one to use. Some things, like data breakpoints can only be done with hardware breakpoints, while simple stops are easier to do with software (planting int 3 in the code).
Supporting breakpoints, handling single step & exceptions is part of the debug framework you need to implement, You also need a method to read & write registers in a debugged thread. If you don't save registers in the thread block, rather keep them on the stack, this will get more complicated to do.
AndrewAPrice wrote:
rdos wrote:
I want only the debugged thread to be stopped, not the whole OS.
Debugging the kernel is easy because QEMU has a built in gdbstub I can connect to. For debugging user programs in my microkernel, I prefer that my gdbstub be a service (e.g. a "GDB Debug Server") but it leads to the problem that I can't debug my Debug Server. (Unless I have multiple debug servers running?
)
If it was in the kernel, I can debug the debug code (via QEMU's gdbstub) but it requires my kernel knowing how to talk to the transport protocol, I don't have access to the C++ UI library (so I can draw a window that says "Hey GDB just connected. Which process would you like to launch or connect it to?"), which stops my microkernel from being very generic. (My own self imposed limitation.)
Generally, the debug server will run in it's own process. That means it must be able to read & write memory in other processes (and, of course, register state). In my design, I can single step into kernel with the application debugger. This is possible since the debugger has addons for understanding device drivers, and because single step & hardware breakpoints work in kernel space too. This is part of the reason I only stop threads that are debugged.
As for attaching the debugger to a process, I can do this both with ordinary programs and file system servers. Basically, the debugger is given the name of the process executable, and then the debug server looks for a process with that name, and if it finds it, attaches the debugger to it. If it is not running, it loads it from disc. This, of course, only works if a single instance of each program runs.
Also, for a serious project, you should not depend on running your OS in an emulator. Debugging should work on real hardware too, including debugging kernel on real hardware.