Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass original .NET callback exception as TrapException's inner exception #172

Merged

Conversation

kpreisser
Copy link
Contributor

@kpreisser kpreisser commented Oct 29, 2022

Hi, this PR implements the functionality to pass a .NET exception thrown at a wasm-to-host transition (.NET callback) (which means the exception is converted to a trap) as inner exception of the created TrapException when the trap bubbles up to the next host-to-wasm transition, by using a ThreadStatic (thread-local) field to temporarily store the exception until the stack is unwound.

That way, the original .NET exception that caused a trap can be retrieved from TrapException.InnerException, e.g. for debugging purposes.

If the exception thrown at the .NET callback is already a TrapException, we will not store that exception itself, but its InnerException, so that when the trap bubbles through multiple levels of wasm-to-host transitions, the InnerException at the root host-to-wasm transition will still be the original .NET exception.

Fixes #63

Note: The mechanism requires that on every location where a trap can occur due to calling wasm code, TrapException.FromOwnedTrap() is called when a trap actually occured, so that the thread-local field is cleared afterwards.

TODO:

What do you think?

Thanks!

@kpreisser
Copy link
Contributor Author

The tests are currently failing due to #171.

@peterhuene
Copy link
Member

We should be able to rebase this PR now.

@peterhuene peterhuene self-requested a review November 2, 2022 19:55
Copy link
Member

@peterhuene peterhuene left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing this feature! This should definitely help users in debugging host functions that throw an exception.

@peterhuene peterhuene merged commit 387edd2 into bytecodealliance:main Nov 2, 2022
@kpreisser kpreisser deleted the callback-trap-inner-exception branch November 2, 2022 21:25
@kpreisser kpreisser mentioned this pull request Nov 3, 2022
kpreisser added a commit to kpreisser/wasmtime-dotnet that referenced this pull request Nov 3, 2022
kpreisser added a commit to kpreisser/wasmtime-dotnet that referenced this pull request Nov 3, 2022
See bytecodealliance#173

Currently, tests that are still failing are the following:
- ExitTrapTests: It is not clear how to get the WASI exit code from a `wasmtime_error_t*` instead of an `wasmtime_trap_t*` via the C API.
- FuelConsumptionTests: `wasmtime_trap_code` unexpectedly fails (SEHException) even though a trap was returned.
- TrapTests: The tests introduced with bytecodealliance#172 fail as the inner exception would need to be set on the WasmtimeException instead of TrapException. Need to check how to solve this.
peterhuene added a commit that referenced this pull request Nov 10, 2022
* Update some tests for the new trap behavior.

See #173

Currently, tests that are still failing are the following:
- ExitTrapTests: It is not clear how to get the WASI exit code from a `wasmtime_error_t*` instead of an `wasmtime_trap_t*` via the C API.
- FuelConsumptionTests: `wasmtime_trap_code` unexpectedly fails (SEHException) even though a trap was returned.
- TrapTests: The tests introduced with #172 fail as the inner exception would need to be set on the WasmtimeException instead of TrapException. Need to check how to solve this.

* Update for the changes in bytecodealliance/wasmtime#5215.

This moves the ExitStatus from TrapException/TrapAccessor to WasmtimeException.

* Follow-Up: Add a Frames property to WasmtimeException which allows to retrieve the frames for a wasmtime_error_t* using the new wasmtime_error_wasm_trace function.

TODO: Add tests.

* Use nuint instead of UIntPtr for sizes (size_t) as they don't actually represent a pointer, but a platform-sized integer.

Note that nint/nuint in C# will behave like regular numeric types like int/uint, so e.g. the checked/unchecked context is respected, which is not the case with IntPtr/UIntPtr until .NET 7.

* Move the functionality of passing the original .NET exception as InnerException from the TrapException to the WasmtimeException, since host-created traps (with wasm_trap_new) are now returned as error (wasmtime_error_t*).

While theoretically it could happen that the returned wasmtime_error_t* has actually another cause than the error returned at the host-to-wasm transition, I assume this is not the case, similar as to a .NET exception that would just bubble up the stack once thrown.

* Fix the definition of wasmtime_trap_code to match the signature from trap.h header file.

See GHSA-h84q-m8rr-3v9q

Previously, we were passing a pointer to an Int32 (4-byte buffer), so the definition actually matched the Rust code, and therefore prior versions of wasmtime-dotnet are not impacted by that issue.

* Add TrapCode.OutOfFuel.

See bytecodealliance/wasmtime#5230

* Follow-Up: Update for the changed exception message.

* Update the consume-fuel example.

This commit updates the consume-fuel example to catch `WasmtimeException`
instead of `TrapException`.

The change is due to the `ConsumeFuel` call returning an error when it
fails to consume the given amount of fuel and that propagates properly now.

Co-authored-by: Peter Huene <[email protected]>
peterhuene pushed a commit that referenced this pull request Nov 11, 2022
…lback()` overloads for efficiently invoking callbacks (#163)

* Use a T4 text template to generate overloads of generic Linker.DefineFunction() methods that can efficiently call the specified callback without reflection.

* Simplify.

* Follow-Up: Also implement the generic overload generation for Function.FromCallback().

* Follow-Up: Returning a ValueTuple<> with more than 4 type arguments requires to use the overload taking a Delegate.

* Add more tests.

* Follow-Up: Fall back to using reflection instead of throwing an exception when the parameter/result type combination cannot be represented with the current generic parameters.

* Add more tests including one for a custom callback delegate.

* Directly specify the delegate parameter and return types, so that it's not necessary to use reflection to find the delegate's Invoke() method.

* Update for changes in #172.

* PR feedback: Extract duplicated code into a separate .t4 file that can be included in the .tt files.

* PR feedback: Use a bool value for hasCaller.

* PR feedback: Use a local tool manifest file for the dotnet-t4 tool, and invoke it automatically when building the project.

* Follow-Up: Run "dotnet tool restore" to automatically make the tool available.

* We no longer need to specify the -o flag since mono/t4#142 has been fixed in version 2.3.1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve debuggability by adding original host function exceptions to TrapException
2 participants