Stack unwinder

For high level debugging, we will start with a simple question. Where am I? I know my IP, but I want to know what function it is. We need to start introducing symbols.

I could have introduced symbols first, but building symbols without a context how the symbol is used is not very useful, therefore I chose to build the stack unwinder and make building the symbols part of it.

Interface

class debugger
{
public:
    void stack_walk();
}

class virtual_machine
{
public:
    debugger* debug(instruction_sequence instructions, symbols* symbols, int entry_point);
};

struct local_symbols
{
    string local_name;
    int address;
};

struct function_symbol
{
    string function_name;
    int entry_point;
    int after_exit;
    vector<local_symbols> locals;
};

struct symbols
{
    vector<function_symbol> functions;
};

struct code_generation_outputs
{
    instruction_sequence instructions;
    int entry_point;
    symbols symbols;
};

Interface explained

The story starts with modifying the code generator to generate the symbols in the code generator output. A debugger takes those as input and perform the stack_walk(). The stack walk will simply print the call stack together with the local variable values to the console.

Implementation

This is going to be a long exercise, but none of the steps are hard. Use labels in the code generation will allow us to build the symbols, doing a simple search in the symbol will find us the function name of the top frame, looking at the first instruction (as guaranteed by the ABI, this will be right), we know how much stack space we need to skip before we can find the return address and old stack pointer value, we can recurse until we find the lowest frame. Local variables can be found by reading memory. This requires us to keep the local variable in memory.

Practice notes

In practice, a stack unwinder is never as simple. Local variable could be stored in registers, so we need to restore them. Stack unwinding also often needed in runtime for exception processing, so the data for stack unwinding is often also available at runtime. Inlining and frame pointer omission are typical optimization trick that often make unwinding more difficult.

results matching ""

    No results matching ""