Skip to content
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

Add helper function for C++ exception formatting #16343

Merged
merged 30 commits into from
Feb 24, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3cf21a0
Wrote format_exception function and hard coded inclusion into system_…
hoodmane Feb 18, 2022
2ea06c8
Add Javascript wrapper, miscellaneous cleanup
hoodmane Feb 19, 2022
693f677
Fix formatting
hoodmane Feb 19, 2022
95ef456
No need for exportedAsmFunction
hoodmane Feb 20, 2022
103dca8
Use withStackSave
hoodmane Feb 20, 2022
cc31016
Free the right thing
hoodmane Feb 20, 2022
5ca4f18
Use stackAlloc({{{ POINTER_SIZE }}});
hoodmane Feb 20, 2022
aa15cc8
Remove accidental change
hoodmane Feb 20, 2022
493e22f
Add final newline to format_exception.cpp
hoodmane Feb 20, 2022
c7d1267
Add emscripten_ prefix to format_exception
hoodmane Feb 20, 2022
0231037
Add format_exception.cpp to libcxxabi
hoodmane Feb 20, 2022
af456b1
Use withStackSave correctly
hoodmane Feb 20, 2022
e233943
Remove libformatexception library
hoodmane Feb 20, 2022
83a1a4a
Various fixes
hoodmane Feb 20, 2022
5e765ab
Add test
hoodmane Feb 20, 2022
1235600
Remove addresses from test comparison
hoodmane Feb 20, 2022
a83007e
Try to fix test
hoodmane Feb 21, 2022
5f19ef5
Fix wasm-exceptions test
hoodmane Feb 21, 2022
86b703b
Fix test
hoodmane Feb 21, 2022
a773e9b
Fix INCLUDE_FULL_LIBRARY test
hoodmane Feb 21, 2022
16f160d
Fix flake8
hoodmane Feb 21, 2022
fbcf6da
Formatting fixes from sbc100
hoodmane Feb 22, 2022
8ad7932
Fix test
hoodmane Feb 22, 2022
68c129e
Return char* instead of returning by value to avoid stackAlloc
hoodmane Feb 22, 2022
c11d036
Remove FORMAT_EXCEPTION_SUPPORT setting
hoodmane Feb 22, 2022
9c0b0a6
Address more review comments
hoodmane Feb 22, 2022
439c09f
Address more review comments
hoodmane Feb 22, 2022
77376ea
Remove unneeded setting
hoodmane Feb 22, 2022
8dad972
Add formatException to EXPORTED_FUNCTIONS rather than EXPORTED_RUNTIM…
hoodmane Feb 22, 2022
dcc15f5
Edits from code review
hoodmane Feb 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2074,6 +2074,15 @@ def default_setting(name, new_default):
else:
settings.JS_LIBRARIES.append((0, 'library_pthread_stub.js'))

if (
settings.FORMAT_EXCEPTION_SUPPORT or
"$formatException" in settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE or
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
settings.INCLUDE_FULL_LIBRARY
):
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ["$formatException"]
settings.EXPORTED_FUNCTIONS += ["_emscripten_format_exception", "_free"]
settings.EXPORTED_RUNTIME_METHODS += ["formatException"]

if settings.FORCE_FILESYSTEM and not settings.MINIMAL_RUNTIME:
# when the filesystem is forced, we export by default methods that filesystem usage
# may need, including filesystem usage from standalone file packager output (i.e.
Expand Down
14 changes: 14 additions & 0 deletions src/library_exceptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,20 @@ var LibraryExceptions = {
catchInfo.free();
{{{ makeThrow('ptr') }}}
},

#if !DISABLE_EXCEPTION_CATCHING
$formatException__deps: ["emscripten_format_exception", "$withStackSave", "free"],
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
$formatException: function(excPtr){
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
return withStackSave(function(){
var result_ptr = stackAlloc({{{ POINTER_SIZE }}});
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
_emscripten_format_exception(result_ptr, excPtr);
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
var utf8_addr = {{{ makeGetValue('result_ptr', '0', '*') }}};
var result = UTF8ToString(utf8_addr);
_free(utf8_addr);
return result;
});
},
#endif
};

// In LLVM, exceptions generate a set of functions of form __cxa_find_matching_catch_1(), __cxa_find_matching_catch_2(), etc.
Expand Down
7 changes: 7 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,13 @@ var EXCEPTION_DEBUG = 0;
// [link]
var DEMANGLE_SUPPORT = 0;

// If 1, expose a formatException function that can be used to format a top
// level C++ exception. If the thrown exception was
// throw new std::runtime_error("a big problem occurred")
// It returns a string like: "Cpp Exception std::runtime_error: A big problem occured."
// [link]
var FORMAT_EXCEPTION_SUPPORT = 0;
hoodmane marked this conversation as resolved.
Show resolved Hide resolved

