Closed robfors closed 2 years ago
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:
// gets an exception object, and prints it out.
void print_exception(void* exception) {
try {
throw exception;
} catch (...) {
std::cout << exception.what() << '\n';
}
}
I think that might work, but I'm not sure.
ok, I didnt know that was an actual pointer! I will play a bit with the idea and write back, thanks!
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 EM_ASM
block, if you wanted to rethrow c++ to JS.
Any updates?
I think adding an option like @Shachlan 's from the previous comment might be a good idea, if @Shachlan or someone else wants to submit a PR.
@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 ./site/source/docs/porting/Debugging.rst
?
Printing exception.what() seems to be covered in this answer without needing to enable exception catching inside C++ and without using embind: https://stackoverflow.com/a/33743704/581848 This post explains why exception catching at the C++ level kills performance under wasm: https://brionv.com/log/2019/10/24/exception-handling-in-emscripten-how-it-works-and-why-its-disabled-by-default/
For the purposes of unit testing C++ compiled with emscripten and run under node.js, which does not have window.onerror
like the browser solutions above, I was attempting to use something like this:
struct Exception{
static std::string what(intptr_t p){
return reinterpret_cast<std::runtime_error*>(p)->what();
}
/**ctor*/
Exception(){
#ifdef __EMSCRIPTEN__
EM_ASM(({
console.log("This gets called and gives false:",
process.hasUncaughtExceptionCaptureCallback()
);
//window.addEventListener('error',
//process['on']('unhandledRejection',
//process['on']('uncaughtExceptionMonitor',
process['on']('uncaughtException',
()=>{console.log("This never gets called.");
console.error(Module.Exception.what(e.error));
});
}),0);
#endif
};
};
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_BINDINGS(CRANE){
emscripten::class_<Exception>("Exception")
.class_function("what",&Exception::what);
}
#endif
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 Exception()
only gets called once, and thus only after main
. However, in node.s, the console.log
inside uncaughtException
will never get printed. I always get something like what the original poster received: exception thrown: 5246984
I believe the reason why is as follows. In parseTools.js
, the function makeThrow(what)
returns a string 'throw' + what + ';'
, where what
seems to come from one of three spots in library_exceptions.js
and is the address of the exception (i.e. the value of the pointer). So, I created a simple one-line node.js script where I threw a number to replicate this behavior, and its output was completely different:
C:\msys64\home\chris.chiasson\wCRANE>node tmp/tmp.js
C:\msys64\home\chris.chiasson\wCRANE\tmp\tmp.js:1
throw 593485;
^
593485
I am also able to catch that throw via process.on('uncaughtException',...)
. So, knowing the weird behavior was not coming from node itself, I grepped all of the emsdk folder for exception thrown
, and three spots stood out, along with two commits.
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 process.on
mechanism (or window.addEventListener
in-brower). That way, Emscripten's handlers would still fire, but we would also be free to add our own straight from within our c++ unit tests and makefiles without any other special steps or source files required (other than prefixing Emscripten wasm/javascript unit tests with the name of the node executable, whereas in straight C++, we'd call the executable directly). This would also prevent the added complexity of integrating emrun and the browser into standard unit tests. What do y'all think?
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.
exception thrown: 5246984
.Is that correct?
I think we can figure out a way to make that work.
If I remember correctly, these lines are key:
//window.addEventListener('error',
//process['on']('unhandledRejection',
//process['on']('uncaughtExceptionMonitor',
process['on']('uncaughtException',
Yes, and if the tests are run in the browser and the window.addEventListener
line is uncommented (and the process[on]
lines are commented), then the rest of the inline javascript in the EM_ASM block will print the what
of the exception. However, for the equivalent facility under Node.js, emscripten seems to intercept the exception before it ever gets to the javscript engine. My post was an attempt to explain that if the current code would just stop intercepting everything before the top level handler (such as uncaughException
) got ahold of it, then emscripten would still be able to handle those events normally... because it could leverage uncaughtException
to properly register its own handler via process['on',...], which would have the side effect of allowing developers to register their handlers normally via process['on',...]... as I was trying to do here.
- You are running tests where each test is its own executable (rather than a single executable which runs a sequence of unit tests)
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 what
method: It does look like all C++ exceptions have that what method: https://www.cplusplus.com/reference/exception/exception/what/.
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 what
of the exception.
Emscripten: Prints the pointer value of the exception.
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 process['on']('uncaughtException',...)
. It would make more sense if emscripten would hook into the node error handler, so that devs could also hook into the same handler (similar to how in browsers devs are able to hook into window.onerror
/ window.addEventListener("error",...)
). For the tests I was working on, sure, printing the "what" would have been ok, but other devs will probably still need access to the exception itself in order to handle it appropriately, and may not want the "what" printed at all. On the std::exception
base class, you are right that the "what" is present (on gcc for the base class it "helpfully" just prints "std::exception", which I only half remembered).
Would it not be OK if libc++abi did the printing, like it does for native executables, before the process exits?
Then no handler at all would be needed I think?
I agree it should be possible to handle unhandled C++ exceptions in JS too.. I just wonder if it would be simpler/better/more consistent for your use case if libc++abi printing happened during shutdown automatically?
That would be ok for my test suite (it is the behavior we rely on for normal C++, so by analogy it should work here provided it only triggers when the program is shutting down due to unhandled exception ... in this case not handled in either JS or C++)
The problem is that in C++ the code can throw anything - primitives ot any user defined objects. Throwing isn't limited to std::exception
and objects that inherit from it. So whatever the handler will be, it will need to either handle anything which is thrown, or be in c++ and use syntax that distinguishes between thrown objects:
catch(const std::exception &exc) {
//handle exception
}
catch(...) {
//handle other objects
}
I don't see why we can't do exactly as the same thing that native systems which is basically to do their best to print whatever exception makes it to the outer level.
Native clang and gcc also seem to have limits on what they display when certain things are thrown... for example if you throw and integer the value is not actually printed:
$ cat test.cc
int main() {
throw 42;
}$ cat test2.cc
#include <exception>
class myexception: public std::exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
int main () {
throw myex;
}
sbc.sbc1 (~/dev/wasm/emscripten) (split_out_wget) $
$ clang++ test.cc
$ ./a.out
terminate called after throwing an instance of 'int'
Aborted
$ clang++ --stdlib=libc++ test.cc
$ ./a.out
libc++abi: terminating with uncaught exception of type int
Aborted
Here we can see the C++ stdlib doesn't know how to describe the exception because it doesn't have what
method, but if we use a subclass of exception
we get more info:
$ cat test2.cc
#include <exception>
class myexception: public std::exception
{
virtual const char* what() const throw()
{
return "My exception happened";
}
} myex;
int main () {
throw myex;
}
$ clang++ --stdlib=libc++ test2.cc
$ ./a.out
libc++abi: terminating with uncaught exception of type myexception: My exception happened
Aborted
I see know reason why emscripten can't behave in the same manor as the native system and print what it can. Am i missing something?
If that's feasible, that would be great IMO.
BTW be careful of cplusplus.com. I think it is showing the old signature for "what". I usually use cppreference.com. Also if you need someone to help test for this I volunteer.
If we could make emscripten print the what value in the same way that native does would that solve your issue?
This would definitely be a better experience than today. I've briefly looked into this but couldn't figure out how to do this as I'm unfamiliar with Emscripten's exception handling code, but if you know how to implement it, that would be great.
You can use EM_ASM
:
#include <stdexcept>
#include <emscripten/emscripten.h>
void mytest()
{
try
{
throw std::runtime_error("ERROR");
}
catch (const std::exception& e)
{
EM_ASM({
throw UTF8ToString($0);
}, e.what());
}
}
@amir-saniyan It's not the same / not what the issue is about. While it works as a workaround, 1) you'd have to add such wrapper to every single exported method to catch its exceptions and rethrow them and 2) you're losing the original C++ stacktrace.
The point here is to print meaningful details for a real C++ exception.
@aheejin Do you think this is something that would be doable (easier to implement) in the new native Wasm exceptions support?
Not sure. Maybe we can come up with a way, but it is likely not the same as what native platforms do.
In the native platform, the message is printed here: https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libcxxabi/src/cxa_default_handlers.cpp#L62-L72
The call stack we get there is:
__cxa_throw
-> failed_throw
https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libcxxabi/src/cxa_exception.cpp#L288
-> std::__terminate
https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libcxxabi/src/cxa_exception.cpp#L152
-> demangling_terminate_handler
https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libcxxabi/src/cxa_handlers.cpp#L59
As we can see from where __cxa_throw
calls failed_throw
, this happens after _Unwind_RaiseException
returns. This means _Unwind_RaiseException
needs to return.
https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libcxxabi/src/cxa_exception.cpp#L281-L288
_Unwind_RaiseException
is in libunwind and the naive code is like this: https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libunwind/src/UnwindLevel1.c#L348-L369
It searches the stack in unwind_phase1
and does the actual unwinding unwind_phase2
, but this two-phase thing is not the point here; the point is it does the unwinding within libunwind, so when it finds there's no matching catch handler, libunwind knows there's no handler so it can return.
But currently Wasm's _Unwind_RaiseException
is like this:
https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libunwind/src/Unwind-wasm.c#L49-L55
__builtin_wasm_throw
is a Clang builtin that will be Wasm's throw
instruction. So we basically just hand over the control to the VM, and we don't have a way to know or intercept when the VM knows there is no catching handler. There's no way to call our terminate handler when VM is about to crash.
What I've said was about Wasm EH, but I'm also not sure about place we can do this for Emscripten EH. Any ideas (for either mechanism)?
and we don't have a way to know or intercept when the VM knows there is no catching handler
Hm maybe it's something worth adding to Wasm exception proposal (detect if there is no catching handler)? I assume it will be a common usecase to present exception to JS in human-friendly manner.
Meanwhile, I guess Emscripten could add extra wrappers only at known boundaries - around functions explicitly exported to JavaScript via Embind or EMSCRIPTEN_KEEP_ALIVE - and wrap them like in https://github.com/emscripten-core/emscripten/issues/6330#issuecomment-917017835. Given that Wasm EH is much lower overhead in terms of code size than old approach, I guess wrapping each export would be a pretty trivial addition and would allow to convert any uncaught C++ exceptions to JS ones at the export boundary.
I imagine it would be up to something like emscripten, or the toolchain to add a top level catch handler which can then call std::terminate. Is not not a reasonable approach? (this could even be done via the some global uncaught exception mechanism on some platforms to avoid wrapping all entry points).
this could even be done via the some global uncaught exception mechanism on some platforms
I guess this could work for CLI usage of Emscripten's output, but not so much for its usage on the Web where normally you'd want to be able to catch an exception and hanlde it somehow on the JS side.
But yeah, I guess doing this on toolchain side makes sense too. I was just hoping that Wasm EH would simplify this by allowing C++ to throw exceptions with message that is "understood" by JavaScript side as if it was a regular JS error, without instrumentation of export boundaries.
IIUC the way Wasm EH works is that it allows different languages to define different exception "tag"s but that are basically opaque to the embedder / JS, so you will always need some kind of runtime support to turn them into something that JS or end user can understand. i.e. you would need libcxxabi support code to turn an uncaught exception into a string or anything readable like that.
@RReverser
Meanwhile, I guess Emscripten could add extra wrappers only at known boundaries - around functions explicitly exported to JavaScript via Embind or EMSCRIPTEN_KEEP_ALIVE - and wrap them like in #6330 (comment).
I think that would require
In the old JS exception mechanism, a thrown number is assumed to be a C++ exception, and the number is a pointer to the exception object. In wasm exceptions, it's an object from the JS point of view, I assume? Is there a JS API for those objects - to check if an object is a wasm exception, and to get one of the fields on it? If not, we'd need to call back into wasm, but does wasm have instructions to do those things (get an externref and check or cast if it is an exception)?
it's an object from the JS point of view, I assume
I honestly don't know. I guess those are questions rather to @aheejin - I thought it was possible to distinguish between C++ exceptions and any other.
Or maybe Embind (or any other export mechanism) can literally do something like
try {
return actual_wrapped_func();
} catch (std::exception &e) {
rethrow_as_js(e.what());
}
and wrap each export with this on C++ side?
I think what @sbc100 said makes sense. Maybe when Wasm EH is enabled, we can wrap the main function with a try-catch, like
try
main's code
catch __cpp_exception
call $print_exception_info
rethrow
end
And create print_exception_info
in libc++abi, which does something like https://github.com/emscripten-core/emscripten/blob/73c20f60cfa046e156a2c3970f7967b00e6662e7/system/lib/libcxxabi/src/cxa_default_handlers.cpp#L62-L72
This can be done by the toolchain only. @sbc100, is this what you meant? I'm not sure what you mean by
this could even be done via the some global uncaught exception mechanism on some platforms to avoid wrapping all entry points)
@RReverser, I'm not sure if we can wrap every exported function from the JS side, because some of them may not want to print this error message, and if JS calls an exported function that throws, that JS code may have another way of handling that exception. But if that function is main
it is pretty clear that an uncaught exception escaping main
is something that crashes the program and worth reporting about the details, I guess..? Please let me know if I didn't understand what you meant correctly.
and if JS calls an exported function that throws, that JS code may have another way of handling that exception
That's precisely the problem - right now calling JS can't handle that exception nicely because there is no way to extract original C++ exception's details from JS side.
So what I'm suggesting is, at each export boundary, to catch a C++ exception, extract its .what()
like C++ runtime does (if it's a std::exception
), but, instead of aborting, rethrow it as a JS exception by doing throw new Error(...original message...)
from JS side.
This way caller will still get an exception it can try-catch from JS, but this time it will be populated with correct C++ message and not just a raw number representing the pointer like it is today.
If performance is a concern, another alternative could be to subclass Error
to some Emscripten-specific CppError
where message
is only retrieved from C++ lazily on first access, although then we need to expose some API from C++ runtime to the glue JS that would check if an error is indeed a C++ std::exception
and return .what()
on-demand.
I think that all makes sense to me. I think we should do both of those things:
(1) can be used to implement (2).
Do we need to worry about re-throw in JS losing the original stack trace?
Do we need to worry about re-throw in JS losing the original stack trace?
It would be nice to preserve it, but that means collecting stacktrace (constructing JS error) at the C++ exception creation site, which can be costly (again, something I thought maybe Wasm EH could solve?). I think we could do this in debug builds at least.
An option (perhaps enable in debug builds by default) that wraps all exports and does this automatically.
As for this, I strongly think that when exceptions are enabled at all, we should rethrow JS-friendly exception in all builds by default. It doesn't add that much overhead, and there isn't a scenario where an exception with raw pointer as a message is more useful than an actual message from C++.
Also, the fewer options to worry about, the better.
I'm a bit rusty, but AFAIK std::exception
contains a stack trace by default, and there's no standard way of adding a stack trace to an exception, or a standard field/getter for a stack trace, so trying to add the stack trace from a C++ exception won't be possible, unless WASM EH will add the stack trace in a way that will be compliant both with C++ and JS.
I believe that with wasm EH proposal the stack trace is automatically tracked (just like it is with JS exceptions). I was just wondering if that information is lost when if we rethrow the exceptions in JS? How does JS handle the stack trace when re-throwing its own exceptions?
Another way of putting it: How seamless can you match the "catch + rethrown" mechansim in JS.. I have some memory of it looking the original stack in this case.
I believe that with wasm EH proposal the stack trace is automatically tracked
Hmm, that's actually exactly what I'm trying to understand. When you say it's "automatically tracked" do you mean it's captured as part of C++ object? Or is the exception represented as anyref?
If it's the last one and the exception already contains the correct stacktrace, then things are even simpler - we can unconditionally (regardless of settings) do
catch (err) {
err.message = ...;
throw err;
}
from JS side at such boundaries, and then it would be completely seamless - the original stacktrace would be preserved, and only the message would be updated to human-readable variant.
Okay I just tried and I see that it does preserve the stack at the original throw
point in C++:
#include <emscripten.h>
#include <iostream>
EMSCRIPTEN_KEEPALIVE
int foo() {
try {
throw std::runtime_error("hello");
} catch (...) {
std::cout << "caught and rethrowing" << std::endl;
throw;
}
}
> Module.__Z3foov()
temp:1237 caught and rethrowing
Uncaught WebAssembly.Exception: wasm exception
at _Unwind_RaiseException (http://localhost:5000/temp.wasm:wasm-function[1585]:0x21fd5)
at __cxa_rethrow (http://localhost:5000/temp.wasm:wasm-function[1583]:0x21f88)
at foo() (http://localhost:5000/temp.wasm:wasm-function[13]:0xef2)
at Object.__Z3foov (http://localhost:5000/temp.js:1519:22)
at <anonymous>:1:8
So this looks pretty promising, the only missing bits are 1) wrapping of each export at least for main
and Embind and 2) actually propagating the .what()
and setting the .message
.
Right that makes sense. I was just checking that throw err
does indeed preserve the stack trace.
I was previously confused because we were loosing stacktraces, but it turns out we were holding it wrong here: https://github.com/emscripten-core/emscripten/pull/15042
Yeah it does. E.g. if I quickly try something like this:
> try { Module.__Z3foov() } catch (e) { e.message = 'custom updated message'; throw e }
caught and rethrowing
VM719:1 Uncaught WebAssembly.Exception: custom updated message
at _Unwind_RaiseException (http://localhost:5000/temp.wasm:wasm-function[1585]:0x21fd5)
at __cxa_rethrow (http://localhost:5000/temp.wasm:wasm-function[1583]:0x21f88)
at foo() (http://localhost:5000/temp.wasm:wasm-function[13]:0xef2)
at Object.__Z3foov (http://localhost:5000/temp.js:1519:22)
at <anonymous>:1:14
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 $print_exception_info
as I suggested in https://github.com/emscripten-core/emscripten/issues/6330#issuecomment-918808611 and add it in libc++abi to be called form JS, and if the calling JS function wants to print some messages, can't it just call the function, like
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.
Not sure why we do a similar thing for every single export entry by default.
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.
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.