diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst --- a/libcxx/docs/UsingLibcxx.rst +++ b/libcxx/docs/UsingLibcxx.rst @@ -163,7 +163,7 @@ .. code-block:: cpp - void __libcpp_assertion_handler(char const* file, int line, char const* expression, char const* message) + void __libcpp_assertion_handler(char const* format, ...) This mechanism is similar to how one can replace the default definition of ``operator new`` and ``operator delete``. For example: @@ -173,8 +173,12 @@ // In HelloWorldHandler.cpp #include // must include any libc++ header before defining the handler (C compatibility headers excluded) - void std::__libcpp_assertion_handler(char const* file, int line, char const* expression, char const* message) { - std::printf("Assertion %s failed at %s:%d, more info: %s", expression, file, line, message); + void std::__libcpp_assertion_handler(char const* format, ...) { + va_list list; + va_start(list, format); + std::vfprintf(stderr, format, list); + va_end(list); + std::abort(); } diff --git a/libcxx/include/__assert b/libcxx/include/__assert --- a/libcxx/include/__assert +++ b/libcxx/include/__assert @@ -45,7 +45,7 @@ # define _LIBCPP_ASSERT(expression, message) \ (__builtin_expect(static_cast(expression), 1) ? \ (void)0 : \ - ::std::__libcpp_assertion_handler(__FILE__, __LINE__, #expression, message)) + ::std::__libcpp_assertion_handler("%s:%d: assertion %s failed: %s", __FILE__, __LINE__, #expression, message)) #elif !defined(_LIBCPP_ASSERTIONS_DISABLE_ASSUME) && __has_builtin(__builtin_assume) # define _LIBCPP_ASSERT(expression, message) \ (_LIBCPP_DIAGNOSTIC_PUSH \ @@ -58,8 +58,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD -_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_ASSERTION_HANDLER -void __libcpp_assertion_handler(char const* __file, int __line, char const* __expression, char const* __message); +_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_ASSERTION_HANDLER _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) +void __libcpp_assertion_handler(const char *__format, ...); _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/lib/abi/CHANGELOG.TXT b/libcxx/lib/abi/CHANGELOG.TXT --- a/libcxx/lib/abi/CHANGELOG.TXT +++ b/libcxx/lib/abi/CHANGELOG.TXT @@ -64,7 +64,7 @@ Symbol removed: _ZTSNSt3__18__c_nodeE Symbol removed: _ZTVNSt3__18__c_nodeE -* b0fd9497af6d - [libc++] Add a lightweight overridable assertion handler +* b0fd9497af6d and XXXXXXX - [libc++] Add a lightweight overridable assertion handler This patch adds a lightweight assertion handler mechanism that can be overriden at link-time in a fashion similar to `operator new`. A default @@ -73,7 +73,7 @@ All platforms ------------- - Symbol added: _ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_ + Symbol added: _ZNSt3__126__libcpp_assertion_handlerEPKcz ------------ Version 14.0 diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist --- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist +++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist @@ -1534,7 +1534,7 @@ {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'} -{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'} +{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKcz', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'} diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist --- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist +++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist @@ -1678,7 +1678,7 @@ {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} -{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} +{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKcz', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist --- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist +++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist @@ -1678,7 +1678,7 @@ {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_istringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEaSEOS5_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} -{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} +{'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKcz', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} {'import_export': 'wEXP', 'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'storage_mapping_class': 'DS', 'type': 'FUNC'} diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist --- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist +++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist @@ -1534,7 +1534,7 @@ {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'} -{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'} +{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKcz', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'} diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist --- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist +++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist @@ -1225,7 +1225,7 @@ {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'} -{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'} +{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKcz', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'} diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist --- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist +++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.noexceptions.nonew.abilist @@ -1197,7 +1197,7 @@ {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'} -{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'} +{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKcz', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'} {'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'} diff --git a/libcxx/src/assert.cpp b/libcxx/src/assert.cpp --- a/libcxx/src/assert.cpp +++ b/libcxx/src/assert.cpp @@ -8,14 +8,59 @@ #include <__assert> #include <__config> +#include #include #include +#ifdef __BIONIC__ +# include +# include +extern "C" void android_set_abort_message(const char* msg); +#endif + +#if defined(__APPLE__) && __has_include() +# include +#endif + _LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_WEAK -void __libcpp_assertion_handler(char const* __file, int __line, char const* __expression, char const* __message) { - std::fprintf(stderr, "%s:%d: libc++ assertion '%s' failed. %s\n", __file, __line, __expression, __message); +void __libcpp_assertion_handler(char const* __format, ...) { + // Write message to stderr. We do this before formatting into a + // buffer so that we still get some information out if that fails. + { + va_list __list; + va_start(__list, __format); + std::vfprintf(stderr, __format, __list); + va_end(__list); + } + + // Format into a buffer for CrashReporter & friends + // Format the arguments into an allocated buffer for CrashReport & friends. + // We leak the buffer on purpose, since we're about to abort() anyway. + char* __buffer; + { + va_list __list; + va_start(__list, __format); + vasprintf(&__buffer, __format, __list); + va_end(__list); + } + +#if defined(__APPLE__) && __has_include() + // Note that we should technically synchronize accesses here (by e.g. taking a lock), + // however concretely we're only setting a pointer, so the likelihood of a race here + // is low. + CRSetCrashLogMessage(__buffer); +#elif defined(__BIONIC__) + // Show error in tombstone. + android_set_abort_message(__buffer); + + // Show error in logcat. + openlog("libc++", 0, 0); + syslog(LOG_CRIT, "%s", __buffer); + closelog(); +#endif + std::abort(); } diff --git a/libcxx/test/libcxx/assertions/customize_handler.backdeployment.pass.cpp b/libcxx/test/libcxx/assertions/customize_handler.backdeployment.pass.cpp --- a/libcxx/test/libcxx/assertions/customize_handler.backdeployment.pass.cpp +++ b/libcxx/test/libcxx/assertions/customize_handler.backdeployment.pass.cpp @@ -17,7 +17,7 @@ #include bool handler_called = false; -void std::__libcpp_assertion_handler(char const*, int, char const*, char const*) { +void std::__libcpp_assertion_handler(char const*, ...) { handler_called = true; } diff --git a/libcxx/test/libcxx/assertions/customize_handler.pass.cpp b/libcxx/test/libcxx/assertions/customize_handler.pass.cpp --- a/libcxx/test/libcxx/assertions/customize_handler.pass.cpp +++ b/libcxx/test/libcxx/assertions/customize_handler.pass.cpp @@ -17,7 +17,7 @@ #include bool handler_called = false; -void std::__libcpp_assertion_handler(char const*, int, char const*, char const*) { +void std::__libcpp_assertion_handler(char const*, ...) { handler_called = true; } diff --git a/libcxx/test/libcxx/assertions/debug_mode_compatibility.pass.cpp b/libcxx/test/libcxx/assertions/debug_mode_compatibility.pass.cpp --- a/libcxx/test/libcxx/assertions/debug_mode_compatibility.pass.cpp +++ b/libcxx/test/libcxx/assertions/debug_mode_compatibility.pass.cpp @@ -19,7 +19,7 @@ #include bool handler_called = false; -void std::__libcpp_assertion_handler(char const*, int, char const*, char const*) { +void std::__libcpp_assertion_handler(char const*, ...) { handler_called = true; } diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h --- a/libcxx/test/support/check_assertion.h +++ b/libcxx/test/support/check_assertion.h @@ -10,6 +10,7 @@ #define TEST_SUPPORT_CHECK_ASSERTION_H #include +#include #include #include #include @@ -235,8 +236,19 @@ std::string stderr_from_child_; }; -void std::__libcpp_assertion_handler(char const* file, int line, char const* /*expression*/, char const* message) { +void std::__libcpp_assertion_handler(char const* format, ...) { assert(!GlobalMatcher().empty()); + + // Extract information from the error message. This has to stay synchronized with + // how we format assertions in the library. + va_list list; + va_start(list, format); + char const* file = va_arg(list, char const*); + int line = va_arg(list, int); + char const* expression = va_arg(list, char const*); (void)expression; + char const* message = va_arg(list, char const*); + va_end(list); + if (GlobalMatcher().Matches(file, line, message)) { std::exit(DeathTest::RK_MatchFound); }