WebAssembly / exception-handling

Proposal to add exception handling to WebAssembly
https://webassembly.github.io/exception-handling/
Other
149 stars 33 forks source link

debugging #307

Open devsnek opened 3 weeks ago

devsnek commented 3 weeks ago

(based on this issue: https://github.com/denoland/deno/issues/22861)

What is a human supposed to do with a WebAssembly.Exception instance? There seems to be no stack trace captured during wasm execution and any data this object does contain is not enumerable, so there is nothing one can do to extract any useful information from it?

If I'm wrong or there are future plans or I'm generally being dumb please let me know...

dschuff commented 3 weeks ago

No, you aren't wrong or being dumb; when an exception is created implicitly via the wasm throw instruction, there is no stack trace captured. This is to keep exception creation/throwing as fast as possible (some use cases want to use exceptions as a more general control-flow mechanism rather than having them be truly exceptional, and having to do a stack walk on every throw is problematic). But that means when the exception propagates out to JS there isn't the usual stack trace that you'd expect on an Error object. It is possible to create an exception that has a stack trace via the JS API though. You can call out from wasm to JS and run something like the following:

let e = new WebAssembly.Exception(MyExceptionTag, [payload], {traceStack: true});
e.message = "An exception message";
throw e;

Where MyExceptionTag is exported from the wasm module. This allows catching these exceptions (and rethrowing and extracting the payload) in wasm the same way you'd catch exceptions thrown with the throw instruction (using the same tag). And when the exception propagates out from wasm to JS the WebAssembly.Exception that comes out will have the message and stack trace. It's still observably different from an instance of Error but it has the fields that you want. This is a bit of a compromise but works well, and it's what we do in emscripten.

Of course if you're calling out to JS, it's also possible to just actually create and throw real Error objects. You can rethrow them in wasm, and if you import the JS exception tag into your module, you can catch them by tag and extract the Error object as the payload (it will appear as an externref).

devsnek commented 3 weeks ago

hmm this generally makes sense to me, though the problem I encountered is a bit different. I did not author the wasm, and the person who did doesn't understand why it created this exception either (interestingly, I think it was created using emscripten as well? But it doesn't export any tags or anything that JS could use to inspect the exception. I'm not super familiar with the details of dart to wasm compilation though, this was just something a user reported to me)

I get that there's a conceptual boundary formed by tags as capabilities, but I feel like the API is lacking in that there's no way to inspect these at all. Do I really need to like mod v8 or something to debug a wasm blob that is misbehaving?

dschuff commented 3 weeks ago

If this is Dart code, then it probably wasn't created by Emscripten; dart has a different compiler. (I might be able to find out how that compiler uses exceptions though). Is this situation materially different from JS code that throws something other than an Error object? e.g. don't you also get an exception without a stack trace in that case?

devsnek commented 3 weeks ago

(I might be able to find out how that compiler uses exceptions though)

Heh I won't ask you to debug random code for me.

Is this situation materially different from JS code that throws something other than an Error object? e.g. don't you also get an exception without a stack trace in that case?

You will not have a stack trace, correct, though many JS engines can helpfully show you the location of the throw as long as it was not caught. I've also never had to debug code which does this in practice, and I would consider such code to be "extremely bad".

Perhaps this is more about how implementations should present exceptions, which is outside the control of wasm, but I would like to push for better information in the part we do have say over, the JS bindings.