// Print out when we enter a library call (library*.js). You can also unset
// Runtime.debug at runtime for logging to cease, and can set it when you want
// it back. A simple way to set it in C++ is
Expand Down
47 changes: 47 additions & 0 deletions system/lib/libcxxabi/src/format_exception.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "cxa_exception.h"
#include "cxxabi.h"
#include <stdio.h>
#include <typeinfo>

#ifdef __USING_EMSCRIPTEN_EXCEPTIONS__

extern "C" {
#define DEMANGLED_BUF_SIZE 100
hoodmane marked this conversation as resolved.
Show resolved Hide resolved

int __cxa_can_catch(const std::type_info* catchType,
const std::type_info* excpType,
void** thrown);

int emscripten_format_exception(char** result, void* exc_ptr) {
__cxxabiv1::__cxa_exception* exc_info =
(__cxxabiv1::__cxa_exception*)exc_ptr - 1;
std::type_info* exc_type = exc_info->exceptionType;
const char* exc_name = exc_type->name();

int status = 0;
char* demangled_buf = __cxxabiv1::__cxa_demangle(exc_name, 0, 0, &status);
if (status == 0 && demangled_buf) {
exc_name = demangled_buf;
}

int can_catch = __cxa_can_catch(&typeid(std::exception), exc_type, &exc_ptr);
int ret;
if (can_catch) {
const char* exc_what = ((std::exception*)exc_ptr)->what();
ret = asprintf(result, "Cpp Exception %s: %s", exc_name, exc_what);
} else {
ret = asprintf(result,
"Cpp Exception: The exception is an object of type '%s' at "
"address %p which does not inherit from std::exception",
exc_name,
exc_ptr);
}

if (demangled_buf) {
free(demangled_buf);
}
return ret;
}
}

#endif // __USING_EMSCRIPTEN_EXCEPTIONS__
59 changes: 59 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,65 @@ def test_exceptions_rethrow_missing(self):
create_file('main.cpp', 'int main() { throw; }')
self.do_runf('main.cpp', None, assert_returncode=NON_ZERO)

def test_format_exception(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add @with_both_eh_sjlj to this test?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you can also remove DISABLE_EXCEPTION_CATCHING below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't work with wasm-exceptions. I am not sure how to make it work with wasm-exceptions, if you like I can look into it. It would probably be entertaining. But I think it should be a separate PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, can you open a bug about that so that we remember to get it fixed. I imagine @aheejin will want it to work for sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened #16380 which is where I got stuck in the wasm-exceptions case.

# needs to flush stdio streams
self.set_setting('EXIT_RUNTIME')
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
self.set_setting('EXPORTED_FUNCTIONS', ['_main', '_throw_exc'])
self.set_setting('FORMAT_EXCEPTION_SUPPORT')
self.set_setting('DISABLE_EXCEPTION_CATCHING', 0)
self.maybe_closure()
self.do_run(
"""
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
#include <emscripten.h>
#include <exception>
#include <stdexcept>
using namespace std;

class myexception : public exception
{
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
virtual const char* what() const throw() { return "My exception happened"; }
} myex;

extern "C" void
throw_exc(int x)
{
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
if (x == 1) {
throw 1000;
}
if (x == 2) {
throw 'c';
}
if (x == 3) {
throw runtime_error("abc");
}
if (x == 4) {
throw myex;
}
if (x == 5) {
throw "abc";
}
}

int
main(){
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
EM_ASM({
for(let i = 1; i < 6; i++){
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
try {
Module["_throw_exc"](i);
} catch(p){
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
console.log(Module["formatException"](p).replace(/0x[0-9a-f]*/, "xxx"));
}
}
});
}
""",
"Cpp Exception: The exception is an object of type 'int' at address xxx which does not inherit from std::exception\n"
"Cpp Exception: The exception is an object of type 'char' at address xxx which does not inherit from std::exception\n"
"Cpp Exception std::runtime_error: abc\n"
"Cpp Exception myexception: My exception happened\n"
"Cpp Exception: The exception is an object of type 'char const*' at address xxx which does not inherit from std::exception\n"
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
)

@with_both_eh_sjlj
def test_bad_typeid(self):
self.do_run(r'''
Expand Down
3 changes: 2 additions & 1 deletion tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,8 @@ def get_files(self):
'stdlib_exception.cpp',
'stdlib_stdexcept.cpp',
'stdlib_typeinfo.cpp',
'private_typeinfo.cpp'
'private_typeinfo.cpp',
'format_exception.cpp'
hoodmane marked this conversation as resolved.
Show resolved Hide resolved
]
if self.eh_mode == Exceptions.NONE:
filenames += ['cxa_noexception.cpp']
Expand Down