Address Breakpoint
A naive implementation of breakpoint would be simply single stepping until we reach the breakpoint instruction. In practice, that would be horribly slow, and therefore we will not do that.
We will introduce a new break instruction that instruct the debugger to break. When creating an address breakpoint, we simply ask the virtual machine to replace the current instruction by the break instruction.
Interface
class debugger
{
public:
void resume();
breakpoint* create_address_breakpoint(int address);
};
class virtual_machine_debugging_interface
{
public:
virtual void resume() = 0;
virtual instruction* get_instruction(int address) = 0;
virtual void set_instruction(int address, instruction* instruction) = 0;
};
class debugger_virtual_machine_interface
{
public:
virtual void on_break_instruction() = 0;
virtual void on_terminate() = 0;
};
class breakpoint
{
public:
int get_address();
void remove();
};
Implementation
Adding a breakpoint and make the program break is NOT the hard part. The hard part is that after we hit the breakpoint, we need to be able to resume correctly (i.e. not missing the current instruction patched away by the breakpoint) and the breakpoint stay there. After stopping at the breakpoint, we need to replace the original instruction back to the instruction stream, single step it, and then put the breakpoint back. This is easier said than done, try it if you do not trust me.
A special case - if you hit a breakpoint while you are single stepping (i.e. you are single stepping into a break instruction, you get on_break_instruction()
call back instead of the on_single_Step()
callback.
Practical notes
Once you get your hand dirty with this, you will soon realize logic could get really messy when we move on. Indeed, it will, since all the interesting events are all called through only a few callbacks. We must be able to dispatch these events and untangle the logic. Chain of responsibility is a really good pattern here to get the logic untangled.
Moving forward
With the address breakpoint, we have done with implementing the low level debugging primitives. We will start on high level debugging.