diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index cc2122e317e..418b35d8503 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -1,4 +1,5 @@ -#include +#include +#include #include #include @@ -94,34 +95,40 @@ void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategori return; } - const std::string_view trace { from }; + std::string_view trace { from }; if (trace.empty ()) [[unlikely]] { log_warn (category, "Empty stack trace passed by the managed runtime."); return; } - for (const auto segment : std::views::split (trace, '\n')) { - const std::string_view line { segment }; + while (true) { + size_t line_end = trace.find ('\n'); + size_t line_length = line_end == std::string_view::npos ? trace.length () : line_end; + std::string_view line { trace.data (), line_length }; if ((category == LOG_GREF && Logger::gref_to_logcat ()) || (category == LOG_LREF && Logger::lref_to_logcat ())) { - log_debug (category, "{}"sv, line); + log_debugf (category, "%.*s", static_cast(line.length ()), line.data ()); } - if (to == nullptr) { - continue; + if (to != nullptr) { + fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to); + fputc ('\n', to); + fflush (to); } - fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to); - fputc ('\n', to); - fflush (to); + if (line_end == std::string_view::npos) { + break; + } + + trace.remove_prefix (line_end + 1); } } void OSBridge::_monodroid_gref_log (const char *message) noexcept { if (Logger::gref_to_logcat ()) { - log_debug (LOG_GREF, "{}"sv, optional_string (message)); + log_debugf (LOG_GREF, "%s", optional_string (message)); } if (Logger::gref_log () == nullptr) { @@ -133,7 +140,7 @@ void OSBridge::_monodroid_gref_log (const char *message) noexcept } [[gnu::always_inline, gnu::flatten]] -void OSBridge::log_it (LogCategories category, std::string const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept +void OSBridge::log_it (LogCategories category, std::string_view const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept { log_write (category, LogLevel::Info, line); @@ -146,13 +153,31 @@ void OSBridge::log_it (LogCategories category, std::string const& line, FILE *to return; } - fwrite (line.c_str (), sizeof (std::string::value_type), line.length (), to); + fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to); fputc ('\n', to); _write_stack_trace (to, from, category); fflush (to); } +void OSBridge::log_itf (LogCategories category, FILE *to, const char *const from, bool logcat_enabled, const char *format, ...) noexcept +{ + const char *safe_format = format == nullptr ? "" : format; + char *line = nullptr; + va_list args; + va_start (args, format); + int length = vasprintf (&line, safe_format, args); + va_end (args); + + if (length < 0) [[unlikely]] { + log_it (category, safe_format, to, from, logcat_enabled); + return; + } + + log_it (category, std::string_view { line, static_cast(length) }, to, from, logcat_enabled); + std::free (line); +} + auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) noexcept -> int { int c = _monodroid_gref_inc (); @@ -161,8 +186,12 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject } int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED); - const std::string log_line = std::format ( - "+g+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv, + log_itf ( + LOG_GREF, + Logger::gref_log (), + from, + Logger::gref_to_logcat (), + "+g+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", c, wc, reinterpret_cast(curHandle), @@ -172,8 +201,6 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject optional_string (threadName), threadId ); - - log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ()); return c; } @@ -185,8 +212,12 @@ void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char } int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED); - const std::string log_line = std::format ( - "-g- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv, + log_itf ( + LOG_GREF, + Logger::gref_log (), + from, + Logger::gref_to_logcat (), + "-g- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", c, wc, reinterpret_cast(handle), @@ -194,8 +225,6 @@ void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char optional_string (threadName), threadId ); - - log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ()); } void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) @@ -206,8 +235,12 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec } int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED); - const std::string log_line = std::format ( - "+w+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv, + log_itf ( + LOG_GREF, + Logger::gref_log (), + from, + Logger::gref_to_logcat (), + "+w+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", gc, c, reinterpret_cast(curHandle), @@ -217,8 +250,6 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec optional_string (threadName), threadId ); - - log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ()); } void @@ -228,16 +259,18 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c return; } - const std::string log_line = std::format ( - "+l+ lrefc {} handle {:p}/{} from thread '{}'({})"sv, + log_itf ( + LOG_LREF, + Logger::lref_log (), + from, + Logger::lref_to_logcat (), + "+l+ lrefc %d handle %p/%c from thread '%s'(%d)", lrefc, reinterpret_cast(handle), type, optional_string (threadName), threadId ); - - log_it (LOG_LREF, log_line, Logger::lref_log (), from, Logger::lref_to_logcat ()); } void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const char *threadName, int threadId, const char *from) @@ -248,8 +281,12 @@ void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const cha } int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED); - const std::string log_line = std::format ( - "-w- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv, + log_itf ( + LOG_GREF, + Logger::gref_log (), + from, + Logger::gref_to_logcat (), + "-w- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", gc, c, reinterpret_cast(handle), @@ -257,8 +294,6 @@ void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const cha optional_string (threadName), threadId ); - - log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ()); } void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, const char *threadName, int threadId, const char *from) @@ -267,14 +302,16 @@ void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, return; } - const std::string log_line = std::format ( - "-l- lrefc {} handle {:p}/{} from thread '{}'({})"sv, + log_itf ( + LOG_LREF, + Logger::lref_log (), + from, + Logger::lref_to_logcat (), + "-l- lrefc %d handle %p/%c from thread '%s'(%d)", lrefc, reinterpret_cast(handle), type, optional_string (threadName), threadId ); - - log_it (LOG_LREF, log_line, Logger::lref_log (), from, Logger::lref_to_logcat ()); } diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh index ee96c220671..9f65cfd7414 100644 --- a/src/native/clr/include/host/os-bridge.hh +++ b/src/native/clr/include/host/os-bridge.hh @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include @@ -57,7 +59,8 @@ namespace xamarin::android { private: static void _write_stack_trace (FILE *to, const char *const from, LogCategories = LOG_NONE) noexcept; - static void log_it (LogCategories category, std::string const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept; + static void log_it (LogCategories category, std::string_view const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept; + static void log_itf (LogCategories category, FILE *to, const char *const from, bool logcat_enabled, const char *format, ...) noexcept __attribute__ ((format (printf, 5, 6))); private: static inline JavaVM *jvm = nullptr; diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh index 7aef6e20456..b87347ad0a1 100644 --- a/src/native/clr/include/shared/log_types.hh +++ b/src/native/clr/include/shared/log_types.hh @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -47,6 +48,12 @@ namespace xamarin::android { // A slightly faster alternative to other log functions as it doesn't parse the message // for format placeholders nor it uses variable arguments void log_write (LogCategories category, LogLevel level, const char *message) noexcept; + void log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept; + void log_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); + void log_debugf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_infof (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_warnf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_errorf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); [[gnu::always_inline]] static inline void log_write (LogCategories category, LogLevel level, std::string_view const& message) noexcept diff --git a/src/native/clr/shared/helpers.cc b/src/native/clr/shared/helpers.cc index 7850592af5a..5c95342b1af 100644 --- a/src/native/clr/shared/helpers.cc +++ b/src/native/clr/shared/helpers.cc @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,11 +8,24 @@ using namespace xamarin::android; +[[noreturn]] void +Helpers::abort_applicationf (LogCategories category, std::source_location sloc, const char *format, ...) noexcept +{ + char *message = nullptr; + const char *safe_format = format == nullptr ? "" : format; + va_list args; + va_start (args, format); + int ret = vasprintf (&message, safe_format, args); + va_end (args); + + abort_application (category, ret < 0 ? safe_format : message, true, sloc); +} + [[noreturn]] void Helpers::abort_application (LogCategories category, const char *message, bool log_location, std::source_location sloc) noexcept { // Log it, but also... - log_fatal (category, "{}", message); + log_write (category, LogLevel::Fatal, message); // ...let android include it in the tombstone, debuggerd output, stack trace etc android_set_abort_message (message); @@ -33,9 +47,10 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo } } - log_fatal ( + log_writef ( category, - "Abort at {}:{}:{} ('{}')", + LogLevel::Fatal, + "Abort at %s:%u:%u ('%s')", file_name, sloc.line (), sloc.column (), diff --git a/src/native/clr/shared/log_functions.cc b/src/native/clr/shared/log_functions.cc index acd5e705c06..2f3db78abe7 100644 --- a/src/native/clr/shared/log_functions.cc +++ b/src/native/clr/shared/log_functions.cc @@ -55,6 +55,17 @@ namespace { }; constexpr size_t loglevel_map_max_index = (sizeof(loglevel_map) / sizeof(android_LogPriority)) - 1; + + [[gnu::always_inline]] + auto priority_for_level (LogLevel level) noexcept -> android_LogPriority + { + size_t map_index = static_cast(level); + if (map_index > loglevel_map_max_index) { + return DEFAULT_PRIORITY; + } + + return loglevel_map[map_index]; + } } unsigned int log_categories = LOG_NONE; @@ -117,15 +128,67 @@ namespace xamarin::android { void log_write (LogCategories category, LogLevel level, const char *message) noexcept { - size_t map_index = static_cast(level); - android_LogPriority priority; + __android_log_write (priority_for_level (level), category_name (category), message); + } - if (map_index > loglevel_map_max_index) { - priority = DEFAULT_PRIORITY; - } else { - priority = loglevel_map[map_index]; + void + log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept + { + const char *safe_format = format == nullptr ? "" : format; + __android_log_vprint (priority_for_level (level), category_name (category), safe_format, args); + } + + void + log_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, level, format, args); + va_end (args); + } + + void + log_debugf (LogCategories category, const char *format, ...) noexcept + { + if ((log_categories & category) == 0) { + return; + } + + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Debug, format, args); + va_end (args); + } + + void + log_infof (LogCategories category, const char *format, ...) noexcept + { + if ((log_categories & category) == 0) { + return; } - __android_log_write (priority, category_name (category), message); + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Info, format, args); + va_end (args); } + + void + log_warnf (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Warn, format, args); + va_end (args); + } + + void + log_errorf (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Error, format, args); + va_end (args); + } + } diff --git a/src/native/common/include/shared/helpers.hh b/src/native/common/include/shared/helpers.hh index 22e29b40f25..f1ec1fb6ab4 100644 --- a/src/native/common/include/shared/helpers.hh +++ b/src/native/common/include/shared/helpers.hh @@ -56,6 +56,9 @@ namespace xamarin::android [[noreturn]] static void abort_application (LogCategories category, const char *message, bool log_location = true, std::source_location sloc = std::source_location::current ()) noexcept; + [[noreturn]] + static void abort_applicationf (LogCategories category, std::source_location sloc, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); + [[noreturn]] static void abort_application (LogCategories category, std::string const& message, bool log_location = true, std::source_location sloc = std::source_location::current ()) noexcept { diff --git a/src/native/mono/shared/log_functions.cc b/src/native/mono/shared/log_functions.cc index 394ba2a1927..16e8eda7525 100644 --- a/src/native/mono/shared/log_functions.cc +++ b/src/native/mono/shared/log_functions.cc @@ -1,4 +1,5 @@ #include +#include #include #include @@ -102,19 +103,81 @@ static constexpr android_LogPriority loglevel_map[] = { static constexpr size_t loglevel_map_max_index = (sizeof(loglevel_map) / sizeof(android_LogPriority)) - 1; +static auto +priority_for_level (xamarin::android::LogLevel level) noexcept -> android_LogPriority +{ + size_t map_index = static_cast(level); + if (map_index > loglevel_map_max_index) { + return DEFAULT_PRIORITY; + } + + return loglevel_map[map_index]; +} + namespace xamarin::android { void log_write (LogCategories category, LogLevel level, const char *message) noexcept { - size_t map_index = static_cast(level); - android_LogPriority priority; + __android_log_write (priority_for_level (level), CATEGORY_NAME (category), message); + } + + void + log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept + { + const char *safe_format = format == nullptr ? "" : format; + __android_log_vprint (priority_for_level (level), CATEGORY_NAME (category), safe_format, args); + } + + void + log_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, level, format, args); + va_end (args); + } + + void + log_debugf (LogCategories category, const char *format, ...) noexcept + { + if ((log_categories & category) == 0) { + return; + } + + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Debug, format, args); + va_end (args); + } - if (map_index > loglevel_map_max_index) { - priority = DEFAULT_PRIORITY; - } else { - priority = loglevel_map[map_index]; + void + log_infof (LogCategories category, const char *format, ...) noexcept + { + if ((log_categories & category) == 0) { + return; } - __android_log_write (priority, CATEGORY_NAME (category), message); + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Info, format, args); + va_end (args); + } + + void + log_warnf (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Warn, format, args); + va_end (args); + } + + void + log_errorf (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Error, format, args); + va_end (args); } } diff --git a/src/native/mono/shared/log_types.hh b/src/native/mono/shared/log_types.hh index 0821ef59077..e476b82b1cd 100644 --- a/src/native/mono/shared/log_types.hh +++ b/src/native/mono/shared/log_types.hh @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -47,6 +48,12 @@ namespace xamarin::android { // A slightly faster alternative to other log functions as it doesn't parse the message // for format placeholders nor it uses variable arguments void log_write (LogCategories category, LogLevel level, const char *message) noexcept; + void log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept; + void log_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); + void log_debugf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_infof (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_warnf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_errorf (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); [[gnu::always_inline]] static inline void log_write (LogCategories category, LogLevel level, std::string_view const& message) noexcept diff --git a/src/native/nativeaot/host/bridge-processing.cc b/src/native/nativeaot/host/bridge-processing.cc index b87a49b431b..eb636515d6f 100644 --- a/src/native/nativeaot/host/bridge-processing.cc +++ b/src/native/nativeaot/host/bridge-processing.cc @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -21,16 +19,12 @@ void BridgeProcessing::naot_initialize_on_runtime_init (JNIEnv *env) noexcept GCUserPeerable_jiClearManagedReferences = env->GetMethodID (GCUserPeerable_class, "jiClearManagedReferences", "()V"); if (GCUserPeerable_jiAddManagedReference == nullptr || GCUserPeerable_jiClearManagedReferences == nullptr) [[unlikely]] { - constexpr auto ABSENT = "absent"sv; - constexpr auto PRESENT = "present"sv; - - Helpers::abort_application ( + Helpers::abort_applicationf ( LOG_DEFAULT, - std::format ( - "Failed to find GCUserPeerable method(s): jiAddManagedReference ({}); jiClearManagedReferences ({})"sv, - GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT, - GCUserPeerable_jiClearManagedReferences == nullptr ? ABSENT : PRESENT - ) + std::source_location::current (), + "Failed to find GCUserPeerable method(s): jiAddManagedReference (%s); jiClearManagedReferences (%s)", + GCUserPeerable_jiAddManagedReference == nullptr ? "absent" : "present", + GCUserPeerable_jiClearManagedReferences == nullptr ? "absent" : "present" ); } } diff --git a/src/native/nativeaot/host/host.cc b/src/native/nativeaot/host/host.cc index b6d87483c78..7258f963785 100644 --- a/src/native/nativeaot/host/host.cc +++ b/src/native/nativeaot/host/host.cc @@ -33,9 +33,9 @@ auto HostCommon::Java_JNI_OnLoad (JavaVM *vm, void *reserved) noexcept -> jint if (__jni_on_load_handler_count > 0) { for (uint32_t i = 0; i < __jni_on_load_handler_count; i++) { - log_debug ( + log_debugf ( LOG_ASSEMBLY, - "Calling JNI on-load init func '{}' ({:p})", + "Calling JNI on-load init func '%s' (%p)", optional_string (__jni_on_load_handler_names[i]), reinterpret_cast(__jni_on_load_handlers[i]) );