emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.83k stars 3.31k forks source link

throw exception with message in cpp #6330

Closed robfors closed 2 years ago

robfors commented 6 years ago

I have a cpp file test.cpp:

#include <stdio.h>
#include <iostream>
#include <emscripten/bind.h>
#include <emscripten/val.h>

int main()
{
  throw std::invalid_argument("test error");

  printf("\ndone\n");
  return 0;
}

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 and Uncaught 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 to val that would accept a message and possibly a javascript exception class.

kripken commented 6 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.

jleni commented 6 years ago

@kripken so there is no way of propagating the exception to js? Is there any way in which it could be wrapped or something?

kripken commented 6 years ago

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.

jleni commented 6 years ago

ok, I didnt know that was an actual pointer! I will play a bit with the idea and write back, thanks!

tuananh commented 5 years ago

is it still open? i would like to know how to throw exception from c++ to js as well.

Shachlan commented 4 years ago

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.

artemjackson commented 4 years ago

Any updates?

kripken commented 4 years ago

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.

Shachlan commented 4 years ago

@kripken, sure. I'm not well versed in the Emscripten project organization - where would you prefer something like this to be added?

kripken commented 4 years ago

@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?

Shachlan commented 4 years ago

https://github.com/emscripten-core/emscripten/pull/11073

ChrisChiasson commented 4 years ago

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/

ChrisChiasson commented 4 years ago

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?

stale[bot] commented 3 years ago

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.

ChrisChiasson commented 3 years ago

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).

sbc100 commented 3 years ago

@ChrisChiasson let me see if understand what you are doing and the behaviour you are asking for.

Here is my understand if your setup.

  1. You are running tests where each test is its own executable (rather than a single executable which runs a sequence of unit tests)
  2. Each test executable can fail by thrown an uncaught C++ exception.
  3. Under emscripten + node.js today when this happens the process will (correctly) exit with a non-zero status (so the failure os registered) but you would a better error message than the default one which is exception thrown: 5246984.

Is that correct?

I think we can figure out a way to make that work.

ChrisChiasson commented 3 years ago

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.

ChrisChiasson commented 3 years ago
  1. 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.

sbc100 commented 3 years ago

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?

ChrisChiasson commented 3 years ago

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).

sbc100 commented 3 years ago

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?

sbc100 commented 3 years ago

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?

ChrisChiasson commented 3 years ago

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++)

Shachlan commented 3 years ago

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
}
sbc100 commented 3 years ago

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?

Shachlan commented 3 years ago

If that's feasible, that would be great IMO.

ChrisChiasson commented 3 years ago

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.

RReverser commented 3 years ago

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.

amir-saniyan commented 3 years ago

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());
    }
}
RReverser commented 3 years ago

@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.

RReverser commented 3 years ago

@aheejin Do you think this is something that would be doable (easier to implement) in the new native Wasm exceptions support?

aheejin commented 3 years ago

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)?

RReverser commented 3 years ago

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.

sbc100 commented 3 years ago

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).

RReverser commented 3 years ago

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.

sbc100 commented 3 years ago

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.

kripken commented 3 years ago

@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)?

RReverser commented 3 years ago

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?

aheejin commented 3 years ago

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.

RReverser commented 3 years ago

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.

sbc100 commented 3 years ago

I think that all makes sense to me. I think we should do both of those things:

  1. Expose an api for taking raw wasm exception and using C++ code to make it more JS-friendly.
  2. An option (perhaps enable in debug builds by default) that wraps all exports and does this automatically.

(1) can be used to implement (2).

Do we need to worry about re-throw in JS losing the original stack trace?

RReverser commented 3 years ago

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.

Shachlan commented 3 years ago

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.

sbc100 commented 3 years ago

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.

RReverser commented 3 years ago

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.

RReverser commented 3 years ago

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.

sbc100 commented 3 years ago

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

RReverser commented 3 years ago

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.

aheejin commented 3 years ago

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.

RReverser commented 3 years ago

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.