Skip to content

Trap Result #151

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

Closed
wants to merge 6 commits into from
Closed

Conversation

martindevans
Copy link
Contributor

Added in 4 new Result types (Result, ResultWithBacktrace, Result<T>, ResultWithBacktrace<T>) which provide a better method for trap handling than exceptions. This entire new system is opt-in, the function wrappers accept these types instead of the actual return type and then, if a trap happens, it is returned in this result struct instead of as an exception.

  • Result represents a function with no return value, which does not capture the trap backtrace.
  • ResultWithBacktrace represents a function with no return value, which captures the trap backtrace.
  • Result<T> represents a function which returns T, which does not capture the trap backtrace.
  • ResultWithBacktrace<T> represents a function which returns T, which captures the trap backtrace.

Also some associated to the TrapException:

  • Added a TrapCode Type property, which was an obvious omission from traps
  • Modified FunctionOffset and ModuleOffset of the TrapFrame to use nuint instead of int (which could cause an arithmetic overflow) (see Overflow Exception In Trap Handling #150)

…lt<T>`, `ResultWithBacktrace<T>`) which provide a better method for trap handling than exceptions. function wrappers accept these types instead of the actual return type and then, if a trap happens, it is returned in this result struct instead of as an exception.

 - `Result` represents a function with no return value, which does not capture the trap backtrace.
 - `ResultWithBacktrace` represents a function with no return value, which captures the trap backtrace.
 - `Result<T>` represents a function which returns `T`, which does not capture the trap backtrace.
 - `ResultWithBacktrace<T>` represents a function which returns `T`, which captures the trap backtrace.

Also some associated to the `TrapException`:
 - Added a `TrapCode Type` property, which was an obvious omission from traps
 - Modified `FunctionOffset` and `ModuleOffset` of the `TrapFrame` to use `nuint` instead of `int` (which could cause an arithmetic overflow)
@martindevans
Copy link
Contributor Author

I'm not sure what the macos failure is caused by, it looks like something wrong with the CI process itself?

@willhausman
Copy link

That error looks an awful lot like the crashes I would see when testing throwing exceptions in WasmerSharp and Wasmtime to compare. It happened locally on mac but not windows. Frustrating to discover. I only mention it to caution against assuming the CI itself is at fault.

@martindevans
Copy link
Contributor Author

Unfortunately I don't have access to a mac to test on. Any hints on what fixed the problem for you that I could try?

@willhausman
Copy link

I can try pulling your fork and see what happens for me later tonight or tomorrow. My solution was to stop using WasmerSharp. Useful, right? Ha. It just feels too much of a coincidence, given you are working with the traps, and I was testing how the two runtimes handled exceptions.

@willhausman
Copy link

I can confirm dotnet test Wasmtime.sln does exhibit this same behavior for me locally on both x64 and ARM Macs. Sorry I can't provide any more useful advice beyond my test results at the moment. I understand the traps at a conceptual level, but have not sifted through the implementation thoroughly enough to understand exactly what is happening.

@martindevans
Copy link
Contributor Author

Thanks for the testing, that at least confirms it isn't just a flakey CI problem.

@martindevans
Copy link
Contributor Author

If possible could you share the log from the test on your Mac? Hopefully that will contain more useful information than the CI log.

@martindevans
Copy link
Contributor Author

martindevans commented Sep 15, 2022

I have worked around the CI failure on MacOS by replacing the stackoverflow errors with integer div zero errors. I think the problem with MacOS+Stackoverflow is a bug in wasmtime-dotnet, independent of the changes in this PR.

I've pushed up #152 which includes a test that I expect to fail on MacOS to help identify the issue.

@peterhuene
Copy link
Member

Hi @martindevans! I'll review this today and dig into the stack overflow trap crashing the test host.

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.

This looks great and I love the opt-in nature of it!

Just a few comments; almost all of them are minor nits.

var result = run();

result.Type.Should().Be(ResultType.Trap);
result.Trap.Should().Be(TrapCode.IntegerDivisionByZero);
Copy link
Member

Choose a reason for hiding this comment

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

Should we be asserting here (and in ItReturnsATrapCodeGenericResult) that the frames are empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unless I'm misunderstanding what you're asking for I don't think that's possible in those two cases. They're both Result types (not ResultWithBacktrace) so the only thing available is the TrapCode, the backtrace is not available.

Copy link
Member

Choose a reason for hiding this comment

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

Oh right, the name Trap threw me off here. It should probably be, at the minimum, TrapCode so it doesn't seem like the whole trap exception is being stored.

Stepping back, though, should we not store a TrapException for the non-backtrace capturing Result types too, just without Frames populated?

I think it'd be useful to get the message in addition to the code, just without the backtrace.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point on the naming, I'll change that now 👍

Regarding the TrapException/message my intention was to have the Result be as lightweight as possible, avoiding allocating a TrapException and the message string. Personally I would prefer to keep it that way.

Maybe the problem here is naming - Result (the simpler name) is the opt-in to a lighter weight version with no useful debugging information. Perhaps instead they should be named Result (with a backtrace) and ResultWithoutAnyUsefulInformation (pending a better name). That way it's probably more obvious that the version with the backtrace is the one you "should" use?


Having said all that, I'm wondering if another possible way to do this is instead of having some special "blessed types" which can interact with traps we should instead detect if the return type implements a special interface and then do things through that. That way we can ship a single Result type with backtraces/messages etc, but there's a door to hook into the trap system (in a safe way of course) and get just what you need. If you don't mind holding off on this PR for a while I'll experiment with that approach to see if it's feasible.

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good, I'll hold off on the PR to see what you come up with.

I'm also good with landing this PR if that's what you decide is best.

@martindevans
Copy link
Contributor Author

I've pushed up a commit that should address all feedback, except that last point. Thanks for the excellent review, as usual 👍

@martindevans
Copy link
Contributor Author

I've pushed up #153 which further develops this idea with a system for custom result types. Although the details of how it works are more complex I think it offers a better user experience for the 99% of users who can just use ActionResult/FunctionResult<T> and don't need to think about if they want a backtrace etc. It also offers a bettter experience for the remaining 1% of users with weird requirements, since they can implement fully custom result types instead of having to pick between whatever options we happen to offer.

@peterhuene
Copy link
Member

Closing this as superseded with #153.

@peterhuene peterhuene closed this Sep 26, 2022
@martindevans martindevans deleted the trap_result branch November 30, 2022 14:30
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.

3 participants