bytecodealliance / wasm-micro-runtime

WebAssembly Micro Runtime (WAMR)
Apache License 2.0
4.96k stars 624 forks source link

Access to call stack when calling native functions #3779

Open sasq64 opened 2 months ago

sasq64 commented 2 months ago

Feature

When a native function is called, I would like to be able to get the call stack from the WASM side to see how the call originated.

Benefit

If anything goes wrong in the native code, it is often very helpful to see which file and line caused the issue in the web assembly code. For instance if I implement a load_file() function, and the file could not be found I can show an exception with information where this call was performed so it can be fixed.

Implementation

Since call stack dumps can already be performed, this information should already be available? Also it is fine if only addresses are provided, the native side can use separate debug info file to look up actual file name and line.

Alternatives

...

sasq64 commented 2 months ago

NOTE: Calling wasm_runtime_dump_call_stack(env) from the native function does nothing since at that point module->frames is empty... (wasm code built with debug, stack trace enabled, classic interpreter)

loganek commented 2 months ago

Not sure if that helps, but native function has access to the exec_env structure, so in order to obtain the call stack, you could do something like (haven't tested, so there might be some syntax issues):

static void your_native_function(wasm_exec_env_t exec_env)
{
  struct WASMInterpFrame *frame = exec_env->cur_frame; // please note that for AOT this is an instance of `AOTFrame`
  while (frame) {
    // do something with the frame
    frame = frame->prev_frame;
  }
}
sasq64 commented 2 months ago

Not sure how to get the relevant info from the frame - there is an ip ptr that I can maybe use if I know the start of the memory to subtract from it.

I have a solution now where I dump a stack trace to a string, through

    std::array<char, 1024> buf;
    wasm_interp_create_call_stack(env);
    wasm_interp_dump_call_stack(env, false, buf.data(), buf.size());

Then I parse the string to get the addresses and use it agains a lookup table I have created from a .map file...

sasq64 commented 2 months ago

Actually this works;

    auto* inst = wasm_runtime_get_module_inst(env);
    auto* module = wasm_runtime_get_module(inst);
    auto ip = (uint32)(cur_frame->prev_frame->ip - ((WASMModule*)module)->load_addr);
loganek commented 2 months ago

Yes, similarly, the function index can be calculated by doing the following:

auto* inst = wasm_runtime_get_module_inst(env);
(uint32)(frame->function - inst->e->functions);

see implementation of the wasm_interp_create_call_stack function https://github.com/bytecodealliance/wasm-micro-runtime/blob/886f9444b9633f9af1bd3268ddbe7877a05de0f2/core/iwasm/interpreter/wasm_runtime.c#L4040 for more details.