Skip to content

Commit a208c42

Browse files
committed
[EH] Make Wasm EH rethrow print stack traces
In Wasm EH, even if we set `-sASSERTION` or `-sEXCEPTION_STACK_TRACES`, if we rethrow an exception in the code, we lose the effect of that option because previously we called `__throw_exception_with_stack_trace` only in `__cxa_throw`: https://github.com/emscripten-core/emscripten/blob/9ce7020632aa6f7578c6e40e197b800a4dd7073f/system/lib/libcxxabi/src/cxa_exception.cpp#L294-L296 This adds the same mechanism to `__cxa_rethrow` (which is used for C++ `throw;`) and `__cxa_rethrow_primary_exception` (which is used for `std::rethrow_exception`). This does not solve the problem of losing stack traces _before_ the rethrowing. libc++abi's `__cxa_rethrow` and `__cxa_rethrow_primary_exception` are implemented as throwing a pointer in the same way we first throw it and they are not aware of any metadata. This happens even in the native platform with GDB; GDB's backtrace only shows stack traces after rethrowing. We may try to fix this later by passing `exnref` (WebAssembly/exception-handling#281) to the library, but this is likely to take some time. Partially fixes emscripten-core#20301.
1 parent 00e3406 commit a208c42

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

system/lib/libcxxabi/src/cxa_exception.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,14 @@ void __cxa_rethrow() {
644644
}
645645
#ifdef __USING_SJLJ_EXCEPTIONS__
646646
_Unwind_SjLj_RaiseException(&exception_header->unwindHeader);
647+
#elif __USING_WASM_EXCEPTIONS__
648+
#ifdef NDEBUG
649+
_Unwind_RaiseException(&exception_header->unwindHeader);
650+
#else
651+
// In debug mode, call a JS library function to use WebAssembly.Exception JS
652+
// API, which enables us to include stack traces
653+
__throw_exception_with_stack_trace(&exception_header->unwindHeader);
654+
#endif
647655
#else
648656
_Unwind_RaiseException(&exception_header->unwindHeader);
649657
#endif
@@ -769,6 +777,14 @@ __cxa_rethrow_primary_exception(void* thrown_object)
769777
dep_exception_header->unwindHeader.exception_cleanup = dependent_exception_cleanup;
770778
#ifdef __USING_SJLJ_EXCEPTIONS__
771779
_Unwind_SjLj_RaiseException(&dep_exception_header->unwindHeader);
780+
#elif __USING_WASM_EXCEPTIONS__
781+
#ifdef NDEBUG
782+
_Unwind_RaiseException(&exception_header->unwindHeader);
783+
#else
784+
// In debug mode, call a JS library function to use WebAssembly.Exception JS
785+
// API, which enables us to include stack traces
786+
__throw_exception_with_stack_trace(&exception_header->unwindHeader);
787+
#endif
772788
#else
773789
_Unwind_RaiseException(&dep_exception_header->unwindHeader);
774790
#endif

test/test_other.py

+58
Original file line numberDiff line numberDiff line change
@@ -8593,6 +8593,64 @@ def test_exceptions_stack_trace_and_message(self, wasm_eh):
85938593
for check in stack_trace_checks:
85948594
self.assertFalse(re.search(check, err), 'Expected regex "%s" to not match on:\n%s' % (check, err))
85958595

8596+
# Rethrowing exception currently loses the stack trace before the
8597+
# rethrowing due to how rethrowing is implemented. So in the examples below
8598+
# we don't print 'bar' at the moment.
8599+
# TODO Make rethrow preserve stack traces before rethrowing?
8600+
rethrow_src1 = r'''
8601+
#include <stdexcept>
8602+
8603+
void bar() {
8604+
throw std::runtime_error("my message");
8605+
}
8606+
void foo() {
8607+
try {
8608+
bar();
8609+
} catch (...) {
8610+
throw; // rethrowing by throw;
8611+
}
8612+
}
8613+
int main() {
8614+
foo();
8615+
return 0;
8616+
}
8617+
'''
8618+
rethrow_src2 = r'''
8619+
#include <stdexcept>
8620+
8621+
void bar() {
8622+
throw std::runtime_error("my message");
8623+
}
8624+
void foo() {
8625+
try {
8626+
bar();
8627+
} catch (...) {
8628+
auto e = std::current_exception();
8629+
std::rethrow_exception(e); // rethrowing by std::rethrow_exception
8630+
}
8631+
}
8632+
int main() {
8633+
foo();
8634+
return 0;
8635+
}
8636+
'''
8637+
rethrow_stack_trace_checks = [
8638+
'std::runtime_error[:,][ ]?my message', # 'std::runtime_error: my message' for Emscripten EH
8639+
'at ((src.wasm.)?_?__cxa_rethrow|___resumeException)', # '___resumeException' (JS symbol) for Emscripten EH
8640+
'at (src.wasm.)?foo',
8641+
'at (src.wasm.)?main']
8642+
8643+
self.set_setting('ASSERTIONS', 1)
8644+
self.clear_setting('EXCEPTION_STACK_TRACES')
8645+
err = self.do_run(rethrow_src1, emcc_args=emcc_args, assert_all=True,
8646+
assert_returncode=NON_ZERO,
8647+
expected_output=rethrow_stack_trace_checks, regex=True)
8648+
self.assertNotContained('bar', err)
8649+
err = self.do_run(rethrow_src2, emcc_args=emcc_args, assert_all=True,
8650+
assert_returncode=NON_ZERO,
8651+
expected_output=rethrow_stack_trace_checks, regex=True)
8652+
self.assertNotContained('bar', err)
8653+
85968654
@requires_node
85978655
def test_jsrun(self):
85988656
print(config.NODE_JS)

0 commit comments

Comments
 (0)