Skip to content

Enable libc++ error messages in debug builds #24543

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

Merged
merged 1 commit into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions embuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
'libc++-wasmexcept',
'libc++-noexcept',
'libc++-ww-noexcept',
'libc++-debug',
'libc++-debug-wasmexcept',
'libc++-debug-legacyexcept',
'libc++-debug-noexcept',
'libc++-debug-ww-noexcept',
'libal',
'libdlmalloc',
'libdlmalloc-tracing',
Expand Down Expand Up @@ -106,6 +111,8 @@
'libc++abi-debug-mt-noexcept',
'libc++-mt',
'libc++-mt-noexcept',
'libc++-debug-mt',
'libc++-debug-mt-noexcept',
'libdlmalloc-mt',
'libdlmalloc-mt-debug',
'libGL-emu',
Expand Down
5 changes: 3 additions & 2 deletions system/lib/libcxx/include/__verbose_abort
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// make sure that the program terminates but without taking any complex dependencies in this header.
#if !defined(_LIBCPP_VERBOSE_ABORT)

// XXX EMSCRIPTEN __libcpp_verbose_abort creases code size too much
# if !_LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT || defined (__EMSCRIPTEN__)
// XXX EMSCRIPTEN avoid __libcpp_verbose_abort in release builds due to code
// size
# if !_LIBCPP_AVAILABILITY_HAS_VERBOSE_ABORT || (defined(__EMSCRIPTEN__) && defined(NDEBUG))
// The decltype is there to suppress -Wunused warnings in this configuration.
void __use(const char*, ...);
# define _LIBCPP_VERBOSE_ABORT(...) (decltype(::std::__use(__VA_ARGS__))(), __builtin_abort())
Expand Down
7 changes: 7 additions & 0 deletions system/lib/libcxx/src/verbose_abort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#ifdef __BIONIC__
# include <syslog.h>
Expand All @@ -30,6 +31,12 @@ _LIBCPP_WEAK void __libcpp_verbose_abort(char const* format, ...) _LIBCPP_VERBOS
va_list list;
va_start(list, format);
std::vfprintf(stderr, format, list);
// TODO(sbc): Add newline here unconditionally. libc++ seems inconsistent about strings
// passed to __libcpp_verbose_abort. The _LIBCPP_VERBOSE_ABORT macro seems to never use
// newlines, but _LIBCPP_ASSERTION_HANDLER does include a newline.
if (format[strlen(format) - 1] != '\n') {
std::fprintf(stderr, "\n");
Copy link
Member

Choose a reason for hiding this comment

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

this could be std::fputs instead?

}
va_end(list);
}

Expand Down
17 changes: 17 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -16219,3 +16219,20 @@ def test_unsupported_min_version_when_unsupported_env(self, env):
})
def test_automatic_env_worker(self, env, emcc_args):
self.emcc(test_file('hello_world.c'), [f'-sENVIRONMENT={env}'] + emcc_args)

def test_libcxx_errors(self):
create_file('main.cpp', '''
#include <thread>
void func() {
}

int main() {
std::thread t(func);
t.join();
}
''')

# Since we are building without -pthread the thread constructor will fail,
# and in debug mode at least we expect to see the error message from libc++
expected = 'system_error was thrown in -fno-exceptions mode with error 6 and message "thread constructor failed"'
self.do_runf('main.cpp', expected, assert_returncode=NON_ZERO)
10 changes: 7 additions & 3 deletions test/test_sanity.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,17 @@ def test_emcc_caching(self):

# Building a file that *does* need something *should* trigger cache
# generation, but only the first time
libname = cache.get_lib_name('libc++.a')
for i in range(3):
print(i)
self.clear()
output = self.do([EMCC, '-O' + str(i), test_file('hello_libcxx.cpp'), '-sDISABLE_EXCEPTION_CATCHING=0'])
print('\n\n\n', output)
self.assertContainedIf(BUILDING_MESSAGE % libname, output, i == 0)
if i == 0:
libname = cache.get_lib_name('libc++-debug.a')
else:
libname = cache.get_lib_name('libc++.a')
# -O0 and -O1 will each build a version of libc++.a, but higher level will re-use the
# one built at -O1.
self.assertContainedIf(BUILDING_MESSAGE % libname, output, i < 2)
self.assertContained('hello, world!', self.run_js('a.out.js'))
self.assertExists(cache.cachedir)
self.assertExists(os.path.join(cache.cachedir, libname))
Expand Down
2 changes: 1 addition & 1 deletion tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1665,7 +1665,7 @@ def get_files(self):
filenames=filenames)


class libcxx(ExceptionLibrary, MTLibrary):
class libcxx(ExceptionLibrary, MTLibrary, DebugLibrary):
name = 'libc++'

cflags = [
Expand Down