-
Notifications
You must be signed in to change notification settings - Fork 3.4k
throw exception with message in cpp #6330
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
Comments
I don't think we have such a feature. In general I think people just add a try-catch at the highest level and print it from C++. If there's a way to add that feature it would be nice, but I'm not sure how easy it would be. |
@kripken so there is no way of propagating the exception to js? Is there any way in which it could be wrapped or something? |
I can't think of an easy way. But the thrown exception is a pointer, so in theory you could call from JS into C++ code like this, and give it that pointer:
I think that might work, but I'm not sure. |
ok, I didnt know that was an actual pointer! |
As mentioned in #6330. Something like this will now work: val::global("TypeError").new_("wrong type").throw_(); In the tests found in test_val.cpp it can be seen that an error can be thrown in CPP and successfully caught in JS.
As mentioned in emscripten-core#6330. Something like this will now work: val::global("TypeError").new_("wrong type").throw_(); In the tests found in test_val.cpp it can be seen that an error can be thrown in CPP and successfully caught in JS.
is it still open? i would like to know how to throw exception from c++ to js as well. |
Hi, this is the current solution I use: In C++: namespace foo {
std::string getExceptionMessage(int exceptionPtr) {
return std::string(reinterpret_cast<std::exception *>(exceptionPtr)->what());
}
}
EMSCRIPTEN_BINDINGS(fooBinding) {
emscripten::function("getExceptionMessage", &foo::getExceptionMessage);
}; In JS: try {
... // some code that calls webassembly
} catch (exception) {
console.error(Module.getExceptionMessage(exception));
} finally {
...
} I guess you can also throw an exception from an |
Any updates? |
@kripken, sure. I'm not well versed in the Emscripten project organization - where would you prefer something like this to be added? |
@Shachlan Good question, I'm not sure. Perhaps just adding an entry in the docs is enough, like in the debugging docs at |
Printing exception.what() seems to be covered in this answer without needing to enable exception catching inside C++ and without using embind: |
For the purposes of unit testing C++ compiled with emscripten and run under node.js, which does not have
I instantiate it as a C++ static variable inside the ctor body of another class I wrote for setting up and running tests, so that I believe the reason why is as follows. In
I am also able to catch that throw via Essentially, these code structures in Emscripten are preventing the global error handlers in node from ever firing. In my opinion, one way to solve it would be to actually register Emscripten's top level error handlers using the node.js |
This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 30 days. Feel free to re-open at any time if this issue is still relevant. |
The reason why this has been an issue is that emscripten does not always allow exceptions to reach the global error handlers of the environments in which it runs (specifically node.js), as mentioned in the last paragraph of my previous post (and cited within #11073). This is why it is so hard to get unit testing working with plain node.js and why people end up with so many workarounds (like #11073). Good job "stale bot" (sarcasm). |
@ChrisChiasson let me see if understand what you are doing and the behaviour you are asking for. Here is my understand if your setup.
Is that correct? I think we can figure out a way to make that work. |
If I remember correctly, these lines are key:
Yes, and if the tests are run in the browser and the |
Yes, a test suite would be its own executable, usually testing an entire class (see this self-test cpp file @ bottom of this gist for a non-emscripten version of what I normally use... The only difference with emscripten is that I would add a static member to the hpp to instantiate my Exception struct from this issue #6330 once, so no js files are needed). In the case of an uncaught (and therefore probably unexpected) exception during the test suite, a normal C++ program would just die and print the "what" of the exception... Although it is possible for some kind of non-standard exception to be thrown that may not even have a "what" (and actually, I think the base C++ exception class doesn't have it), so we need to allow developers to specify custom behavior. |
Regarding the It sounds like the problem is the difference between native and wasm here when an unhandled C++ exceptions reaches the outer scope: Native: Prints the If we could make emscripten print the what value in the same way that native does would that solve your issue? |
Speifically, emscripten intercepts the exception before it gets to the official error handler for node |
Okay I just tried and I see that it does preserve the stack at the original #include <emscripten.h>
#include <iostream>
EMSCRIPTEN_KEEPALIVE
int foo() {
try {
throw std::runtime_error("hello");
} catch (...) {
std::cout << "caught and rethrowing" << std::endl;
throw;
}
}
So this looks pretty promising, the only missing bits are 1) wrapping of each export at least for |
Right that makes sense. I was just checking that I was previously confused because we were loosing stacktraces, but it turns out we were holding it wrong here: #15042 |
Yeah it does. E.g. if I quickly try something like this:
You can see the message got updated but original stacktrace is preserved. |
I'm still not sure why we need to wrap every export site. We can make a library function, such as try {
some_wasm_function();
} catch (e) {
print_exception_info(e);
} Not sure why we do a similar thing for every single export entry by default. And about the stack traces, the Wasm EH does not mandate embedding stack traces when throwing exceptions, but all web VMs that currently implement the proposal (V8 and FF) do. The relevant explainer's paragraph is here. We didn't make it mandatory at the proposal level because it was considered the area of embedders and there were concerns that some non-web VMs that use exceptions to implement something lightweight may not want to embed them. Note that the embedded stack trace is not the same thing as the whole stack is preserved; we include the stack traces using string, and the stack has been already unwound at that point. |
Because it provides much better development experience - both during debugging and in production - than "exception thrown: 1234". Development experience is something that we are currently actively trying to improve in Emscripten, as bad DX usually discourages developers from using a toolchain in the first place. |
Also note that last two examples you posted are with The exception flow itself doesn't change for the consumer - from their standpoint, it's still an exception thrown from Wasm, and they can choose whether to print it, to ignore it, to report to a 3rd-party service like Sentry or something else. |
I don't think I was suggesting to print "exception thrown: 1234" instead of some helpful message? I was saying, without wrapping every export call site, I think we can give the JS caller the capability to print the helpful error message. I said two things: One is we can wrap
The function doesn't have to be |
Ah okay, and what I'm saying is that the best way to expose that message is via standard That is, I want those exceptions have The only way to achieve without significant perf overhead that that I could come up with is to wrap each Embind export + |
I see, and I think that makes sense. Is there a way to do that automatically in every export site? I'm not familiar with embind myself.. |
Yes, we have way in emscripten of adding wrappers around exports which we already do in certain circumstances. |
Here is where asyncify adds wrappers for all exports: emscripten/src/library_async.js Line 111 in 565fb36
|
I was thinking that we could limit it to Embind exports, since that would 1) limit wrappers to C++ code and not C where we know exceptions don't exist so we don't need to catch them and 2) limit it to explicitly Embind-exposed methods where users already expect value conversions and some associated overhead. Do you think we should wrap every exported function instead? |
If the wrapping is cheap I don't see why we wouldn't just wrap all explicitly exported functions. I don't see why embind should be special here. |
My motivation is mostly because currently it's the method for exposing C++ code, and C++ code is the only place where C++ exceptions can originate, so it feels like a natural boundary / annotation for the wrapping. I guess it's possible that some C++ code is exposed via C FFI, but in those scenarios exceptions are usually exposed via C style |
I guess that makes sense yes. I guess it would be easy to limit the wrapping to just embind exports. OTOH, if exceptions are enabled there is no reason they can't be thrown when normal (non-embind) exports are called directly and it might be confusing for users if uncaught exceptions only sometimes have useful messages on them. |
Right, that's what I meant by C FFI for C++ codebase. Or are you saying there are users who call C++ methods directly from JS via their mangled names with no binding layer in between, like |
@RReverser I'm not familiar with embind, but does that also take care of exceptions escaping The other thing is, if we use SjLj support using Wasm EH (#14976), longjmps will surface as exceptions as well. Longjmps don't have a helpful message, but it'd be good to just at last print "longjmp".. But I just checked in native Linux and unmatched longjmps surface as a segmentation fault there, so it's not great there either. |
Yeah no,
Ah that's an interesting point, makes sense. I'm not sure how to distinguish |
To be clear - I'm not opposed to wrapping all exports either, it just seemed wasteful to wrap C functions (including somewhat frequently called things like Plus, it's usually easier to start off with a limited surface and expand later as necessary. However, if there are cases where we want to wrap C functions, that makes sense too. |
No we give that a separate tag. So if we want to distinguish it it would be something like
|
I think it might be ok to wrap all exports. That's simpler, and it would help users that do not use embind. A common case might be a problem with no bindings, just exported main and callbacks to render a frame etc. |
Right, but if it's callbacks, those exceptions would propagate through |
After emscripten-core#17979, when `ASSERTIONS` is set, uncaught exceptions carry stack traces that are printed to the screen for debugging help. This adds an exception message to the exception objects in addition to that. The message it adds is produced by `__get_exception_message` function: https://github.com/emscripten-core/emscripten/blob/f6c46570e3780e52050bf822a07b342ec4bdddbe/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp#L75-L111 If an exception is a subclass of `std::exception`, it returns `std::exception::what()`. If not, it just prints its type. If an exception is uncaught and its type is a subclass of `std::exception`, now we print the `what()` message along with the stack trace when `ASSERTION` is set. In case our exception is `std::runtime_error` and the message is "my exception", when uncaught, this prints: ``` exiting due to exception: [object WebAssembly.Exception],Error: std::runtime_error,my exception at __cxa_throw (wasm://wasm/009a7c9a:wasm-function[1551]:0x24367) ... ``` Fixes emscripten-core#6330.
After #17979, when `ASSERTIONS` is set, uncaught exceptions carry stack traces that are printed to the screen for debugging help. This adds an exception message to the exception objects in addition to that. The message it adds is produced by `__get_exception_message` function: https://github.com/emscripten-core/emscripten/blob/f6c46570e3780e52050bf822a07b342ec4bdddbe/system/lib/libcxxabi/src/cxa_exception_emscripten.cpp#L75-L111 If an exception is a subclass of `std::exception`, it returns `std::exception::what()`. If not, it just prints its type. If an exception is uncaught and its type is a subclass of `std::exception`, now we print the `what()` message along with the stack trace when `ASSERTIONS` is set. In case our exception is `std::runtime_error` and the message is "my exception", when uncaught, this prints: ``` exiting due to exception: [object WebAssembly.Exception],Error: std::runtime_error,my exception at __cxa_throw (wasm://wasm/009a7c9a:wasm-function[1551]:0x24367) ... ``` Fixes #6330.
I have a cpp file test.cpp:
Compiled with
emcc --bind -fexceptions -std=c++11 test.cpp -o test.js
I would like the code to print out the exception message in some way so the user has a description of the error.
When I run it I get
exception thrown: 5246984
andUncaught 5246984
.It does not appear as though emscripten will print any distinguishable message.
Does emscripten have this feature?
If not, I was thinking of adding a
thow
method toval
that would accept a message and possibly a javascript exception class.The text was updated successfully, but these errors were encountered: