From ef9c5cec3e63b5797dc96cf329fee565b948df20 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 29 Sep 2022 15:09:31 -0700 Subject: [PATCH 1/5] [EH] Print message for uncaught exceptions 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 `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 #6330. --- emcc.py | 1 + src/library_exceptions.js | 3 ++- system/lib/libcxxabi/src/cxa_exception.cpp | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/emcc.py b/emcc.py index 9479dac09eec5..de4d1499701b8 100755 --- a/emcc.py +++ b/emcc.py @@ -2641,6 +2641,7 @@ def get_full_import_name(name): # using the JS API, which needs this C++ tag exported. if settings.ASSERTIONS and settings.WASM_EXCEPTIONS: settings.EXPORTED_FUNCTIONS += ['___cpp_exception'] + settings.EXPORT_EXCEPTION_HANDLING_HELPERS = True; # Make `getExceptionMessage` and other necessary functions available for use. if settings.EXPORT_EXCEPTION_HANDLING_HELPERS: diff --git a/src/library_exceptions.js b/src/library_exceptions.js index 511030bab2791..3a6184a16f651 100644 --- a/src/library_exceptions.js +++ b/src/library_exceptions.js @@ -431,7 +431,7 @@ var LibraryExceptions = { // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Exception // In release builds, this function is not needed and the native // _Unwind_RaiseException in libunwind is used instead. - __throw_exception_with_stack_trace__deps: ['$getCppExceptionTag'], + __throw_exception_with_stack_trace__deps: ['$getCppExceptionTag', '$getExceptionMessage'], __throw_exception_with_stack_trace: function(ex) { var e = new WebAssembly.Exception(getCppExceptionTag(), [ex], {traceStack: true}); // The generated stack trace will be in the form of: @@ -446,6 +446,7 @@ var LibraryExceptions = { var arr = e.stack.split('\n'); arr.splice(1,1); e.stack = arr.join('\n'); + e.message = getExceptionMessage(e); throw e; }, #endif diff --git a/system/lib/libcxxabi/src/cxa_exception.cpp b/system/lib/libcxxabi/src/cxa_exception.cpp index bbb6cd8ddbe4c..56e38c50d850c 100644 --- a/system/lib/libcxxabi/src/cxa_exception.cpp +++ b/system/lib/libcxxabi/src/cxa_exception.cpp @@ -256,7 +256,7 @@ exception. #if defined(__USING_WASM_EXCEPTIONS__) && !defined(NDEBUG) extern "C" { -void __throw_exception_with_stack_trace(_Unwind_Exception*, bool); +void __throw_exception_with_stack_trace(_Unwind_Exception*); } // extern "C" #endif @@ -293,7 +293,7 @@ __cxa_throw(void *thrown_object, std::type_info *tinfo, void (*dest)(void *)) { #else // In debug mode, call a JS library function to use WebAssembly.Exception JS // API, which enables us to include stack traces - __throw_exception_with_stack_trace(&exception_header->unwindHeader, true); + __throw_exception_with_stack_trace(&exception_header->unwindHeader); #endif #else _Unwind_RaiseException(&exception_header->unwindHeader); From 8e29c3bd5721927b2d80e893c70ba1f9b9921dea Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 6 Oct 2022 00:06:11 -0700 Subject: [PATCH 2/5] Changelog --- ChangeLog.md | 3 ++- src/library_exceptions.js | 2 +- test/test_other.py | 17 ++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index a80a6ec0ee331..f498c7bc77bcd 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -21,7 +21,8 @@ See docs/process.md for more on how version tagging works. 3.1.24 (in development) ----------------------- - In Wasm exception mode (`-fwasm-exceptions`), when `ASSERTIONS` is enabled, - uncaught exceptions will display stack traces. (#17979) + uncaught exceptions will display stack traces and what() message. (#17979 and + #18003) 3.1.23 - 09/23/22 ----------------- diff --git a/src/library_exceptions.js b/src/library_exceptions.js index 3a6184a16f651..1386c74b42abe 100644 --- a/src/library_exceptions.js +++ b/src/library_exceptions.js @@ -434,6 +434,7 @@ var LibraryExceptions = { __throw_exception_with_stack_trace__deps: ['$getCppExceptionTag', '$getExceptionMessage'], __throw_exception_with_stack_trace: function(ex) { var e = new WebAssembly.Exception(getCppExceptionTag(), [ex], {traceStack: true}); + e.message = getExceptionMessage(e); // The generated stack trace will be in the form of: // // Error @@ -446,7 +447,6 @@ var LibraryExceptions = { var arr = e.stack.split('\n'); arr.splice(1,1); e.stack = arr.join('\n'); - e.message = getExceptionMessage(e); throw e; }, #endif diff --git a/test/test_other.py b/test/test_other.py index b6a3391a82d9b..f53509a4523c0 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -7905,10 +7905,12 @@ def test_wasm_nope(self): self.assertContained('no native wasm support detected', out) @requires_v8 - def test_wasm_exceptions_stack_trace(self): + def test_wasm_exceptions_stack_trace_and_message(self): src = r''' + #include + void bar() { - throw 3; + throw std::runtime_error("my message"); } void foo() { bar(); @@ -7921,8 +7923,8 @@ def test_wasm_exceptions_stack_trace(self): emcc_args = ['-g', '-fwasm-exceptions'] self.v8_args.append('--experimental-wasm-eh') - # Stack trace example for this example code: - # exiting due to exception: [object WebAssembly.Exception],Error + # Stack trace and message example for this example code: + # exiting due to exception: [object WebAssembly.Exception],Error: std::runtime_error, my message # at __cxa_throw (wasm://wasm/009a7c9a:wasm-function[1551]:0x24367) # at bar() (wasm://wasm/009a7c9a:wasm-function[12]:0xf53) # at foo() (wasm://wasm/009a7c9a:wasm-function[19]:0x154e) @@ -7932,7 +7934,12 @@ def test_wasm_exceptions_stack_trace(self): # at callMain (test.js:4567:15) # at doRun (test.js:4621:23) # at run (test.js:4636:5) - stack_trace_checks = ['at __cxa_throw', 'at bar', 'at foo', 'at main'] + stack_trace_checks = [ + 'std::runtime_error,my message', + 'at __cxa_throw', + 'at bar', + 'at foo', + 'at main'] # We attach stack traces to exception objects only when ASSERTIONS is set self.set_setting('ASSERTIONS') From 24b556612bc791853c84a799221a4ec24f30e373 Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 6 Oct 2022 00:15:38 -0700 Subject: [PATCH 3/5] flake8 --- emcc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emcc.py b/emcc.py index de4d1499701b8..802763903f13a 100755 --- a/emcc.py +++ b/emcc.py @@ -2641,7 +2641,7 @@ def get_full_import_name(name): # using the JS API, which needs this C++ tag exported. if settings.ASSERTIONS and settings.WASM_EXCEPTIONS: settings.EXPORTED_FUNCTIONS += ['___cpp_exception'] - settings.EXPORT_EXCEPTION_HANDLING_HELPERS = True; + settings.EXPORT_EXCEPTION_HANDLING_HELPERS = True # Make `getExceptionMessage` and other necessary functions available for use. if settings.EXPORT_EXCEPTION_HANDLING_HELPERS: From ecdef0b76ade38c47566825f22b2c2f565d2bdeb Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 6 Oct 2022 14:22:30 -0700 Subject: [PATCH 4/5] Add _free to EXPORTED_FUNCTION, which was missing all along --- emcc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emcc.py b/emcc.py index 802763903f13a..f1180f5352cbf 100755 --- a/emcc.py +++ b/emcc.py @@ -2651,7 +2651,7 @@ def get_full_import_name(name): # What you need to do is different depending on the kind of EH you use # (https://github.com/emscripten-core/emscripten/issues/17115). settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getExceptionMessage', '$incrementExceptionRefcount', '$decrementExceptionRefcount'] - settings.EXPORTED_FUNCTIONS += ['getExceptionMessage', '___get_exception_message'] + settings.EXPORTED_FUNCTIONS += ['getExceptionMessage', '___get_exception_message', '_free'] if settings.WASM_EXCEPTIONS: settings.EXPORTED_FUNCTIONS += ['___cpp_exception', '___cxa_increment_exception_refcount', '___cxa_decrement_exception_refcount', '___thrown_object_from_unwind_exception'] From e825db28959fd6524daccbf4af365f4b2d16769a Mon Sep 17 00:00:00 2001 From: Heejin Ahn Date: Thu, 6 Oct 2022 18:22:06 -0700 Subject: [PATCH 5/5] Wasm64 fix --- src/library_exceptions.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/library_exceptions.js b/src/library_exceptions.js index 1386c74b42abe..3cca7181e6b83 100644 --- a/src/library_exceptions.js +++ b/src/library_exceptions.js @@ -396,11 +396,11 @@ var LibraryExceptions = { $getExceptionMessageCommon__deps: ['__get_exception_message', 'free', '$withStackSave'], $getExceptionMessageCommon: function(ptr) { return withStackSave(function() { - var type_addr_addr = stackAlloc(4); - var message_addr_addr = stackAlloc(4); - ___get_exception_message(ptr, type_addr_addr, message_addr_addr); - var type_addr = HEAP32[type_addr_addr >> 2]; - var message_addr = HEAP32[message_addr_addr >> 2]; + var type_addr_addr = stackAlloc({{{ POINTER_SIZE }}}); + var message_addr_addr = stackAlloc({{{ POINTER_SIZE }}}); + ___get_exception_message({{{ to64('ptr') }}}, {{{ to64('type_addr_addr') }}}, {{{ to64('message_addr_addr') }}}); + var type_addr = {{{ makeGetValue('type_addr_addr', 0, '*') }}}; + var message_addr = {{{ makeGetValue('message_addr_addr', 0, '*') }}}; var type = UTF8ToString(type_addr); _free(type_addr); var message;