From 2c4267a080a2b7172354090f5ffe0b93c1de7af4 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 8 May 2026 19:45:50 +0200 Subject: [PATCH 01/16] Drop NativeAOT libc++ link dependency Remove the explicit NativeAOT final-link dependency on libc++/libc++abi and keep the Android NativeAOT link guarded with -nostdlib++. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Microsoft.Android.Sdk.NativeAOT.targets | 13 +-- .../Utilities/NativeRuntimeComponents.cs | 4 - src/native/clr/host/bridge-processing.cc | 84 ++++++++++---- src/native/clr/host/gc-bridge.cc | 45 ++++++-- src/native/clr/host/host-shared.cc | 10 +- .../clr/host/internal-pinvokes-shared.cc | 18 ++- src/native/clr/host/os-bridge.cc | 106 +++++++++++++----- .../include/host/bridge-processing-shared.hh | 22 ++-- .../clr/include/host/bridge-processing.hh | 11 -- src/native/clr/include/host/gc-bridge.hh | 26 +++-- .../clr/include/host/host-environment.hh | 16 ++- src/native/clr/include/host/os-bridge.hh | 2 +- .../include/runtime-base/android-system.hh | 33 ++++++ src/native/clr/include/runtime-base/util.hh | 66 ++++++----- src/native/clr/include/shared/log_types.hh | 47 ++++++++ .../clr/runtime-base/android-system-shared.cc | 20 +++- src/native/clr/runtime-base/logger.cc | 52 ++++++--- src/native/clr/runtime-base/util.cc | 17 ++- src/native/clr/shared/helpers.cc | 12 +- .../include/runtime-base/jni-wrappers.hh | 12 +- .../common/include/runtime-base/strings.hh | 42 ++++--- .../include/runtime-base/timing-internal.hh | 21 ++-- .../common/include/runtime-base/timing.hh | 22 ++-- src/native/common/include/shared/cpp-util.hh | 70 +++--------- src/native/nativeaot/host/CMakeLists.txt | 1 + .../nativeaot/host/bridge-processing.cc | 36 ++++-- src/native/nativeaot/host/cxx-shims.cc | 62 ++++++++++ src/native/nativeaot/host/host.cc | 19 +++- .../nativeaot/host/internal-pinvoke-stubs.cc | 7 +- .../include/host/bridge-processing.hh | 9 +- src/native/nativeaot/include/host/host.hh | 3 + 31 files changed, 622 insertions(+), 286 deletions(-) create mode 100644 src/native/nativeaot/host/cxx-shims.cc create mode 100644 src/native/nativeaot/include/host/host.hh diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index e145c311485..a6f5b0f65ec 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -132,7 +132,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. $(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt) llvm-objcopy - + false @@ -197,13 +198,9 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_NdkLibs Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" /> - - <_NdkLibs Include="$(_NdkSysrootDir)libc++_static.a" /> - <_NdkLibs Include="$(_NdkSysrootDir)libc++abi.a" /> - - + @@ -362,12 +359,10 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_NativeAotSystemLibraries Include="c" /> - <_NativeAotLinkLibraries Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" /> - <_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++_static.a" /> - <_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++abi.a" /> <_NativeAotLinkLibraries Include="@(_NdkLibs)" /> diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs index 4773d0cd1c1..cf5e40120ea 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs @@ -129,10 +129,6 @@ public NativeRuntimeComponents (ITaskItem[]? monoComponents) new AndroidArchive ("libxa-shared-bits-release.a"), new AndroidArchive ("libxamarin-startup-release.a"), - // C++ standard library - new CplusPlusArchive ("libc++_static.a"), - new CplusPlusArchive ("libc++abi.a"), - // LLVM clang built-ins archives new ClangBuiltinsArchive ("aarch64"), new ClangBuiltinsArchive ("arm"), diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index b4ea077a081..586c33d8394 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -1,3 +1,6 @@ +#include +#include + #include #include #include @@ -17,9 +20,10 @@ void BridgeProcessingShared::initialize_on_runtime_init (JNIEnv *env, jclass run abort_unless (GCUserPeer_class != nullptr && GCUserPeer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!"); } -BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args) noexcept +BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *host_callbacks) noexcept : env{ OSBridge::ensure_jnienv () }, - cross_refs{ args } + cross_refs{ args }, + callbacks{ host_callbacks != nullptr ? *host_callbacks : BridgeProcessingCallbacks {} } { if (args == nullptr) [[unlikely]] { Helpers::abort_application (LOG_GC, "Cross references argument is a NULL pointer"sv); @@ -32,6 +36,16 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args) n if (args->CrossReferenceCount > 0 && args->CrossReferences == nullptr) [[unlikely]] { Helpers::abort_application (LOG_GC, "CrossReferences member of the cross references arguments structure is NULL"sv); } + + if (args->ComponentCount > 0) { + temporary_peers = static_cast(calloc (args->ComponentCount, sizeof (jobject))); + abort_unless (temporary_peers != nullptr, "Failed to allocate GC bridge temporary peer array"); + } +} + +BridgeProcessingShared::~BridgeProcessingShared () noexcept +{ + free (temporary_peers); } void BridgeProcessingShared::process () noexcept @@ -59,8 +73,11 @@ void BridgeProcessingShared::prepare_for_java_collection () noexcept } // With cross references processed, the temporary peer list can be released - for (const auto& [scc, temporary_peer] : temporary_peers) { - env->DeleteLocalRef (temporary_peer); + for (size_t i = 0; i < cross_refs->ComponentCount; i++) { + if (temporary_peers[i] != nullptr) { + env->DeleteLocalRef (temporary_peers[i]); + temporary_peers[i] = nullptr; + } } // Switch global to weak references @@ -79,7 +96,8 @@ void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, { // Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one if (scc.Count == 0) { - temporary_peers [scc_index] = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); + abort_unless (temporary_peers != nullptr, "Temporary peer array must be allocated"); + temporary_peers[scc_index] = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); return; } @@ -98,9 +116,9 @@ CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size const StronglyConnectedComponent &scc = cross_refs->Components [scc_index]; if (scc.Count == 0) { - const auto temporary_peer = temporary_peers.find (scc_index); - abort_unless (temporary_peer != temporary_peers.end(), "Temporary peer must be found in the map"); - return { .is_temporary_peer = true, .temporary_peer = temporary_peer->second }; + abort_unless (temporary_peers != nullptr, "Temporary peer array must be allocated"); + abort_unless (temporary_peers[scc_index] != nullptr, "Temporary peer must be found in the array"); + return { .is_temporary_peer = true, .temporary_peer = temporary_peers[scc_index] }; } abort_unless (scc.Contexts [0] != nullptr, "SCC must have at least one context"); @@ -219,6 +237,24 @@ void BridgeProcessingShared::clear_references (jobject handle) noexcept env->CallVoidMethod (handle, clear_method_id); } +bool BridgeProcessingShared::maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *jni_env, jobject from, jobject to) noexcept +{ + if (callbacks.maybe_call_gc_user_peerable_add_managed_reference == nullptr) { + return false; + } + + return callbacks.maybe_call_gc_user_peerable_add_managed_reference (callbacks.context, jni_env, from, to); +} + +bool BridgeProcessingShared::maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *jni_env, jobject handle) noexcept +{ + if (callbacks.maybe_call_gc_user_peerable_clear_managed_references == nullptr) { + return false; + } + + return callbacks.maybe_call_gc_user_peerable_clear_managed_references (callbacks.context, jni_env, handle); +} + void BridgeProcessingShared::take_global_ref (HandleContext &context) noexcept { abort_unless (context.control_block != nullptr, "Control block must not be null"); @@ -317,7 +353,7 @@ void CrossReferenceTarget::mark_refs_added_if_needed () noexcept [[gnu::always_inline]] void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] jclass java_class) noexcept { - log_error (LOG_DEFAULT, "Failed to find monodroidAddReferences method"); + log_write (LOG_DEFAULT, LogLevel::Error, "Failed to find monodroidAddReferences method"); #if DEBUG abort_if_invalid_pointer_argument (java_class, "java_class"); if (!Logger::gc_spew_enabled ()) [[likely]] { @@ -325,7 +361,9 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - log_error (LOG_GC, "Missing monodroidAddReferences method for object of class {}", optional_string (class_name)); + char message[256]; + snprintf (message, sizeof (message), "Missing monodroidAddReferences method for object of class %s", optional_string (class_name)); + log_write (LOG_GC, LogLevel::Error, message); free (class_name); #endif } @@ -333,7 +371,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] [[gnu::always_inline]] void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused]] jclass java_class) noexcept { - log_error (LOG_DEFAULT, "Failed to find monodroidClearReferences method"); + log_write (LOG_DEFAULT, LogLevel::Error, "Failed to find monodroidClearReferences method"); #if DEBUG abort_if_invalid_pointer_argument (java_class, "java_class"); if (!Logger::gc_spew_enabled ()) [[likely]] { @@ -341,7 +379,9 @@ void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - log_error (LOG_GC, "Missing monodroidClearReferences method for object of class {}", optional_string (class_name)); + char message[256]; + snprintf (message, sizeof (message), "Missing monodroidClearReferences method for object of class %s", optional_string (class_name)); + log_write (LOG_GC, LogLevel::Error, message); free (class_name); #endif } @@ -364,10 +404,9 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe return; } - OSBridge::_monodroid_gref_log ( - std::format ("take_global_ref wref={:#x} -> handle={:#x}\n"sv, - reinterpret_cast (weak), - reinterpret_cast (handle)).data ()); + char message[128]; + snprintf (message, sizeof (message), "take_global_ref wref=%p -> handle=%p\n", reinterpret_cast(weak), reinterpret_cast(handle)); + OSBridge::_monodroid_gref_log (message); } [[gnu::always_inline]] @@ -377,8 +416,9 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept return; } - OSBridge::_monodroid_gref_log ( - std::format ("handle {:#x}/W; was collected by a Java GC"sv, reinterpret_cast (weak)).data ()); + char message[128]; + snprintf (message, sizeof (message), "handle %p/W; was collected by a Java GC", reinterpret_cast(weak)); + OSBridge::_monodroid_gref_log (message); } [[gnu::always_inline]] @@ -388,7 +428,9 @@ void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept return; } - OSBridge::_monodroid_gref_log (std::format ("take_weak_global_ref handle={:#x}\n"sv, reinterpret_cast (handle)).data ()); + char message[128]; + snprintf (message, sizeof (message), "take_weak_global_ref handle=%p\n", reinterpret_cast(handle)); + OSBridge::_monodroid_gref_log (message); } [[gnu::always_inline]] @@ -449,5 +491,7 @@ void BridgeProcessingShared::log_gc_summary () noexcept } } - log_info (LOG_GC, "GC cleanup summary: {} objects tested - resurrecting {}.", total, alive); + char message[128]; + snprintf (message, sizeof (message), "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); + log_write (LOG_GC, LogLevel::Info, message); } diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 6bf3a387e96..31cd005a826 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -1,3 +1,7 @@ +#include +#include +#include + #include #include #include @@ -45,7 +49,7 @@ void GCBridge::trigger_java_gc (JNIEnv *env) noexcept env->ExceptionDescribe (); env->ExceptionClear (); - log_error (LOG_DEFAULT, "Java GC failed"); + log_write (LOG_DEFAULT, LogLevel::Error, "Java GC failed"); } void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept @@ -55,8 +59,9 @@ void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept abort_unless (args->CrossReferences != nullptr || args->CrossReferenceCount == 0, "CrossReferences must not be null if CrossReferenceCount is greater than 0"); log_mark_cross_references_args_if_enabled (args); - shared_args.store (args); - shared_args_semaphore.release (); + __atomic_store_n (&shared_args, args, __ATOMIC_RELEASE); + int ret = sem_post (&shared_args_semaphore); + abort_unless (ret == 0, "Failed to release GC bridge semaphore"); } void GCBridge::bridge_processing () noexcept @@ -66,8 +71,13 @@ void GCBridge::bridge_processing () noexcept while (true) { // wait until mark cross references args are set by the GC callback - shared_args_semaphore.acquire (); - MarkCrossReferencesArgs *args = shared_args.load (); + int ret; + do { + ret = sem_wait (&shared_args_semaphore); + } while (ret == -1 && errno == EINTR); + abort_unless (ret == 0, "Failed to acquire GC bridge semaphore"); + + MarkCrossReferencesArgs *args = __atomic_load_n (&shared_args, __ATOMIC_ACQUIRE); bridge_processing_started_callback (args); @@ -78,6 +88,12 @@ void GCBridge::bridge_processing () noexcept } } +auto GCBridge::bridge_processing_thread_entry ([[maybe_unused]] void *arg) noexcept -> void* +{ + bridge_processing (); + return nullptr; +} + [[gnu::always_inline]] void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept { @@ -85,13 +101,16 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg return; } - log_info (LOG_GC, "cross references callback invoked with {} sccs and {} xrefs.", args->ComponentCount, args->CrossReferenceCount); + char message[128]; + snprintf (message, sizeof (message), "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount); + log_write (LOG_GC, LogLevel::Info, message); JNIEnv *env = OSBridge::ensure_jnienv (); for (size_t i = 0; i < args->ComponentCount; ++i) { const StronglyConnectedComponent &scc = args->Components [i]; - log_info (LOG_GC, "group {} with {} objects", i, scc.Count); + snprintf (message, sizeof (message), "group %zu with %zu objects", i, scc.Count); + log_write (LOG_GC, LogLevel::Info, message); for (size_t j = 0; j < scc.Count; ++j) { log_handle_context (env, scc.Contexts [j]); } @@ -104,7 +123,9 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg for (size_t i = 0; i < args->CrossReferenceCount; ++i) { size_t source_index = args->CrossReferences [i].SourceGroupIndex; size_t dest_index = args->CrossReferences [i].DestinationGroupIndex; - log_info_nocheck_fmt (LOG_GC, "xref [{}] {} -> {}", i, source_index, dest_index); + char xref_message[128]; + snprintf (xref_message, sizeof (xref_message), "xref [%zu] %zu -> %zu", i, source_index, dest_index); + log_write (LOG_GC, LogLevel::Info, xref_message); } } @@ -118,9 +139,13 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept jclass java_class = env->GetObjectClass (handle); if (java_class != nullptr) { char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - log_info (LOG_GC, "gref {:#x} [{}]", reinterpret_cast (handle), class_name); + char message[256]; + snprintf (message, sizeof (message), "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name)); + log_write (LOG_GC, LogLevel::Info, message); free (class_name); } else { - log_info (LOG_GC, "gref {:#x} [unknown class]", reinterpret_cast (handle)); + char message[128]; + snprintf (message, sizeof (message), "gref %p [unknown class]", reinterpret_cast(handle)); + log_write (LOG_GC, LogLevel::Info, message); } } diff --git a/src/native/clr/host/host-shared.cc b/src/native/clr/host/host-shared.cc index 5015a7a7a34..97b24668524 100644 --- a/src/native/clr/host/host-shared.cc +++ b/src/native/clr/host/host-shared.cc @@ -1,3 +1,5 @@ +#include + #include #include @@ -12,13 +14,15 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept -> JNIEnv *env = OSBridge::ensure_jnienv (); jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName)); if (name == nullptr) { - log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at {:p}", reinterpret_cast(klass)); + char message[128]; + snprintf (message, sizeof (message), "Failed to obtain Java class name for object at %p", reinterpret_cast(klass)); + log_write (LOG_DEFAULT, LogLevel::Error, message); return nullptr; } const char *mutf8 = env->GetStringUTFChars (name, nullptr); if (mutf8 == nullptr) { - log_error (LOG_DEFAULT, "Failed to convert Java class name to UTF8 (out of memory?)"sv); + log_write (LOG_DEFAULT, LogLevel::Error, "Failed to convert Java class name to UTF8 (out of memory?)"); env->DeleteLocalRef (name); return nullptr; } @@ -34,4 +38,4 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept -> } return ret; -} \ No newline at end of file +} diff --git a/src/native/clr/host/internal-pinvokes-shared.cc b/src/native/clr/host/internal-pinvokes-shared.cc index 813e6c64f90..e4fbc06c766 100644 --- a/src/native/clr/host/internal-pinvokes-shared.cc +++ b/src/native/clr/host/internal-pinvokes-shared.cc @@ -56,30 +56,36 @@ void monodroid_log (LogLevel level, LogCategories category, const char *message) switch (level) { case LogLevel::Verbose: case LogLevel::Debug: - log_debug_nocheck (category, std::string_view { message }); + if ((log_categories & category) != 0) { + log_write (category, LogLevel::Debug, message); + } break; case LogLevel::Info: - log_info_nocheck (category, std::string_view { message }); + if ((log_categories & category) != 0) { + log_write (category, LogLevel::Info, message); + } break; case LogLevel::Warn: case LogLevel::Silent: // warn is always printed - log_warn (category, std::string_view { message }); + log_write (category, LogLevel::Warn, message); break; case LogLevel::Error: - log_error (category, std::string_view { message }); + log_write (category, LogLevel::Error, message); break; case LogLevel::Fatal: - log_fatal (category, std::string_view { message }); + log_write (category, LogLevel::Fatal, message); break; default: case LogLevel::Unknown: case LogLevel::Default: - log_info_nocheck (category, std::string_view { message }); + if ((log_categories & category) != 0) { + log_write (category, LogLevel::Info, message); + } break; } } diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index cc2122e317e..2e04dadffef 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -1,4 +1,6 @@ -#include +#include +#include +#include #include #include @@ -8,6 +10,32 @@ using namespace xamarin::android; +namespace { + constexpr size_t LOG_LINE_BUFFER_SIZE = 512; + + void write_logcat_line (LogCategories category, const char *line, size_t line_len) noexcept + { + char local_buffer[LOG_LINE_BUFFER_SIZE]; + char *buffer = local_buffer; + + if (line_len >= sizeof (local_buffer)) { + buffer = static_cast(malloc (line_len + 1)); + if (buffer == nullptr) { + log_write (category, LogLevel::Debug, ""); + return; + } + } + + memcpy (buffer, line, line_len); + buffer[line_len] = '\0'; + log_write (category, LogLevel::Debug, buffer); + + if (buffer != local_buffer) { + free (buffer); + } + } +} + void OSBridge::initialize_on_onload (JavaVM *vm, JNIEnv *env) noexcept { abort_if_invalid_pointer_argument (env, "env"); @@ -90,38 +118,42 @@ auto OSBridge::_monodroid_weak_gref_dec () noexcept -> int void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategories category) noexcept { if (from == nullptr) [[unlikely]] { - log_warn (category, "Unable to write stack trace, managed runtime passed a NULL string."); + log_write (category, LogLevel::Warn, "Unable to write stack trace, managed runtime passed a NULL string."); return; } - const std::string_view trace { from }; - if (trace.empty ()) [[unlikely]] { - log_warn (category, "Empty stack trace passed by the managed runtime."); + if (*from == '\0') [[unlikely]] { + log_write (category, LogLevel::Warn, "Empty stack trace passed by the managed runtime."); return; } - for (const auto segment : std::views::split (trace, '\n')) { - const std::string_view line { segment }; + const char *line = from; + while (line != nullptr && *line != '\0') { + const char *newline = strchr (line, '\n'); + size_t line_len = newline == nullptr ? strlen (line) : static_cast(newline - line); if ((category == LOG_GREF && Logger::gref_to_logcat ()) || (category == LOG_LREF && Logger::lref_to_logcat ())) { - log_debug (category, "{}"sv, line); + write_logcat_line (category, line, line_len); } - if (to == nullptr) { - continue; + if (to != nullptr) { + fwrite (line, sizeof (char), line_len, to); + fputc ('\n', to); + fflush (to); } - fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to); - fputc ('\n', to); - fflush (to); + if (newline == nullptr) { + break; + } + line = newline + 1; } } void OSBridge::_monodroid_gref_log (const char *message) noexcept { if (Logger::gref_to_logcat ()) { - log_debug (LOG_GREF, "{}"sv, optional_string (message)); + log_write (LOG_GREF, LogLevel::Debug, optional_string (message)); } if (Logger::gref_log () == nullptr) { @@ -133,7 +165,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, const char *line, FILE *to, const char *const from, bool logcat_enabled) noexcept { log_write (category, LogLevel::Info, line); @@ -146,7 +178,7 @@ 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, sizeof (char), strlen (line), to); fputc ('\n', to); _write_stack_trace (to, from, category); @@ -161,8 +193,11 @@ 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, + char log_line[LOG_LINE_BUFFER_SIZE]; + snprintf ( + log_line, + sizeof (log_line), + "+g+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", c, wc, reinterpret_cast(curHandle), @@ -185,8 +220,11 @@ 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, + char log_line[LOG_LINE_BUFFER_SIZE]; + snprintf ( + log_line, + sizeof (log_line), + "-g- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", c, wc, reinterpret_cast(handle), @@ -206,8 +244,11 @@ 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, + char log_line[LOG_LINE_BUFFER_SIZE]; + snprintf ( + log_line, + sizeof (log_line), + "+w+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", gc, c, reinterpret_cast(curHandle), @@ -228,8 +269,11 @@ 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, + char log_line[LOG_LINE_BUFFER_SIZE]; + snprintf ( + log_line, + sizeof (log_line), + "+l+ lrefc %d handle %p/%c from thread '%s'(%d)", lrefc, reinterpret_cast(handle), type, @@ -248,8 +292,11 @@ 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, + char log_line[LOG_LINE_BUFFER_SIZE]; + snprintf ( + log_line, + sizeof (log_line), + "-w- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", gc, c, reinterpret_cast(handle), @@ -267,8 +314,11 @@ 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, + char log_line[LOG_LINE_BUFFER_SIZE]; + snprintf ( + log_line, + sizeof (log_line), + "-l- lrefc %d handle %p/%c from thread '%s'(%d)", lrefc, reinterpret_cast(handle), type, diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh index 0e0a9a6244c..c9f55b4fc66 100644 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ b/src/native/clr/include/host/bridge-processing-shared.hh @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include @@ -20,16 +19,26 @@ struct CrossReferenceTarget void mark_refs_added_if_needed () noexcept; }; +struct BridgeProcessingCallbacks +{ + void *context; + bool (*maybe_call_gc_user_peerable_add_managed_reference) (void *context, JNIEnv *env, jobject from, jobject to) noexcept; + bool (*maybe_call_gc_user_peerable_clear_managed_references) (void *context, JNIEnv *env, jobject handle) noexcept; +}; + class BridgeProcessingShared { public: - explicit BridgeProcessingShared (MarkCrossReferencesArgs *args) noexcept; + explicit BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *callbacks = nullptr) noexcept; + ~BridgeProcessingShared () noexcept; + static void initialize_on_runtime_init (JNIEnv *jniEnv, jclass runtimeClass) noexcept; void process () noexcept; private: JNIEnv* env; MarkCrossReferencesArgs *cross_refs; - std::unordered_map temporary_peers; + jobject *temporary_peers = nullptr; + BridgeProcessingCallbacks callbacks; static inline jclass GCUserPeer_class = nullptr; static inline jmethodID GCUserPeer_ctor = nullptr; @@ -50,6 +59,8 @@ private: void clear_references_if_needed (const HandleContext &context) noexcept; void clear_references (jobject handle) noexcept; + bool maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept; + bool maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept; void log_missing_add_references_method (jclass java_class) noexcept; void log_missing_clear_references_method (jclass java_class) noexcept; @@ -60,9 +71,4 @@ private: void log_gref_delete (jobject handle) noexcept; void log_weak_ref_delete (jobject weak) noexcept; void log_gc_summary () noexcept; - - // These methods must be implemented by every host individually - // Both methods below return `true` if they processed the call - virtual auto maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept -> bool = 0; - virtual auto maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept -> bool = 0; }; diff --git a/src/native/clr/include/host/bridge-processing.hh b/src/native/clr/include/host/bridge-processing.hh index 2d638c0e6ba..1a2ab64dd1d 100644 --- a/src/native/clr/include/host/bridge-processing.hh +++ b/src/native/clr/include/host/bridge-processing.hh @@ -10,15 +10,4 @@ public: explicit BridgeProcessing (MarkCrossReferencesArgs *args) noexcept : BridgeProcessingShared (args) {} - -private: - auto maybe_call_gc_user_peerable_add_managed_reference ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject from, [[maybe_unused]] jobject to) noexcept -> bool override final - { - return false; // no-op for CoreCLR, we didn't process the call - } - - auto maybe_call_gc_user_peerable_clear_managed_references ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject handle) noexcept -> bool override final - { - return false; // no-op for CoreCLR, we didn't process the call - } }; diff --git a/src/native/clr/include/host/gc-bridge.hh b/src/native/clr/include/host/gc-bridge.hh index 80c0ac68132..f9f4972e212 100644 --- a/src/native/clr/include/host/gc-bridge.hh +++ b/src/native/clr/include/host/gc-bridge.hh @@ -1,11 +1,9 @@ #pragma once -#include +#include +#include + #include -#include -#include -#include -#include #include @@ -72,8 +70,14 @@ namespace xamarin::android { GCBridge::bridge_processing_started_callback = bridge_processing_started; GCBridge::bridge_processing_finished_callback = bridge_processing_finished; - bridge_processing_thread = std::thread { GCBridge::bridge_processing }; - bridge_processing_thread.detach (); + int ret = sem_init (&shared_args_semaphore, 0, 0); + abort_unless (ret == 0, "Failed to initialize GC bridge semaphore"); + + ret = pthread_create (&bridge_processing_thread, nullptr, GCBridge::bridge_processing_thread_entry, nullptr); + abort_unless (ret == 0, "Failed to create GC bridge processing thread"); + + ret = pthread_detach (bridge_processing_thread); + abort_unless (ret == 0, "Failed to detach GC bridge processing thread"); return mark_cross_references; } @@ -81,10 +85,9 @@ namespace xamarin::android { static void trigger_java_gc (JNIEnv *env) noexcept; private: - static inline std::thread bridge_processing_thread {}; - - static inline std::binary_semaphore shared_args_semaphore{0}; - static inline std::atomic shared_args; + static inline pthread_t bridge_processing_thread {}; + static inline sem_t shared_args_semaphore {}; + static inline MarkCrossReferencesArgs *shared_args = nullptr; static inline jobject Runtime_instance = nullptr; static inline jmethodID Runtime_gc = nullptr; @@ -93,6 +96,7 @@ namespace xamarin::android { static inline BridgeProcessingFinishedFtn bridge_processing_finished_callback = nullptr; static void bridge_processing () noexcept; + static auto bridge_processing_thread_entry (void *arg) noexcept -> void*; static void mark_cross_references (MarkCrossReferencesArgs *args) noexcept; static void log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept; diff --git a/src/native/clr/include/host/host-environment.hh b/src/native/clr/include/host/host-environment.hh index 2c4dc95252d..f1a157cf736 100644 --- a/src/native/clr/include/host/host-environment.hh +++ b/src/native/clr/include/host/host-environment.hh @@ -42,7 +42,11 @@ namespace xamarin::android { static void set_system_property (const char *name, const char *value) noexcept { // TODO: should we **actually** try to set the system property here? Would that even work? Needs testing - log_debug (LOG_DEFAULT, " System property {} = '{}'", optional_string (name), optional_string (value)); + if ((log_categories & LOG_DEFAULT) != 0) { + char message[512]; + snprintf (message, sizeof (message), " System property %s = '%s'", optional_string (name), optional_string (value)); + log_write (LOG_DEFAULT, LogLevel::Debug, message); + } } [[gnu::flatten, gnu::always_inline]] @@ -88,10 +92,16 @@ namespace xamarin::android { static_local_string dir (home_len + relative_path.length ()); Util::path_combine (dir, home.get_string_view (), relative_path); - log_debug (LOG_DEFAULT, "Creating XDG directory: {}"sv, optional_string (dir.get ())); + if ((log_categories & LOG_DEFAULT) != 0) { + char message[512]; + snprintf (message, sizeof (message), "Creating XDG directory: %s", optional_string (dir.get ())); + log_write (LOG_DEFAULT, LogLevel::Debug, message); + } int rv = Util::create_directory (dir.get (), Constants::DEFAULT_DIRECTORY_MODE); if (rv < 0 && errno != EEXIST) { - log_warn (LOG_DEFAULT, "Failed to create XDG directory {}. {}"sv, optional_string (dir.get ()), strerror (errno)); + char message[512]; + snprintf (message, sizeof (message), "Failed to create XDG directory %s. %s", optional_string (dir.get ()), strerror (errno)); + log_write (LOG_DEFAULT, LogLevel::Warn, message); } if (!environment_variable_name.empty ()) { diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh index ee96c220671..2ba614fb78d 100644 --- a/src/native/clr/include/host/os-bridge.hh +++ b/src/native/clr/include/host/os-bridge.hh @@ -57,7 +57,7 @@ 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, const char *line, FILE *to, const char *const from, bool logcat_enabled) noexcept; private: static inline JavaVM *jvm = nullptr; diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh index b60871a93c4..a2c03962510 100644 --- a/src/native/clr/include/runtime-base/android-system.hh +++ b/src/native/clr/include/runtime-base/android-system.hh @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -18,6 +19,7 @@ struct BundledProperty; namespace xamarin::android { class AndroidSystem { +#if !defined(XA_HOST_NATIVEAOT) // This optimizes things a little bit. The array is allocated at build time, so we pay no cost for its // allocation and at run time it allows us to skip dynamic memory allocation. inline static std::array single_app_lib_directory{}; @@ -35,6 +37,7 @@ namespace xamarin::android { std::string_view { "x86_64" }, // CPU_KIND_X86_64 std::string_view { "riscv" }, // CPU_KIND_RISCV }; +#endif public: static auto get_gref_gc_threshold () noexcept -> long @@ -60,16 +63,28 @@ namespace xamarin::android { running_in_emulator = yesno; } +#if defined(XA_HOST_NATIVEAOT) + static auto get_primary_override_dir () noexcept -> const char* + { + return primary_override_dir; + } +#else static auto get_primary_override_dir () noexcept -> std::string const& { return primary_override_dir; } +#endif static void set_primary_override_dir (jstring_wrapper& home) noexcept { +#if defined(XA_HOST_NATIVEAOT) + determine_primary_override_dir (home, primary_override_dir, sizeof (primary_override_dir)); +#else primary_override_dir = determine_primary_override_dir (home); +#endif } +#if !defined(XA_HOST_NATIVEAOT) static auto get_native_libraries_dir () noexcept -> std::string const& { return native_libraries_dir; @@ -94,6 +109,7 @@ namespace xamarin::android { log_debug (LOG_DEFAULT, "Creating public update directory: `{}`", override_dir); Util::create_public_directory (override_dir); } +#endif static auto is_embedded_dso_mode_enabled () noexcept -> bool { @@ -129,6 +145,18 @@ namespace xamarin::android { embedded_dso_mode_enabled = yesno; } +#if defined(XA_HOST_NATIVEAOT) + static void determine_primary_override_dir (jstring_wrapper &home, char *buffer, size_t buffer_size) noexcept + { + dynamic_local_string name { home.get_cstr () }; + name.append ("/") + .append (Constants::OVERRIDE_DIRECTORY_NAME) + .append ("/") + .append (Constants::android_lib_abi); + + snprintf (buffer, buffer_size, "%s", name.get ()); + } +#else static auto determine_primary_override_dir (jstring_wrapper &home) noexcept -> std::string { dynamic_local_string name { home.get_cstr () }; @@ -139,16 +167,21 @@ namespace xamarin::android { return {name.get (), name.length ()}; } +#endif private: static inline long max_gref_count = 0; static inline bool running_in_emulator = false; static inline bool embedded_dso_mode_enabled = false; +#if defined(XA_HOST_NATIVEAOT) + static inline char primary_override_dir[SENSIBLE_PATH_MAX] {}; +#else static inline std::string primary_override_dir; static inline std::string native_libraries_dir; #if defined (DEBUG) static inline std::unordered_map bundled_properties; +#endif #endif }; } diff --git a/src/native/clr/include/runtime-base/util.hh b/src/native/clr/include/runtime-base/util.hh index 656a3d4a68a..3082eb90220 100644 --- a/src/native/clr/include/runtime-base/util.hh +++ b/src/native/clr/include/runtime-base/util.hh @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -139,7 +140,9 @@ namespace xamarin::android { { struct stat sbuf; if (fstatat (dirfd, file_name, &sbuf, 0) == -1) { - log_warn (LOG_ASSEMBLY, "Failed to stat file '{}': {}", file_name, std::strerror (errno)); + char message[512]; + snprintf (message, sizeof (message), "Failed to stat file '%s': %s", file_name, std::strerror (errno)); + log_write (LOG_ASSEMBLY, LogLevel::Warn, message); return std::nullopt; } @@ -154,9 +157,15 @@ namespace xamarin::android { [[gnu::flatten, gnu::always_inline]] static void set_environment_variable (const char *name, const char *value) noexcept { - log_debug (LOG_DEFAULT, "Setting environment variable {} = '{}'", optional_string (name), optional_string (value)); + if (should_log (LOG_DEFAULT)) { + char message[512]; + snprintf (message, sizeof (message), "Setting environment variable %s = '%s'", optional_string (name), optional_string (value)); + log_write (LOG_DEFAULT, LogLevel::Debug, message); + } if (::setenv (name, value, 1) < 0) { - log_warn (LOG_DEFAULT, "Failed to set environment variable '{}': {}", name, ::strerror (errno)); + char message[512]; + snprintf (message, sizeof (message), "Failed to set environment variable '%s': %s", optional_string (name), ::strerror (errno)); + log_write (LOG_DEFAULT, LogLevel::Warn, message); } } @@ -178,7 +187,9 @@ namespace xamarin::android { if (createDirectory) { int rv = create_directory (value.get_cstr (), mode); if (rv < 0 && errno != EEXIST) { - log_warn (LOG_DEFAULT, "Failed to create directory '{}' for environment variable '{}'. {}", value.get_string_view (), name, strerror (errno)); + char message[512]; + snprintf (message, sizeof (message), "Failed to create directory '%s' for environment variable '%s'. %s", optional_string (value.get_cstr ()), name.data (), strerror (errno)); + log_write (LOG_DEFAULT, LogLevel::Warn, message); } } set_environment_variable (name, value); @@ -208,33 +219,32 @@ namespace xamarin::android { mmap_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast(offsetPage)); if (mmap_info.area == MAP_FAILED) { - Helpers::abort_application ( - LOG_ASSEMBLY, - std::format ( - "Could not mmap APK fd {}: {}; File={}", - fd, - strerror (errno), - filename - ) - ); + char message[512]; + snprintf (message, sizeof (message), "Could not mmap APK fd %d: %s; File=%s", fd, strerror (errno), filename.data ()); + Helpers::abort_application (LOG_ASSEMBLY, message); } mmap_info.size = offsetSize; file_info.area = pointer_add (mmap_info.area, offsetFromPage); file_info.size = size; - log_info ( - LOG_ASSEMBLY, - " mmap_start: {:<8p}; mmap_end: {:<8p} mmap_len: {:<12} file_start: {:<8p} file_end: {:<8p} file_len: {:<12} apk descriptor: {} file: {}", - mmap_info.area, - pointer_add (mmap_info.area, mmap_info.size), - mmap_info.size, - file_info.area, - pointer_add (file_info.area, file_info.size), - file_info.size, - fd, - filename - ); + if (should_log (LOG_ASSEMBLY)) { + char message[512]; + snprintf ( + message, + sizeof (message), + " mmap_start: %-8p; mmap_end: %-8p mmap_len: %-12zu file_start: %-8p file_end: %-8p file_len: %-12zu apk descriptor: %d file: %s", + mmap_info.area, + pointer_add (mmap_info.area, mmap_info.size), + mmap_info.size, + file_info.area, + pointer_add (file_info.area, file_info.size), + file_info.size, + fd, + filename.data () + ); + log_write (LOG_ASSEMBLY, LogLevel::Info, message); + } return file_info; } @@ -256,7 +266,11 @@ namespace xamarin::android { elf_header->e_ident[EI_MAG1] != ELFMAG1 || elf_header->e_ident[EI_MAG2] != ELFMAG2 || elf_header->e_ident[EI_MAG3] != ELFMAG3) { - log_debug (LOG_ASSEMBLY, "Not an ELF image: {}", file_name); + if (should_log (LOG_ASSEMBLY)) { + char message[512]; + snprintf (message, sizeof (message), "Not an ELF image: %s", file_name.data ()); + log_write (LOG_ASSEMBLY, LogLevel::Debug, message); + } // Not an ELF image, just return what we mmapped before return { map_info.area, map_info.size }; } diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh index 7aef6e20456..9ecef42a0ce 100644 --- a/src/native/clr/include/shared/log_types.hh +++ b/src/native/clr/include/shared/log_types.hh @@ -1,8 +1,11 @@ #pragma once #include +#if !defined(XA_HOST_NATIVEAOT) #include #include +#include +#endif #include #include "java-interop-logger.h" @@ -17,12 +20,22 @@ #undef log_info #endif +#if defined(XA_HOST_NATIVEAOT) +#define DO_LOG_FMT(_level, _category_, _fmt_, ...) \ + do { \ + static_assert (sizeof (#__VA_ARGS__) == 1, "NativeAOT logging must use preformatted messages"); \ + if ((log_categories & ((_category_))) != 0) { \ + ::log_ ## _level ## _nocheck ((_category_), _fmt_); \ + } \ + } while (0) +#else #define DO_LOG_FMT(_level, _category_, _fmt_, ...) \ do { \ if ((log_categories & ((_category_))) != 0) { \ ::log_ ## _level ## _nocheck_fmt ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \ } \ } while (0) +#endif // // For std::format spec, see https://en.cppreference.com/w/cpp/utility/format/spec @@ -54,13 +67,16 @@ namespace xamarin::android { log_write (category, level, message.data ()); } +#if !defined(XA_HOST_NATIVEAOT) template [[gnu::always_inline]] static inline constexpr void log_write_fmt (LogCategories category, LogLevel level, std::format_string fmt, Args&& ...args) { log_write (category, level, std::format (fmt, std::forward(args)...).c_str ()); } +#endif } +#if !defined(XA_HOST_NATIVEAOT) template [[gnu::always_inline]] static inline constexpr void log_debug_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args) { @@ -120,5 +136,36 @@ static inline constexpr void log_fatal_fmt (LogCategories category, std::string_ { log_write (category, xamarin::android::LogLevel::Fatal, message.data ()); } +#else +[[gnu::always_inline]] +static inline constexpr void log_debug_nocheck (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Debug, message.data ()); +} + +[[gnu::always_inline]] +static inline constexpr void log_info_nocheck (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Info, message.data ()); +} + +[[gnu::always_inline]] +static inline constexpr void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Warn, message.data ()); +} + +[[gnu::always_inline]] +static inline constexpr void log_error_fmt (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Error, message.data ()); +} + +[[gnu::always_inline]] +static inline constexpr void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept +{ + log_write (category, xamarin::android::LogLevel::Fatal, message.data ()); +} +#endif extern unsigned int log_categories; diff --git a/src/native/clr/runtime-base/android-system-shared.cc b/src/native/clr/runtime-base/android-system-shared.cc index 2aa1d54b001..c7ef49464ce 100644 --- a/src/native/clr/runtime-base/android-system-shared.cc +++ b/src/native/clr/runtime-base/android-system-shared.cc @@ -1,3 +1,6 @@ +#include +#include +#include #include #include @@ -36,15 +39,18 @@ AndroidSystem::monodroid__system_property_get (std::string_view const& name, cha char *buf = nullptr; if (sp_value_len < Constants::PROPERTY_VALUE_BUFFER_LEN) { size_t alloc_size = Helpers::add_with_overflow_check (Constants::PROPERTY_VALUE_BUFFER_LEN, 1uz); - log_warn (LOG_DEFAULT, "Buffer to store system property may be too small, will copy only {} bytes", sp_value_len); - buf = new char [alloc_size]; + char message[128]; + snprintf (message, sizeof (message), "Buffer to store system property may be too small, will copy only %zu bytes", sp_value_len); + log_write (LOG_DEFAULT, LogLevel::Warn, message); + buf = static_cast (std::malloc (alloc_size)); + abort_unless (buf != nullptr, "Failed to allocate system property buffer"); } int len = __system_property_get (name.data (), buf ? buf : sp_value); if (buf != nullptr) { strncpy (sp_value, buf, sp_value_len); sp_value [sp_value_len] = '\0'; - delete[] buf; + std::free (buf); } return len; @@ -81,10 +87,14 @@ AndroidSystem::get_max_gref_count_from_system () noexcept -> long } if (*e) { - log_warn (LOG_GC, "Unsupported '{}' value '{}'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); + char message[256]; + snprintf (message, sizeof (message), "Unsupported '%s' value '%s'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); + log_write (LOG_GC, LogLevel::Warn, message); } - log_warn (LOG_GC, "Overriding max JNI Global Reference count to {}", max); + char message[128]; + snprintf (message, sizeof (message), "Overriding max JNI Global Reference count to %ld", max); + log_write (LOG_GC, LogLevel::Warn, message); } return max; diff --git a/src/native/clr/runtime-base/logger.cc b/src/native/clr/runtime-base/logger.cc index a61b68d4a76..369947ee4d2 100644 --- a/src/native/clr/runtime-base/logger.cc +++ b/src/native/clr/runtime-base/logger.cc @@ -1,10 +1,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -22,10 +22,17 @@ using namespace xamarin::android; using std::operator""sv; namespace { - std::string gref_file{}; - std::string lref_file{}; + char *gref_file = nullptr; + char *lref_file = nullptr; bool light_gref = false; bool light_lref = false; + + void set_log_file (char *&log_file, const char *path) noexcept + { + std::free (log_file); + log_file = path == nullptr ? nullptr : strdup (path); + abort_unless (path == nullptr || log_file != nullptr, "Failed to allocate reference log file path"); + } } [[gnu::always_inline]] @@ -52,7 +59,11 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p { auto log_and_return = [&category](FILE *f, std::string_view const& path) -> FILE* { if (f != nullptr) { - log_debug (category, "Opened file '{}' for logging.", path); + if ((log_categories & category) != 0) { + char message[512]; + snprintf (message, sizeof (message), "Opened file '%s' for logging.", path.data ()); + log_write (category, LogLevel::Debug, message); + } } return f; }; @@ -62,28 +73,28 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p return log_and_return (ret, custom_path); } - std::string p{}; Util::create_public_directory (override_dir); - p.assign (override_dir); - p.append ("/"); - p.append (fallback_filename); + dynamic_local_string p; + p.append (override_dir) + .append ("/") + .append (fallback_filename); - return log_and_return (open_file (p), p); + return log_and_return (open_file (p.get ()), p.get ()); } void Logger::init_reference_logging (std::string_view const& override_dir) noexcept { if ((log_categories & LOG_GREF) != 0 && !light_gref) { - _gref_log = open_file (LOG_GREF, gref_file, override_dir, "grefs.txt"sv); + _gref_log = open_file (LOG_GREF, gref_file == nullptr ? std::string_view {} : std::string_view { gref_file }, override_dir, "grefs.txt"sv); } if ((log_categories & LOG_LREF) != 0 && !light_lref) { // if both lref & gref have files specified, and they're the same path, reuse the FILE*. - if (!lref_file.empty () && strcmp (lref_file.c_str (), !gref_file.empty () ? gref_file.c_str () : "") == 0) { + if (lref_file != nullptr && strcmp (lref_file, gref_file != nullptr ? gref_file : "") == 0) { _lref_log = _gref_log; } else { - _lref_log = open_file (LOG_LREF, lref_file, override_dir, "lrefs.txt"sv); + _lref_log = open_file (LOG_LREF, lref_file == nullptr ? std::string_view {} : std::string_view { lref_file }, override_dir, "lrefs.txt"sv); } } } @@ -162,7 +173,18 @@ Logger::init_logging_categories () noexcept auto file_name = segment.at (offset); if (!file_name.has_value ()) { - log_warn (LOG_DEFAULT, "Unable to set path to {} log file: {}", file_kind, to_string (file_name.error ())); + char message[256]; + std::string_view error = to_string (file_name.error ()); + snprintf ( + message, + sizeof (message), + "Unable to set path to %.*s log file: %.*s", + static_cast(file_kind.length ()), + file_kind.data (), + static_cast(error.length ()), + error.data () + ); + log_write (LOG_DEFAULT, LogLevel::Warn, message); return nullptr; } @@ -171,7 +193,7 @@ Logger::init_logging_categories () noexcept constexpr std::string_view CAT_GREF_EQUALS { "gref=" }; if (set_category (CAT_GREF_EQUALS, param, LOG_GREF, true /* arg_starts_with_name */)) { - gref_file = get_log_file_name ("gref"sv, param, CAT_GREF_EQUALS.length ()); + set_log_file (gref_file, get_log_file_name ("gref"sv, param, CAT_GREF_EQUALS.length ())); continue; } @@ -187,7 +209,7 @@ Logger::init_logging_categories () noexcept constexpr std::string_view CAT_LREF_EQUALS { "lref=" }; if (set_category (CAT_LREF_EQUALS, param, LOG_LREF, true /* arg_starts_with_name */)) { - lref_file = get_log_file_name ("lref"sv, param, CAT_LREF_EQUALS.length ()); + set_log_file (lref_file, get_log_file_name ("lref"sv, param, CAT_LREF_EQUALS.length ())); continue; } diff --git a/src/native/clr/runtime-base/util.cc b/src/native/clr/runtime-base/util.cc index 8f59681a55f..0f1c8799376 100644 --- a/src/native/clr/runtime-base/util.cc +++ b/src/native/clr/runtime-base/util.cc @@ -1,4 +1,5 @@ #include +#include #include #include @@ -58,7 +59,9 @@ Util::create_public_directory (std::string_view const& dir) // Try to change the mode, just in case chmod (dir.data (), 0777); } else { - log_warn (LOG_DEFAULT, "Failed to create directory '{}'. {}"sv, dir, std::strerror (errno)); + char message[512]; + snprintf (message, sizeof (message), "Failed to create directory '%s'. %s", dir.data (), std::strerror (errno)); + log_write (LOG_DEFAULT, LogLevel::Warn, message); } } umask (m); @@ -72,7 +75,9 @@ Util::monodroid_fopen (std::string_view const& filename, std::string_view const& */ FILE *ret = fopen (filename.data (), mode.data ()); if (ret == nullptr) { - log_error (LOG_DEFAULT, "fopen failed for file {}: {}", filename, strerror (errno)); + char message[512]; + snprintf (message, sizeof (message), "fopen failed for file %s: %s", filename.data (), strerror (errno)); + log_write (LOG_DEFAULT, LogLevel::Error, message); return nullptr; } @@ -87,7 +92,9 @@ void Util::set_world_accessable (std::string_view const& path) } while (r == -1 && errno == EINTR); if (r == -1) { - log_error (LOG_DEFAULT, "chmod(\"{}\", 0664) failed: {}", path, strerror (errno)); + char message[512]; + snprintf (message, sizeof (message), "chmod(\"%s\", 0664) failed: %s", path.data (), strerror (errno)); + log_write (LOG_DEFAULT, LogLevel::Error, message); } } @@ -99,7 +106,9 @@ auto Util::set_world_accessible (int fd) noexcept -> bool } while (r == -1 && errno == EINTR); if (r == -1) { - log_error (LOG_DEFAULT, "fchmod() failed: {}"sv, strerror (errno)); + char message[128]; + snprintf (message, sizeof (message), "fchmod() failed: %s", strerror (errno)); + log_write (LOG_DEFAULT, LogLevel::Error, message); return false; } diff --git a/src/native/clr/shared/helpers.cc b/src/native/clr/shared/helpers.cc index 7850592af5a..69c0f6812b8 100644 --- a/src/native/clr/shared/helpers.cc +++ b/src/native/clr/shared/helpers.cc @@ -1,4 +1,5 @@ #include +#include #include #include @@ -11,7 +12,7 @@ using namespace xamarin::android; 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,14 +34,17 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo } } - log_fatal ( - category, - "Abort at {}:{}:{} ('{}')", + char location_message[512]; + snprintf ( + location_message, + sizeof (location_message), + "Abort at %s:%u:%u ('%s')", file_name, sloc.line (), sloc.column (), sloc.function_name () ); + log_write (category, LogLevel::Fatal, location_message); } std::abort (); } diff --git a/src/native/common/include/runtime-base/jni-wrappers.hh b/src/native/common/include/runtime-base/jni-wrappers.hh index 679aed3df59..2978e6e1d33 100644 --- a/src/native/common/include/runtime-base/jni-wrappers.hh +++ b/src/native/common/include/runtime-base/jni-wrappers.hh @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -162,7 +163,11 @@ namespace xamarin::android if (_arr != nullptr) { len = static_cast(_env->GetArrayLength (_arr)); if (len > sizeof (static_wrappers) / sizeof (jstring_wrapper)) { - wrappers = new jstring_wrapper [len]; + wrappers = static_cast (std::malloc (len * sizeof (jstring_wrapper))); + abort_unless (wrappers != nullptr, "Failed to allocate jstring array wrappers"); + for (size_t i = 0; i < len; i++) { + ::new (static_cast(&wrappers [i])) jstring_wrapper (); + } } else { wrappers = static_wrappers; } @@ -175,7 +180,10 @@ namespace xamarin::android ~jstring_array_wrapper () noexcept { if (wrappers != nullptr && wrappers != static_wrappers) { - delete[] wrappers; + for (size_t i = 0; i < len; i++) { + wrappers [i].~jstring_wrapper (); + } + std::free (wrappers); } } diff --git a/src/native/common/include/runtime-base/strings.hh b/src/native/common/include/runtime-base/strings.hh index 8e6bf8cbaea..561b558d7ad 100644 --- a/src/native/common/include/runtime-base/strings.hh +++ b/src/native/common/include/runtime-base/strings.hh @@ -1,10 +1,11 @@ #pragma once -#include #include #include #include #include +#include +#include #include #include #include @@ -205,7 +206,9 @@ namespace xamarin::android { } if (!can_access (start_index)) { - log_error (LOG_DEFAULT, "Cannot convert string to integer, index {} is out of range", start_index); + char message[128]; + snprintf (message, sizeof (message), "Cannot convert string to integer, index %zu is out of range", start_index); + log_write (LOG_DEFAULT, LogLevel::Error, message); return false; } @@ -229,17 +232,23 @@ namespace xamarin::android { } if (out_of_range || errno == ERANGE) { - log_error (LOG_DEFAULT, "Value {} is out of range of this type ({}..{})", reinterpret_cast(s), static_cast(min), static_cast(max)); + char message[256]; + snprintf (message, sizeof (message), "Value %s is out of range of this type (%lld..%llu)", reinterpret_cast(s), static_cast(min), static_cast(max)); + log_write (LOG_DEFAULT, LogLevel::Error, message); return false; } if (endp == s) { - log_error (LOG_DEFAULT, "Value {} does not represent a base {} integer", reinterpret_cast(s), base); + char message[256]; + snprintf (message, sizeof (message), "Value %s does not represent a base %d integer", reinterpret_cast(s), base); + log_write (LOG_DEFAULT, LogLevel::Error, message); return false; } if (*endp != '\0') { - log_error (LOG_DEFAULT, "Value {} has non-numeric characters at the end", reinterpret_cast(s)); + char message[256]; + snprintf (message, sizeof (message), "Value %s has non-numeric characters at the end", reinterpret_cast(s)); + log_write (LOG_DEFAULT, LogLevel::Error, message); return false; } @@ -274,9 +283,6 @@ namespace xamarin::android { template class local_storage { - protected: - using LocalStoreArray = std::array; - public: static constexpr bool has_resize = HasResize; @@ -287,19 +293,19 @@ namespace xamarin::android { init_store (size < MaxStackSize ? MaxStackSize : size); } - virtual ~local_storage () + ~local_storage () { free_store (); } auto get () noexcept -> T* { - return allocated_store == nullptr ? local_store.data () : allocated_store; + return allocated_store == nullptr ? local_store : allocated_store; } auto get () const noexcept -> const T* { - return allocated_store == nullptr ? local_store.data () : allocated_store; + return allocated_store == nullptr ? local_store : allocated_store; } auto size () const noexcept -> size_t @@ -312,7 +318,8 @@ namespace xamarin::android { void init_store (size_t new_size) noexcept { if (new_size > MaxStackSize) { - allocated_store = new T[new_size]; + allocated_store = static_cast (std::malloc (new_size * sizeof (T))); + abort_unless (allocated_store != nullptr, "Failed to allocate local string storage"); } else { allocated_store = nullptr; } @@ -326,11 +333,12 @@ namespace xamarin::android { return; } - delete[] allocated_store; + std::free (allocated_store); + allocated_store = nullptr; } [[gnu::always_inline]] - auto get_local_store () noexcept -> LocalStoreArray& + auto get_local_store () noexcept -> T* { return local_store; } @@ -343,7 +351,7 @@ namespace xamarin::android { private: size_t store_size; - LocalStoreArray local_store; + T local_store[MaxStackSize]; T* allocated_store; }; @@ -416,11 +424,11 @@ namespace xamarin::android { T* new_allocated_store = base::get_allocated_store (); if (old_allocated_store != nullptr) { std::memcpy (new_allocated_store, old_allocated_store, old_size); - delete[] old_allocated_store; + std::free (old_allocated_store); return; } - std::memcpy (new_allocated_store, base::get_local_store ().data (), MaxStackSize); + std::memcpy (new_allocated_store, base::get_local_store (), MaxStackSize); } }; diff --git a/src/native/common/include/runtime-base/timing-internal.hh b/src/native/common/include/runtime-base/timing-internal.hh index 31099b86322..4d52be9c334 100644 --- a/src/native/common/include/runtime-base/timing-internal.hh +++ b/src/native/common/include/runtime-base/timing-internal.hh @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -260,7 +261,9 @@ namespace xamarin::android { // likely we'll run out of memory way, way, way before that happens size_t old_size = events.capacity (); events.reserve (old_size << 1); - log_warn (LOG_TIMING, "Reallocated timing event buffer from {} to {}"sv, old_size, events.size ()); + char message[128]; + snprintf (message, sizeof (message), "Reallocated timing event buffer from %zu to %zu", old_size, events.size ()); + log_write (LOG_TIMING, LogLevel::Warn, message); } } @@ -378,7 +381,9 @@ namespace xamarin::android { { struct timespec t; if (clock_gettime (CLOCK_MONOTONIC_RAW, &t) != 0) [[unlikely]] { - log_warn (LOG_TIMING, "clock_gettime failed for CLOCK_MONOTONIC_RAW: {}"sv, optional_string (strerror (errno))); + char message[128]; + snprintf (message, sizeof (message), "clock_gettime failed for CLOCK_MONOTONIC_RAW: %s", optional_string (strerror (errno))); + log_write (LOG_TIMING, LogLevel::Warn, message); return {}; // Results will be nonsensical, but no point in aborting the app } return time_point (chrono::seconds (t.tv_sec) + chrono::nanoseconds (t.tv_nsec)); @@ -494,11 +499,9 @@ namespace xamarin::android { return; } - log_warn ( - LOG_TIMING, - "Unknown event kind '{}' logged"sv, - static_cast>(kind) - ); + char warning[128]; + snprintf (warning, sizeof (warning), "Unknown event kind '%u' logged", static_cast(kind)); + log_write (LOG_TIMING, LogLevel::Warn, warning); append_desc ("unknown event kind"sv); } @@ -510,7 +513,9 @@ namespace xamarin::android { auto is_valid_event_index (size_t index, std::source_location sloc = std::source_location::current ()) const noexcept -> bool { if (index >= events.capacity ()) [[unlikely]] { - log_warn (LOG_TIMING, "Invalid event index passed to method '{}'"sv, sloc.function_name ()); + char message[256]; + snprintf (message, sizeof (message), "Invalid event index passed to method '%s'", sloc.function_name ()); + log_write (LOG_TIMING, LogLevel::Warn, message); return false; } diff --git a/src/native/common/include/runtime-base/timing.hh b/src/native/common/include/runtime-base/timing.hh index 6e883c240f8..d6d60246096 100644 --- a/src/native/common/include/runtime-base/timing.hh +++ b/src/native/common/include/runtime-base/timing.hh @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -83,17 +84,22 @@ namespace xamarin::android return; } - using namespace std::literals; auto interval = seq->end - seq->start; // nanoseconds - auto text = std::format ( - "{}; elapsed: {}:{}::{}"sv, - message == nullptr ? ""sv : message, - static_cast((std::chrono::duration_cast(interval).count ())), - static_cast((std::chrono::duration_cast(interval)).count ()), - static_cast((interval % 1ms).count ()) + auto seconds = static_cast((std::chrono::duration_cast(interval).count ())); + auto milliseconds = static_cast((std::chrono::duration_cast(interval)).count ()); + auto nanoseconds = static_cast((interval % std::chrono::milliseconds (1)).count ()); + char text[256]; + snprintf ( + text, + sizeof (text), + "%s; elapsed: %llu:%llu::%llu", + message == nullptr ? "" : message, + static_cast(seconds), + static_cast(milliseconds), + static_cast(nanoseconds) ); - log_write (LOG_TIMING, level, text.c_str ()); + log_write (LOG_TIMING, level, text); } private: diff --git a/src/native/common/include/shared/cpp-util.hh b/src/native/common/include/shared/cpp-util.hh index 6693ac69793..ebdc4d4d720 100644 --- a/src/native/common/include/shared/cpp-util.hh +++ b/src/native/common/include/shared/cpp-util.hh @@ -6,12 +6,9 @@ #include #include #include -#include #include -#include #include #include -#include #include #include @@ -33,49 +30,6 @@ namespace xamarin::android::detail { return ret == -1 ? "Out of memory" : message; } - [[gnu::always_inline]] - static inline std::string get_function_name (const char *signature) - { - using std::operator""sv; - - std::string_view sig { signature }; - if (sig.length () == 0) { - return ""; - } - - auto splitSignature = sig | std::views::split ("::"sv) | std::ranges::to> (); - - std::string ret; - if (splitSignature.size () > 1) { - ret.append (splitSignature [splitSignature.size () - 2]); - ret.append ("::"sv); - } - std::string_view func_name { splitSignature[splitSignature.size () - 1] }; - std::string_view::size_type args_pos = func_name.find ('('); - std::string_view::size_type name_start_pos = func_name.find (' '); - - if (name_start_pos == std::string_view::npos) { - name_start_pos = 0; - } else { - name_start_pos++; // point to after the space which separates return type from name - if (name_start_pos >= func_name.length ()) [[unlikely]] { - name_start_pos = 0; - } - } - - if (args_pos == std::string_view::npos) { - ret.append (func_name.substr (name_start_pos)); - } else { - // If there's a snafu with positions, start from 0 - if (name_start_pos >= args_pos || name_start_pos > func_name.length ()) [[unlikely]] { - name_start_pos = 0; - } - - ret.append (func_name.substr (name_start_pos, args_pos - name_start_pos)); - } - - return ret; - } } template F> @@ -110,11 +64,11 @@ abort_if_invalid_pointer_argument (T *ptr, const char *ptr_name, std::source_loc abort_unless ( ptr != nullptr, [&ptr_name, &sloc] { - return xamarin::android::detail::_format_message ( - "%s: parameter '%s' must be a valid pointer", - xamarin::android::detail::get_function_name (sloc.function_name ()).c_str (), - ptr_name - ); + return xamarin::android::detail::_format_message ( + "%s: parameter '%s' must be a valid pointer", + sloc.function_name (), + ptr_name + ); }, sloc ); @@ -127,11 +81,11 @@ abort_if_negative_integer_argument (int arg, const char *arg_name, std::source_l abort_unless ( arg > 0, [&arg_name, &sloc] { - return xamarin::android::detail::_format_message ( - "%s: parameter '%s' must be a positive integer", - xamarin::android::detail::get_function_name (sloc.function_name ()).c_str (), - arg_name - ); + return xamarin::android::detail::_format_message ( + "%s: parameter '%s' must be a positive integer", + sloc.function_name (), + arg_name + ); }, sloc ); @@ -142,7 +96,9 @@ abort_if_negative_integer_argument (int arg, const char *arg_name, std::source_l [[gnu::always_inline]] inline void pd_log_location (std::source_location sloc = std::source_location::current ()) noexcept { - log_warn (LOG_DEFAULT, "loc: {}:{} ('{}')", sloc.file_name (), sloc.line (), sloc.function_name ()); + char message[512]; + snprintf (message, sizeof (message), "loc: %s:%u ('%s')", sloc.file_name (), sloc.line (), sloc.function_name ()); + __android_log_write (ANDROID_LOG_WARN, "monodroid", message); } namespace xamarin::android diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 9f4ce16fb25..e072da41620 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -28,6 +28,7 @@ set(CLR_SOURCES_PATH "../../clr") set(XAMARIN_MONODROID_SOURCES bridge-processing.cc + cxx-shims.cc host.cc host-environment.cc host-jni.cc diff --git a/src/native/nativeaot/host/bridge-processing.cc b/src/native/nativeaot/host/bridge-processing.cc index b87a49b431b..4b6d77deb8a 100644 --- a/src/native/nativeaot/host/bridge-processing.cc +++ b/src/native/nativeaot/host/bridge-processing.cc @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -6,13 +6,23 @@ using namespace xamarin::android; +const BridgeProcessingCallbacks BridgeProcessing::bridge_processing_callbacks { + .context = nullptr, + .maybe_call_gc_user_peerable_add_managed_reference = &BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference, + .maybe_call_gc_user_peerable_clear_managed_references = &BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references, +}; + +BridgeProcessing::BridgeProcessing (MarkCrossReferencesArgs *args) noexcept + : BridgeProcessingShared (args, &bridge_processing_callbacks) +{} + void BridgeProcessing::naot_initialize_on_runtime_init (JNIEnv *env) noexcept { GCUserPeerable_class = env->FindClass ("net/dot/jni/GCUserPeerable"); if (GCUserPeerable_class == nullptr) [[unlikely]] { Helpers::abort_application ( LOG_DEFAULT, - "Failed to find net/dot/jni/GCUserPeerable class while initializing GC bridge processing."sv + "Failed to find net/dot/jni/GCUserPeerable class while initializing GC bridge processing." ); } @@ -21,21 +31,25 @@ 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; + constexpr char ABSENT[] = "absent"; + constexpr char PRESENT[] = "present"; + char message[128]; + snprintf ( + message, + sizeof (message), + "Failed to find GCUserPeerable method(s): jiAddManagedReference (%s); jiClearManagedReferences (%s)", + GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT, + GCUserPeerable_jiClearManagedReferences == nullptr ? ABSENT : PRESENT + ); Helpers::abort_application ( LOG_DEFAULT, - std::format ( - "Failed to find GCUserPeerable method(s): jiAddManagedReference ({}); jiClearManagedReferences ({})"sv, - GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT, - GCUserPeerable_jiClearManagedReferences == nullptr ? ABSENT : PRESENT - ) + message ); } } -auto BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept -> bool +bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference ([[maybe_unused]] void *context, JNIEnv *env, jobject from, jobject to) noexcept { if (!env->IsInstanceOf (from, GCUserPeerable_class)) { return false; @@ -45,7 +59,7 @@ auto BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference (JNIEnv return true; } -auto BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept -> bool +bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references ([[maybe_unused]] void *context, JNIEnv *env, jobject handle) noexcept { if (!env->IsInstanceOf (handle, GCUserPeerable_class)) { return false; diff --git a/src/native/nativeaot/host/cxx-shims.cc b/src/native/nativeaot/host/cxx-shims.cc new file mode 100644 index 00000000000..79868018251 --- /dev/null +++ b/src/native/nativeaot/host/cxx-shims.cc @@ -0,0 +1,62 @@ +#include +#include + +#include + +namespace std { + const nothrow_t nothrow {}; +} + +static void *allocate (size_t size) noexcept +{ + return malloc (size == 0 ? 1 : size); +} + +static void *allocate_or_abort (size_t size) noexcept +{ + void *ret = allocate (size); + if (ret == nullptr) { + abort (); + } + return ret; +} + +void *operator new (size_t size) +{ + return allocate_or_abort (size); +} + +void *operator new[] (size_t size) +{ + return allocate_or_abort (size); +} + +void *operator new (size_t size, std::nothrow_t const&) noexcept +{ + return allocate (size); +} + +void *operator new[] (size_t size, std::nothrow_t const&) noexcept +{ + return allocate (size); +} + +void operator delete (void *ptr) noexcept +{ + free (ptr); +} + +void operator delete[] (void *ptr) noexcept +{ + free (ptr); +} + +void operator delete (void *ptr, size_t) noexcept +{ + free (ptr); +} + +void operator delete[] (void *ptr, size_t) noexcept +{ + free (ptr); +} diff --git a/src/native/nativeaot/host/host.cc b/src/native/nativeaot/host/host.cc index b6d87483c78..c3714e0ac8d 100644 --- a/src/native/nativeaot/host/host.cc +++ b/src/native/nativeaot/host/host.cc @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -33,12 +35,17 @@ 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_ASSEMBLY, - "Calling JNI on-load init func '{}' ({:p})", - optional_string (__jni_on_load_handler_names[i]), - reinterpret_cast(__jni_on_load_handlers[i]) - ); + if ((log_categories & LOG_ASSEMBLY) != 0) { + char message[256]; + snprintf ( + message, + sizeof (message), + "Calling JNI on-load init func '%s' (%p)", + optional_string (__jni_on_load_handler_names[i]), + reinterpret_cast(__jni_on_load_handlers[i]) + ); + log_write (LOG_ASSEMBLY, LogLevel::Debug, message); + } __jni_on_load_handlers[i] (vm, reserved); } } diff --git a/src/native/nativeaot/host/internal-pinvoke-stubs.cc b/src/native/nativeaot/host/internal-pinvoke-stubs.cc index 1c59786a9b1..b33316c1e4f 100644 --- a/src/native/nativeaot/host/internal-pinvoke-stubs.cc +++ b/src/native/nativeaot/host/internal-pinvoke-stubs.cc @@ -5,13 +5,12 @@ using namespace xamarin::android; namespace { [[gnu::noreturn]] - void pinvoke_unreachable (std::source_location sloc = std::source_location::current ()) + void pinvoke_unreachable () { Helpers::abort_application ( LOG_DEFAULT, - "The p/invoke is not implemented. This is a stub and should not be called."sv, - true, // log_location - sloc + "The p/invoke is not implemented. This is a stub and should not be called.", + false // log_location ); } } diff --git a/src/native/nativeaot/include/host/bridge-processing.hh b/src/native/nativeaot/include/host/bridge-processing.hh index 17f533aae0b..9e473107520 100644 --- a/src/native/nativeaot/include/host/bridge-processing.hh +++ b/src/native/nativeaot/include/host/bridge-processing.hh @@ -7,17 +7,16 @@ class BridgeProcessing final : public BridgeProcessingShared { public: - explicit BridgeProcessing (MarkCrossReferencesArgs *args) noexcept - : BridgeProcessingShared (args) - {} + explicit BridgeProcessing (MarkCrossReferencesArgs *args) noexcept; static void naot_initialize_on_runtime_init (JNIEnv *env) noexcept; private: - auto maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept -> bool override final; - auto maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept -> bool override final; + static bool maybe_call_gc_user_peerable_add_managed_reference ([[maybe_unused]] void *context, JNIEnv *env, jobject from, jobject to) noexcept; + static bool maybe_call_gc_user_peerable_clear_managed_references ([[maybe_unused]] void *context, JNIEnv *env, jobject handle) noexcept; private: + static const BridgeProcessingCallbacks bridge_processing_callbacks; static inline jclass GCUserPeerable_class = nullptr; static inline jmethodID GCUserPeerable_jiAddManagedReference = nullptr; static inline jmethodID GCUserPeerable_jiClearManagedReferences = nullptr; diff --git a/src/native/nativeaot/include/host/host.hh b/src/native/nativeaot/include/host/host.hh new file mode 100644 index 00000000000..c582b598bd4 --- /dev/null +++ b/src/native/nativeaot/include/host/host.hh @@ -0,0 +1,3 @@ +#pragma once + +#include "host-nativeaot.hh" From 2f77200821e7d9475ca0c7c37419c4c0abe91194 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 27 May 2026 17:46:52 +0200 Subject: [PATCH 02/16] Refactor CoreCLR gref logging Centralize gref log message formatting in OSBridge so call sites can pass printf-style arguments directly and formatting is skipped when gref logging is disabled. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 20 +++-------------- .../clr/host/internal-pinvokes-shared.cc | 3 ++- src/native/clr/host/os-bridge.cc | 22 ++++++++++++++----- src/native/clr/include/host/os-bridge.hh | 2 +- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 586c33d8394..c7a3b176a6d 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -400,13 +400,7 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe } } - if (!Logger::gref_log ()) [[likely]] { - return; - } - - char message[128]; - snprintf (message, sizeof (message), "take_global_ref wref=%p -> handle=%p\n", reinterpret_cast(weak), reinterpret_cast(handle)); - OSBridge::_monodroid_gref_log (message); + OSBridge::_monodroid_gref_log ("take_global_ref wref=%p -> handle=%p\n", reinterpret_cast(weak), reinterpret_cast(handle)); } [[gnu::always_inline]] @@ -416,21 +410,13 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept return; } - char message[128]; - snprintf (message, sizeof (message), "handle %p/W; was collected by a Java GC", reinterpret_cast(weak)); - OSBridge::_monodroid_gref_log (message); + OSBridge::_monodroid_gref_log ("handle %p/W; was collected by a Java GC", reinterpret_cast(weak)); } [[gnu::always_inline]] void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept { - if (!Logger::gref_log ()) [[likely]] { - return; - } - - char message[128]; - snprintf (message, sizeof (message), "take_weak_global_ref handle=%p\n", reinterpret_cast(handle)); - OSBridge::_monodroid_gref_log (message); + OSBridge::_monodroid_gref_log ("take_weak_global_ref handle=%p\n", reinterpret_cast(handle)); } [[gnu::always_inline]] diff --git a/src/native/clr/host/internal-pinvokes-shared.cc b/src/native/clr/host/internal-pinvokes-shared.cc index e4fbc06c766..16a0218bc79 100644 --- a/src/native/clr/host/internal-pinvokes-shared.cc +++ b/src/native/clr/host/internal-pinvokes-shared.cc @@ -6,6 +6,7 @@ #include #include #include +#include using namespace xamarin::android; @@ -26,7 +27,7 @@ int _monodroid_gref_dec () noexcept void _monodroid_gref_log (const char *message) noexcept { - OSBridge::_monodroid_gref_log (message); + OSBridge::_monodroid_gref_log ("%s", optional_string (message)); } int _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, [[maybe_unused]] int from_writable) noexcept diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index 2e04dadffef..d0e5ee9e794 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -150,18 +151,29 @@ void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategori } } -void OSBridge::_monodroid_gref_log (const char *message) noexcept +void OSBridge::_monodroid_gref_log (const char *format, ...) noexcept { + FILE *gref_log = Logger::gref_log (); + if (!Logger::gref_to_logcat () && gref_log == nullptr) { + return; + } + + char message[LOG_LINE_BUFFER_SIZE]; + va_list args; + va_start (args, format); + vsnprintf (message, sizeof (message), optional_string (format), args); + va_end (args); + if (Logger::gref_to_logcat ()) { - log_write (LOG_GREF, LogLevel::Debug, optional_string (message)); + log_write (LOG_GREF, LogLevel::Debug, message); } - if (Logger::gref_log () == nullptr) { + if (gref_log == nullptr) { return; } - fprintf (Logger::gref_log (), "%s", optional_string (message)); - fflush (Logger::gref_log ()); + fputs (message, gref_log); + fflush (gref_log); } [[gnu::always_inline, gnu::flatten]] diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh index 2ba614fb78d..408b2558ecc 100644 --- a/src/native/clr/include/host/os-bridge.hh +++ b/src/native/clr/include/host/os-bridge.hh @@ -30,7 +30,7 @@ namespace xamarin::android { static auto _monodroid_weak_gref_inc () noexcept -> int; static auto _monodroid_weak_gref_dec () noexcept -> int; - static void _monodroid_gref_log (const char *message) noexcept; + static void _monodroid_gref_log (const char *format, ...) noexcept __attribute__ ((format (printf, 1, 2))); static auto _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) noexcept -> int; static void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from) noexcept; static void _monodroid_weak_gref_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from); From d59c62e9dbeb2ed5941ad478bb5b1ef6650c4d32 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 27 May 2026 17:53:08 +0200 Subject: [PATCH 03/16] Address gref logging feedback Keep gref file guards at the call sites, allow literal single-argument gref log calls, and centralize printf-style log formatting helpers for CLR native logging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 20 +-- src/native/clr/host/gc-bridge.cc | 19 +-- src/native/clr/host/host-shared.cc | 4 +- .../clr/host/internal-pinvokes-shared.cc | 3 +- src/native/clr/host/os-bridge.cc | 115 +++++++++++------- .../clr/include/host/host-environment.hh | 12 +- src/native/clr/include/host/os-bridge.hh | 2 + src/native/clr/include/runtime-base/util.hh | 39 +++--- src/native/clr/include/shared/log_types.hh | 1 + .../clr/runtime-base/android-system-shared.cc | 12 +- src/native/clr/runtime-base/logger.cc | 12 +- src/native/clr/runtime-base/util.cc | 16 +-- src/native/clr/shared/helpers.cc | 21 +++- src/native/clr/shared/log_functions.cc | 19 +++ src/native/common/include/shared/helpers.hh | 3 + 15 files changed, 159 insertions(+), 139 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index c7a3b176a6d..31c25bc092a 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -361,9 +361,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - char message[256]; - snprintf (message, sizeof (message), "Missing monodroidAddReferences method for object of class %s", optional_string (class_name)); - log_write (LOG_GC, LogLevel::Error, message); + log_writef (LOG_GC, LogLevel::Error, "Missing monodroidAddReferences method for object of class %s", optional_string (class_name)); free (class_name); #endif } @@ -379,9 +377,7 @@ void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - char message[256]; - snprintf (message, sizeof (message), "Missing monodroidClearReferences method for object of class %s", optional_string (class_name)); - log_write (LOG_GC, LogLevel::Error, message); + log_writef (LOG_GC, LogLevel::Error, "Missing monodroidClearReferences method for object of class %s", optional_string (class_name)); free (class_name); #endif } @@ -400,6 +396,10 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe } } + if (!Logger::gref_log ()) [[likely]] { + return; + } + OSBridge::_monodroid_gref_log ("take_global_ref wref=%p -> handle=%p\n", reinterpret_cast(weak), reinterpret_cast(handle)); } @@ -416,6 +416,10 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept [[gnu::always_inline]] void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept { + if (!Logger::gref_log ()) [[likely]] { + return; + } + OSBridge::_monodroid_gref_log ("take_weak_global_ref handle=%p\n", reinterpret_cast(handle)); } @@ -477,7 +481,5 @@ void BridgeProcessingShared::log_gc_summary () noexcept } } - char message[128]; - snprintf (message, sizeof (message), "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); - log_write (LOG_GC, LogLevel::Info, message); + log_writef (LOG_GC, LogLevel::Info, "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); } diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 31cd005a826..ecee8a122af 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -101,16 +101,13 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg return; } - char message[128]; - snprintf (message, sizeof (message), "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount); - log_write (LOG_GC, LogLevel::Info, message); + log_writef (LOG_GC, LogLevel::Info, "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount); JNIEnv *env = OSBridge::ensure_jnienv (); for (size_t i = 0; i < args->ComponentCount; ++i) { const StronglyConnectedComponent &scc = args->Components [i]; - snprintf (message, sizeof (message), "group %zu with %zu objects", i, scc.Count); - log_write (LOG_GC, LogLevel::Info, message); + log_writef (LOG_GC, LogLevel::Info, "group %zu with %zu objects", i, scc.Count); for (size_t j = 0; j < scc.Count; ++j) { log_handle_context (env, scc.Contexts [j]); } @@ -123,9 +120,7 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg for (size_t i = 0; i < args->CrossReferenceCount; ++i) { size_t source_index = args->CrossReferences [i].SourceGroupIndex; size_t dest_index = args->CrossReferences [i].DestinationGroupIndex; - char xref_message[128]; - snprintf (xref_message, sizeof (xref_message), "xref [%zu] %zu -> %zu", i, source_index, dest_index); - log_write (LOG_GC, LogLevel::Info, xref_message); + log_writef (LOG_GC, LogLevel::Info, "xref [%zu] %zu -> %zu", i, source_index, dest_index); } } @@ -139,13 +134,9 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept jclass java_class = env->GetObjectClass (handle); if (java_class != nullptr) { char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - char message[256]; - snprintf (message, sizeof (message), "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name)); - log_write (LOG_GC, LogLevel::Info, message); + log_writef (LOG_GC, LogLevel::Info, "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name)); free (class_name); } else { - char message[128]; - snprintf (message, sizeof (message), "gref %p [unknown class]", reinterpret_cast(handle)); - log_write (LOG_GC, LogLevel::Info, message); + log_writef (LOG_GC, LogLevel::Info, "gref %p [unknown class]", reinterpret_cast(handle)); } } diff --git a/src/native/clr/host/host-shared.cc b/src/native/clr/host/host-shared.cc index 97b24668524..eca9ff91954 100644 --- a/src/native/clr/host/host-shared.cc +++ b/src/native/clr/host/host-shared.cc @@ -14,9 +14,7 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept -> JNIEnv *env = OSBridge::ensure_jnienv (); jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName)); if (name == nullptr) { - char message[128]; - snprintf (message, sizeof (message), "Failed to obtain Java class name for object at %p", reinterpret_cast(klass)); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_writef (LOG_DEFAULT, LogLevel::Error, "Failed to obtain Java class name for object at %p", reinterpret_cast(klass)); return nullptr; } diff --git a/src/native/clr/host/internal-pinvokes-shared.cc b/src/native/clr/host/internal-pinvokes-shared.cc index 16a0218bc79..e4fbc06c766 100644 --- a/src/native/clr/host/internal-pinvokes-shared.cc +++ b/src/native/clr/host/internal-pinvokes-shared.cc @@ -6,7 +6,6 @@ #include #include #include -#include using namespace xamarin::android; @@ -27,7 +26,7 @@ int _monodroid_gref_dec () noexcept void _monodroid_gref_log (const char *message) noexcept { - OSBridge::_monodroid_gref_log ("%s", optional_string (message)); + OSBridge::_monodroid_gref_log (message); } int _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from, [[maybe_unused]] int from_writable) noexcept diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index d0e5ee9e794..80d88df5a08 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -35,6 +35,20 @@ namespace { free (buffer); } } + + void write_gref_message (const char *message, FILE *gref_log) noexcept + { + if (Logger::gref_to_logcat ()) { + log_write (LOG_GREF, LogLevel::Debug, optional_string (message)); + } + + if (gref_log == nullptr) { + return; + } + + fputs (optional_string (message), gref_log); + fflush (gref_log); + } } void OSBridge::initialize_on_onload (JavaVM *vm, JNIEnv *env) noexcept @@ -151,6 +165,16 @@ void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategori } } +void OSBridge::_monodroid_gref_log (const char *message) noexcept +{ + FILE *gref_log = Logger::gref_log (); + if (!Logger::gref_to_logcat () && gref_log == nullptr) { + return; + } + + write_gref_message (message, gref_log); +} + void OSBridge::_monodroid_gref_log (const char *format, ...) noexcept { FILE *gref_log = Logger::gref_log (); @@ -164,16 +188,7 @@ void OSBridge::_monodroid_gref_log (const char *format, ...) noexcept vsnprintf (message, sizeof (message), optional_string (format), args); va_end (args); - if (Logger::gref_to_logcat ()) { - log_write (LOG_GREF, LogLevel::Debug, message); - } - - if (gref_log == nullptr) { - return; - } - - fputs (message, gref_log); - fflush (gref_log); + write_gref_message (message, gref_log); } [[gnu::always_inline, gnu::flatten]] @@ -197,6 +212,21 @@ void OSBridge::log_it (LogCategories category, const char *line, FILE *to, const fflush (to); } +void OSBridge::log_itf (LogCategories category, FILE *to, const char *from, bool logcat_enabled, const char *format, ...) noexcept +{ + if (!logcat_enabled && to == nullptr) { + return; + } + + char log_line[LOG_LINE_BUFFER_SIZE]; + va_list args; + va_start (args, format); + vsnprintf (log_line, sizeof (log_line), optional_string (format), args); + va_end (args); + + log_it (category, log_line, to, from, logcat_enabled); +} + 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 (); @@ -205,10 +235,11 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject } int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED); - char log_line[LOG_LINE_BUFFER_SIZE]; - snprintf ( - log_line, - sizeof (log_line), + 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, @@ -220,7 +251,6 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject threadId ); - log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ()); return c; } @@ -232,10 +262,11 @@ void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char } int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED); - char log_line[LOG_LINE_BUFFER_SIZE]; - snprintf ( - log_line, - sizeof (log_line), + 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, @@ -244,8 +275,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) @@ -256,10 +285,11 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec } int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED); - char log_line[LOG_LINE_BUFFER_SIZE]; - snprintf ( - log_line, - sizeof (log_line), + 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, @@ -270,8 +300,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 @@ -281,10 +309,11 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c return; } - char log_line[LOG_LINE_BUFFER_SIZE]; - snprintf ( - log_line, - sizeof (log_line), + 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), @@ -292,8 +321,6 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c 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) @@ -304,10 +331,11 @@ void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const cha } int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED); - char log_line[LOG_LINE_BUFFER_SIZE]; - snprintf ( - log_line, - sizeof (log_line), + 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, @@ -316,8 +344,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) @@ -326,10 +352,11 @@ void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, return; } - char log_line[LOG_LINE_BUFFER_SIZE]; - snprintf ( - log_line, - sizeof (log_line), + 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), @@ -337,6 +364,4 @@ void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char 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/host-environment.hh b/src/native/clr/include/host/host-environment.hh index f1a157cf736..ec7a53d52c0 100644 --- a/src/native/clr/include/host/host-environment.hh +++ b/src/native/clr/include/host/host-environment.hh @@ -43,9 +43,7 @@ namespace xamarin::android { { // TODO: should we **actually** try to set the system property here? Would that even work? Needs testing if ((log_categories & LOG_DEFAULT) != 0) { - char message[512]; - snprintf (message, sizeof (message), " System property %s = '%s'", optional_string (name), optional_string (value)); - log_write (LOG_DEFAULT, LogLevel::Debug, message); + log_writef (LOG_DEFAULT, LogLevel::Debug, " System property %s = '%s'", optional_string (name), optional_string (value)); } } @@ -93,15 +91,11 @@ namespace xamarin::android { Util::path_combine (dir, home.get_string_view (), relative_path); if ((log_categories & LOG_DEFAULT) != 0) { - char message[512]; - snprintf (message, sizeof (message), "Creating XDG directory: %s", optional_string (dir.get ())); - log_write (LOG_DEFAULT, LogLevel::Debug, message); + log_writef (LOG_DEFAULT, LogLevel::Debug, "Creating XDG directory: %s", optional_string (dir.get ())); } int rv = Util::create_directory (dir.get (), Constants::DEFAULT_DIRECTORY_MODE); if (rv < 0 && errno != EEXIST) { - char message[512]; - snprintf (message, sizeof (message), "Failed to create XDG directory %s. %s", optional_string (dir.get ()), strerror (errno)); - log_write (LOG_DEFAULT, LogLevel::Warn, message); + log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to create XDG directory %s. %s", optional_string (dir.get ()), strerror (errno)); } if (!environment_variable_name.empty ()) { diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh index 408b2558ecc..4ffbf57a3bf 100644 --- a/src/native/clr/include/host/os-bridge.hh +++ b/src/native/clr/include/host/os-bridge.hh @@ -30,6 +30,7 @@ namespace xamarin::android { static auto _monodroid_weak_gref_inc () noexcept -> int; static auto _monodroid_weak_gref_dec () noexcept -> int; + static void _monodroid_gref_log (const char *message) noexcept; static void _monodroid_gref_log (const char *format, ...) noexcept __attribute__ ((format (printf, 1, 2))); static auto _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) noexcept -> int; static void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from) noexcept; @@ -58,6 +59,7 @@ 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, const char *line, FILE *to, const char *const from, bool logcat_enabled) noexcept; + static void log_itf (LogCategories category, FILE *to, const char *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/runtime-base/util.hh b/src/native/clr/include/runtime-base/util.hh index 3082eb90220..273fb6a34eb 100644 --- a/src/native/clr/include/runtime-base/util.hh +++ b/src/native/clr/include/runtime-base/util.hh @@ -140,9 +140,7 @@ namespace xamarin::android { { struct stat sbuf; if (fstatat (dirfd, file_name, &sbuf, 0) == -1) { - char message[512]; - snprintf (message, sizeof (message), "Failed to stat file '%s': %s", file_name, std::strerror (errno)); - log_write (LOG_ASSEMBLY, LogLevel::Warn, message); + log_writef (LOG_ASSEMBLY, LogLevel::Warn, "Failed to stat file '%s': %s", file_name, std::strerror (errno)); return std::nullopt; } @@ -158,14 +156,10 @@ namespace xamarin::android { static void set_environment_variable (const char *name, const char *value) noexcept { if (should_log (LOG_DEFAULT)) { - char message[512]; - snprintf (message, sizeof (message), "Setting environment variable %s = '%s'", optional_string (name), optional_string (value)); - log_write (LOG_DEFAULT, LogLevel::Debug, message); + log_writef (LOG_DEFAULT, LogLevel::Debug, "Setting environment variable %s = '%s'", optional_string (name), optional_string (value)); } if (::setenv (name, value, 1) < 0) { - char message[512]; - snprintf (message, sizeof (message), "Failed to set environment variable '%s': %s", optional_string (name), ::strerror (errno)); - log_write (LOG_DEFAULT, LogLevel::Warn, message); + log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to set environment variable '%s': %s", optional_string (name), ::strerror (errno)); } } @@ -187,9 +181,7 @@ namespace xamarin::android { if (createDirectory) { int rv = create_directory (value.get_cstr (), mode); if (rv < 0 && errno != EEXIST) { - char message[512]; - snprintf (message, sizeof (message), "Failed to create directory '%s' for environment variable '%s'. %s", optional_string (value.get_cstr ()), name.data (), strerror (errno)); - log_write (LOG_DEFAULT, LogLevel::Warn, message); + log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s' for environment variable '%s'. %s", optional_string (value.get_cstr ()), name.data (), strerror (errno)); } } set_environment_variable (name, value); @@ -219,9 +211,14 @@ namespace xamarin::android { mmap_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast(offsetPage)); if (mmap_info.area == MAP_FAILED) { - char message[512]; - snprintf (message, sizeof (message), "Could not mmap APK fd %d: %s; File=%s", fd, strerror (errno), filename.data ()); - Helpers::abort_application (LOG_ASSEMBLY, message); + Helpers::abort_applicationf ( + LOG_ASSEMBLY, + std::source_location::current (), + "Could not mmap APK fd %d: %s; File=%s", + fd, + strerror (errno), + filename.data () + ); } mmap_info.size = offsetSize; @@ -229,10 +226,9 @@ namespace xamarin::android { file_info.size = size; if (should_log (LOG_ASSEMBLY)) { - char message[512]; - snprintf ( - message, - sizeof (message), + log_writef ( + LOG_ASSEMBLY, + LogLevel::Info, " mmap_start: %-8p; mmap_end: %-8p mmap_len: %-12zu file_start: %-8p file_end: %-8p file_len: %-12zu apk descriptor: %d file: %s", mmap_info.area, pointer_add (mmap_info.area, mmap_info.size), @@ -243,7 +239,6 @@ namespace xamarin::android { fd, filename.data () ); - log_write (LOG_ASSEMBLY, LogLevel::Info, message); } return file_info; @@ -267,9 +262,7 @@ namespace xamarin::android { elf_header->e_ident[EI_MAG2] != ELFMAG2 || elf_header->e_ident[EI_MAG3] != ELFMAG3) { if (should_log (LOG_ASSEMBLY)) { - char message[512]; - snprintf (message, sizeof (message), "Not an ELF image: %s", file_name.data ()); - log_write (LOG_ASSEMBLY, LogLevel::Debug, message); + log_writef (LOG_ASSEMBLY, LogLevel::Debug, "Not an ELF image: %s", file_name.data ()); } // Not an ELF image, just return what we mmapped before return { map_info.area, map_info.size }; diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh index 9ecef42a0ce..ea6a0cae91a 100644 --- a/src/native/clr/include/shared/log_types.hh +++ b/src/native/clr/include/shared/log_types.hh @@ -60,6 +60,7 @@ 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_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); [[gnu::always_inline]] static inline void log_write (LogCategories category, LogLevel level, std::string_view const& message) noexcept diff --git a/src/native/clr/runtime-base/android-system-shared.cc b/src/native/clr/runtime-base/android-system-shared.cc index c7ef49464ce..f4b42bda4de 100644 --- a/src/native/clr/runtime-base/android-system-shared.cc +++ b/src/native/clr/runtime-base/android-system-shared.cc @@ -39,9 +39,7 @@ AndroidSystem::monodroid__system_property_get (std::string_view const& name, cha char *buf = nullptr; if (sp_value_len < Constants::PROPERTY_VALUE_BUFFER_LEN) { size_t alloc_size = Helpers::add_with_overflow_check (Constants::PROPERTY_VALUE_BUFFER_LEN, 1uz); - char message[128]; - snprintf (message, sizeof (message), "Buffer to store system property may be too small, will copy only %zu bytes", sp_value_len); - log_write (LOG_DEFAULT, LogLevel::Warn, message); + log_writef (LOG_DEFAULT, LogLevel::Warn, "Buffer to store system property may be too small, will copy only %zu bytes", sp_value_len); buf = static_cast (std::malloc (alloc_size)); abort_unless (buf != nullptr, "Failed to allocate system property buffer"); } @@ -87,14 +85,10 @@ AndroidSystem::get_max_gref_count_from_system () noexcept -> long } if (*e) { - char message[256]; - snprintf (message, sizeof (message), "Unsupported '%s' value '%s'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); - log_write (LOG_GC, LogLevel::Warn, message); + log_writef (LOG_GC, LogLevel::Warn, "Unsupported '%s' value '%s'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); } - char message[128]; - snprintf (message, sizeof (message), "Overriding max JNI Global Reference count to %ld", max); - log_write (LOG_GC, LogLevel::Warn, message); + log_writef (LOG_GC, LogLevel::Warn, "Overriding max JNI Global Reference count to %ld", max); } return max; diff --git a/src/native/clr/runtime-base/logger.cc b/src/native/clr/runtime-base/logger.cc index 369947ee4d2..58deabfed59 100644 --- a/src/native/clr/runtime-base/logger.cc +++ b/src/native/clr/runtime-base/logger.cc @@ -60,9 +60,7 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p auto log_and_return = [&category](FILE *f, std::string_view const& path) -> FILE* { if (f != nullptr) { if ((log_categories & category) != 0) { - char message[512]; - snprintf (message, sizeof (message), "Opened file '%s' for logging.", path.data ()); - log_write (category, LogLevel::Debug, message); + log_writef (category, LogLevel::Debug, "Opened file '%s' for logging.", path.data ()); } } return f; @@ -173,18 +171,16 @@ Logger::init_logging_categories () noexcept auto file_name = segment.at (offset); if (!file_name.has_value ()) { - char message[256]; std::string_view error = to_string (file_name.error ()); - snprintf ( - message, - sizeof (message), + log_writef ( + LOG_DEFAULT, + LogLevel::Warn, "Unable to set path to %.*s log file: %.*s", static_cast(file_kind.length ()), file_kind.data (), static_cast(error.length ()), error.data () ); - log_write (LOG_DEFAULT, LogLevel::Warn, message); return nullptr; } diff --git a/src/native/clr/runtime-base/util.cc b/src/native/clr/runtime-base/util.cc index 0f1c8799376..98d26503b8f 100644 --- a/src/native/clr/runtime-base/util.cc +++ b/src/native/clr/runtime-base/util.cc @@ -59,9 +59,7 @@ Util::create_public_directory (std::string_view const& dir) // Try to change the mode, just in case chmod (dir.data (), 0777); } else { - char message[512]; - snprintf (message, sizeof (message), "Failed to create directory '%s'. %s", dir.data (), std::strerror (errno)); - log_write (LOG_DEFAULT, LogLevel::Warn, message); + log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s'. %s", dir.data (), std::strerror (errno)); } } umask (m); @@ -75,9 +73,7 @@ Util::monodroid_fopen (std::string_view const& filename, std::string_view const& */ FILE *ret = fopen (filename.data (), mode.data ()); if (ret == nullptr) { - char message[512]; - snprintf (message, sizeof (message), "fopen failed for file %s: %s", filename.data (), strerror (errno)); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_writef (LOG_DEFAULT, LogLevel::Error, "fopen failed for file %s: %s", filename.data (), strerror (errno)); return nullptr; } @@ -92,9 +88,7 @@ void Util::set_world_accessable (std::string_view const& path) } while (r == -1 && errno == EINTR); if (r == -1) { - char message[512]; - snprintf (message, sizeof (message), "chmod(\"%s\", 0664) failed: %s", path.data (), strerror (errno)); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_writef (LOG_DEFAULT, LogLevel::Error, "chmod(\"%s\", 0664) failed: %s", path.data (), strerror (errno)); } } @@ -106,9 +100,7 @@ auto Util::set_world_accessible (int fd) noexcept -> bool } while (r == -1 && errno == EINTR); if (r == -1) { - char message[128]; - snprintf (message, sizeof (message), "fchmod() failed: %s", strerror (errno)); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_writef (LOG_DEFAULT, LogLevel::Error, "fchmod() failed: %s", strerror (errno)); return false; } diff --git a/src/native/clr/shared/helpers.cc b/src/native/clr/shared/helpers.cc index 69c0f6812b8..61f0cbcfef0 100644 --- a/src/native/clr/shared/helpers.cc +++ b/src/native/clr/shared/helpers.cc @@ -8,6 +8,19 @@ using namespace xamarin::android; +[[noreturn]] void +Helpers::abort_applicationf (LogCategories category, std::source_location sloc, const char *format, ...) noexcept +{ + char message[512]; + va_list args; + const char *safe_format = format == nullptr ? "" : format; + va_start (args, format); + vsnprintf (message, sizeof (message), safe_format, args); + va_end (args); + + abort_application (category, message, true, sloc); +} + [[noreturn]] void Helpers::abort_application (LogCategories category, const char *message, bool log_location, std::source_location sloc) noexcept { @@ -34,17 +47,15 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo } } - char location_message[512]; - snprintf ( - location_message, - sizeof (location_message), + log_writef ( + category, + LogLevel::Fatal, "Abort at %s:%u:%u ('%s')", file_name, sloc.line (), sloc.column (), sloc.function_name () ); - log_write (category, LogLevel::Fatal, location_message); } std::abort (); } diff --git a/src/native/clr/shared/log_functions.cc b/src/native/clr/shared/log_functions.cc index acd5e705c06..fc174dbf8ad 100644 --- a/src/native/clr/shared/log_functions.cc +++ b/src/native/clr/shared/log_functions.cc @@ -128,4 +128,23 @@ namespace xamarin::android { __android_log_write (priority, category_name (category), message); } + + void + log_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept + { + size_t map_index = static_cast(level); + android_LogPriority priority; + + if (map_index > loglevel_map_max_index) { + priority = DEFAULT_PRIORITY; + } else { + priority = loglevel_map[map_index]; + } + + va_list args; + const char *safe_format = format == nullptr ? "" : format; + va_start (args, format); + __android_log_vprint (priority, category_name (category), safe_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 { From b6dc9900a79aa08adfe5bb3d2877f38388c673ca Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 27 May 2026 18:05:05 +0200 Subject: [PATCH 04/16] Use robin_map for CLR temporary peers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 48 +++++++++------- src/native/clr/host/gc-bridge.cc | 12 ++-- src/native/clr/host/host-shared.cc | 2 +- .../include/host/bridge-processing-shared.hh | 31 +++++++++- .../clr/include/host/host-environment.hh | 6 +- src/native/clr/include/runtime-base/util.hh | 12 ++-- src/native/clr/include/shared/log_types.hh | 48 +++++++++++++--- .../clr/runtime-base/android-system-shared.cc | 6 +- src/native/clr/runtime-base/logger.cc | 4 +- src/native/clr/runtime-base/util.cc | 8 +-- src/native/clr/shared/helpers.cc | 2 +- src/native/clr/shared/log_functions.cc | 57 ++++++++++++++++++- 12 files changed, 176 insertions(+), 60 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 31c25bc092a..93cc13c74fc 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -37,15 +37,11 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, c Helpers::abort_application (LOG_GC, "CrossReferences member of the cross references arguments structure is NULL"sv); } - if (args->ComponentCount > 0) { - temporary_peers = static_cast(calloc (args->ComponentCount, sizeof (jobject))); - abort_unless (temporary_peers != nullptr, "Failed to allocate GC bridge temporary peer array"); - } } BridgeProcessingShared::~BridgeProcessingShared () noexcept { - free (temporary_peers); + release_temporary_peers (); } void BridgeProcessingShared::process () noexcept @@ -73,12 +69,7 @@ void BridgeProcessingShared::prepare_for_java_collection () noexcept } // With cross references processed, the temporary peer list can be released - for (size_t i = 0; i < cross_refs->ComponentCount; i++) { - if (temporary_peers[i] != nullptr) { - env->DeleteLocalRef (temporary_peers[i]); - temporary_peers[i] = nullptr; - } - } + release_temporary_peers (); // Switch global to weak references for (size_t i = 0; i < cross_refs->ComponentCount; i++) { @@ -96,8 +87,11 @@ void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, { // Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one if (scc.Count == 0) { - abort_unless (temporary_peers != nullptr, "Temporary peer array must be allocated"); - temporary_peers[scc_index] = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); + auto [peer_entry, inserted] = temporary_peers.emplace (scc_index, nullptr); + abort_unless (inserted, "Temporary peer must not already exist"); + + peer_entry->second = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); + abort_unless (peer_entry->second != nullptr, "Failed to create GC bridge temporary peer"); return; } @@ -116,15 +110,27 @@ CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size const StronglyConnectedComponent &scc = cross_refs->Components [scc_index]; if (scc.Count == 0) { - abort_unless (temporary_peers != nullptr, "Temporary peer array must be allocated"); - abort_unless (temporary_peers[scc_index] != nullptr, "Temporary peer must be found in the array"); - return { .is_temporary_peer = true, .temporary_peer = temporary_peers[scc_index] }; + auto peer_entry = temporary_peers.find (scc_index); + abort_unless (peer_entry != temporary_peers.end (), "Temporary peer must be found in the map"); + abort_unless (peer_entry->second != nullptr, "Temporary peer must not be null"); + return { .is_temporary_peer = true, .temporary_peer = peer_entry->second }; } abort_unless (scc.Contexts [0] != nullptr, "SCC must have at least one context"); return { .is_temporary_peer = false, .context = scc.Contexts [0] }; } +void BridgeProcessingShared::release_temporary_peers () noexcept +{ + for (const auto &entry : temporary_peers) { + jobject temporary_peer = entry.second; + if (temporary_peer != nullptr) { + env->DeleteLocalRef (temporary_peer); + } + } + temporary_peers.clear (); +} + // caller must ensure that scc.Count > 1 void BridgeProcessingShared::add_circular_references (const StronglyConnectedComponent &scc) noexcept { @@ -353,7 +359,7 @@ void CrossReferenceTarget::mark_refs_added_if_needed () noexcept [[gnu::always_inline]] void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] jclass java_class) noexcept { - log_write (LOG_DEFAULT, LogLevel::Error, "Failed to find monodroidAddReferences method"); + (xamarin::android::log_error) (LOG_DEFAULT, "Failed to find monodroidAddReferences method"); #if DEBUG abort_if_invalid_pointer_argument (java_class, "java_class"); if (!Logger::gc_spew_enabled ()) [[likely]] { @@ -361,7 +367,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - log_writef (LOG_GC, LogLevel::Error, "Missing monodroidAddReferences method for object of class %s", optional_string (class_name)); + xamarin::android::log_error_fmt (LOG_GC, "Missing monodroidAddReferences method for object of class %s", optional_string (class_name)); free (class_name); #endif } @@ -369,7 +375,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] [[gnu::always_inline]] void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused]] jclass java_class) noexcept { - log_write (LOG_DEFAULT, LogLevel::Error, "Failed to find monodroidClearReferences method"); + (xamarin::android::log_error) (LOG_DEFAULT, "Failed to find monodroidClearReferences method"); #if DEBUG abort_if_invalid_pointer_argument (java_class, "java_class"); if (!Logger::gc_spew_enabled ()) [[likely]] { @@ -377,7 +383,7 @@ void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - log_writef (LOG_GC, LogLevel::Error, "Missing monodroidClearReferences method for object of class %s", optional_string (class_name)); + xamarin::android::log_error_fmt (LOG_GC, "Missing monodroidClearReferences method for object of class %s", optional_string (class_name)); free (class_name); #endif } @@ -481,5 +487,5 @@ void BridgeProcessingShared::log_gc_summary () noexcept } } - log_writef (LOG_GC, LogLevel::Info, "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); + xamarin::android::log_info_fmt (LOG_GC, "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); } diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index ecee8a122af..98d77c5a178 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -49,7 +49,7 @@ void GCBridge::trigger_java_gc (JNIEnv *env) noexcept env->ExceptionDescribe (); env->ExceptionClear (); - log_write (LOG_DEFAULT, LogLevel::Error, "Java GC failed"); + (xamarin::android::log_error) (LOG_DEFAULT, "Java GC failed"); } void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept @@ -101,13 +101,13 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg return; } - log_writef (LOG_GC, LogLevel::Info, "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount); + xamarin::android::log_info_fmt (LOG_GC, "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount); JNIEnv *env = OSBridge::ensure_jnienv (); for (size_t i = 0; i < args->ComponentCount; ++i) { const StronglyConnectedComponent &scc = args->Components [i]; - log_writef (LOG_GC, LogLevel::Info, "group %zu with %zu objects", i, scc.Count); + xamarin::android::log_info_fmt (LOG_GC, "group %zu with %zu objects", i, scc.Count); for (size_t j = 0; j < scc.Count; ++j) { log_handle_context (env, scc.Contexts [j]); } @@ -120,7 +120,7 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg for (size_t i = 0; i < args->CrossReferenceCount; ++i) { size_t source_index = args->CrossReferences [i].SourceGroupIndex; size_t dest_index = args->CrossReferences [i].DestinationGroupIndex; - log_writef (LOG_GC, LogLevel::Info, "xref [%zu] %zu -> %zu", i, source_index, dest_index); + xamarin::android::log_info_fmt (LOG_GC, "xref [%zu] %zu -> %zu", i, source_index, dest_index); } } @@ -134,9 +134,9 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept jclass java_class = env->GetObjectClass (handle); if (java_class != nullptr) { char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - log_writef (LOG_GC, LogLevel::Info, "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name)); + xamarin::android::log_info_fmt (LOG_GC, "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name)); free (class_name); } else { - log_writef (LOG_GC, LogLevel::Info, "gref %p [unknown class]", reinterpret_cast(handle)); + xamarin::android::log_info_fmt (LOG_GC, "gref %p [unknown class]", reinterpret_cast(handle)); } } diff --git a/src/native/clr/host/host-shared.cc b/src/native/clr/host/host-shared.cc index eca9ff91954..5909283a498 100644 --- a/src/native/clr/host/host-shared.cc +++ b/src/native/clr/host/host-shared.cc @@ -14,7 +14,7 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept -> JNIEnv *env = OSBridge::ensure_jnienv (); jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName)); if (name == nullptr) { - log_writef (LOG_DEFAULT, LogLevel::Error, "Failed to obtain Java class name for object at %p", reinterpret_cast(klass)); + log_write_fmt (LOG_DEFAULT, LogLevel::Error, "Failed to obtain Java class name for object at %p", reinterpret_cast(klass)); return nullptr; } diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh index c9f55b4fc66..277c8371378 100644 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ b/src/native/clr/include/host/bridge-processing-shared.hh @@ -1,7 +1,33 @@ #pragma once +#include + #include +// NDEBUG causes robin_map.h not to include which, in turn, prevents indirect inclusion of . +// conflicts with our std::mutex definition in cppcompat.hh +#if !defined (NDEBUG) +#define NDEBUG +#define NDEBUG_UNDEFINE +#endif + +// hush some compiler warnings +#if defined (__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif // __clang__ + +#include + +#if defined (__clang__) +#pragma clang diagnostic pop +#endif // __clang__ + +#if defined (NDEBUG_UNDEFINE) +#undef NDEBUG +#undef NDEBUG_UNDEFINE +#endif + #include #include #include @@ -28,6 +54,8 @@ struct BridgeProcessingCallbacks class BridgeProcessingShared { + using temporary_peer_map = tsl::robin_map; + public: explicit BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *callbacks = nullptr) noexcept; ~BridgeProcessingShared () noexcept; @@ -37,7 +65,7 @@ public: private: JNIEnv* env; MarkCrossReferencesArgs *cross_refs; - jobject *temporary_peers = nullptr; + temporary_peer_map temporary_peers; BridgeProcessingCallbacks callbacks; static inline jclass GCUserPeer_class = nullptr; @@ -45,6 +73,7 @@ private: void prepare_for_java_collection () noexcept; void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept; + void release_temporary_peers () noexcept; void take_weak_global_ref (const HandleContext &context) noexcept; void add_circular_references (const StronglyConnectedComponent &scc) noexcept; diff --git a/src/native/clr/include/host/host-environment.hh b/src/native/clr/include/host/host-environment.hh index ec7a53d52c0..7ef13ba5835 100644 --- a/src/native/clr/include/host/host-environment.hh +++ b/src/native/clr/include/host/host-environment.hh @@ -43,7 +43,7 @@ namespace xamarin::android { { // TODO: should we **actually** try to set the system property here? Would that even work? Needs testing if ((log_categories & LOG_DEFAULT) != 0) { - log_writef (LOG_DEFAULT, LogLevel::Debug, " System property %s = '%s'", optional_string (name), optional_string (value)); + log_write_fmt (LOG_DEFAULT, LogLevel::Debug, " System property %s = '%s'", optional_string (name), optional_string (value)); } } @@ -91,11 +91,11 @@ namespace xamarin::android { Util::path_combine (dir, home.get_string_view (), relative_path); if ((log_categories & LOG_DEFAULT) != 0) { - log_writef (LOG_DEFAULT, LogLevel::Debug, "Creating XDG directory: %s", optional_string (dir.get ())); + log_write_fmt (LOG_DEFAULT, LogLevel::Debug, "Creating XDG directory: %s", optional_string (dir.get ())); } int rv = Util::create_directory (dir.get (), Constants::DEFAULT_DIRECTORY_MODE); if (rv < 0 && errno != EEXIST) { - log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to create XDG directory %s. %s", optional_string (dir.get ()), strerror (errno)); + log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to create XDG directory %s. %s", optional_string (dir.get ()), strerror (errno)); } if (!environment_variable_name.empty ()) { diff --git a/src/native/clr/include/runtime-base/util.hh b/src/native/clr/include/runtime-base/util.hh index 273fb6a34eb..b94746c679f 100644 --- a/src/native/clr/include/runtime-base/util.hh +++ b/src/native/clr/include/runtime-base/util.hh @@ -140,7 +140,7 @@ namespace xamarin::android { { struct stat sbuf; if (fstatat (dirfd, file_name, &sbuf, 0) == -1) { - log_writef (LOG_ASSEMBLY, LogLevel::Warn, "Failed to stat file '%s': %s", file_name, std::strerror (errno)); + log_write_fmt (LOG_ASSEMBLY, LogLevel::Warn, "Failed to stat file '%s': %s", file_name, std::strerror (errno)); return std::nullopt; } @@ -156,10 +156,10 @@ namespace xamarin::android { static void set_environment_variable (const char *name, const char *value) noexcept { if (should_log (LOG_DEFAULT)) { - log_writef (LOG_DEFAULT, LogLevel::Debug, "Setting environment variable %s = '%s'", optional_string (name), optional_string (value)); + log_write_fmt (LOG_DEFAULT, LogLevel::Debug, "Setting environment variable %s = '%s'", optional_string (name), optional_string (value)); } if (::setenv (name, value, 1) < 0) { - log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to set environment variable '%s': %s", optional_string (name), ::strerror (errno)); + log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to set environment variable '%s': %s", optional_string (name), ::strerror (errno)); } } @@ -181,7 +181,7 @@ namespace xamarin::android { if (createDirectory) { int rv = create_directory (value.get_cstr (), mode); if (rv < 0 && errno != EEXIST) { - log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s' for environment variable '%s'. %s", optional_string (value.get_cstr ()), name.data (), strerror (errno)); + log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s' for environment variable '%s'. %s", optional_string (value.get_cstr ()), name.data (), strerror (errno)); } } set_environment_variable (name, value); @@ -226,7 +226,7 @@ namespace xamarin::android { file_info.size = size; if (should_log (LOG_ASSEMBLY)) { - log_writef ( + log_write_fmt ( LOG_ASSEMBLY, LogLevel::Info, " mmap_start: %-8p; mmap_end: %-8p mmap_len: %-12zu file_start: %-8p file_end: %-8p file_len: %-12zu apk descriptor: %d file: %s", @@ -262,7 +262,7 @@ namespace xamarin::android { elf_header->e_ident[EI_MAG2] != ELFMAG2 || elf_header->e_ident[EI_MAG3] != ELFMAG3) { if (should_log (LOG_ASSEMBLY)) { - log_writef (LOG_ASSEMBLY, LogLevel::Debug, "Not an ELF image: %s", file_name.data ()); + log_write_fmt (LOG_ASSEMBLY, LogLevel::Debug, "Not an ELF image: %s", file_name.data ()); } // Not an ELF image, just return what we mmapped before return { map_info.area, map_info.size }; diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh index ea6a0cae91a..6dc4819158b 100644 --- a/src/native/clr/include/shared/log_types.hh +++ b/src/native/clr/include/shared/log_types.hh @@ -48,19 +48,20 @@ #define log_info(_category_, _fmt_, ...) DO_LOG_FMT (info, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) // NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_warn(_category_, _fmt_, ...) log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#define log_warn(_category_, _fmt_, ...) ::log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) // NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_error(_category_, _fmt_, ...) log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#define log_error(_category_, _fmt_, ...) ::log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) // NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_fatal(_category_, _fmt_, ...) log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +#define log_fatal(_category_, _fmt_, ...) ::log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) 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_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); + void log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept; + void log_write_fmt (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); [[gnu::always_inline]] static inline void log_write (LogCategories category, LogLevel level, std::string_view const& message) noexcept @@ -68,13 +69,42 @@ namespace xamarin::android { log_write (category, level, message.data ()); } -#if !defined(XA_HOST_NATIVEAOT) - template [[gnu::always_inline]] - static inline constexpr void log_write_fmt (LogCategories category, LogLevel level, std::format_string fmt, Args&& ...args) + [[gnu::always_inline]] + static inline void (log_debug) (LogCategories category, const char *message) noexcept { - log_write (category, level, std::format (fmt, std::forward(args)...).c_str ()); + log_write (category, LogLevel::Debug, message); } -#endif + + [[gnu::always_inline]] + static inline void (log_info) (LogCategories category, const char *message) noexcept + { + log_write (category, LogLevel::Info, message); + } + + [[gnu::always_inline]] + static inline void (log_warn) (LogCategories category, const char *message) noexcept + { + log_write (category, LogLevel::Warn, message); + } + + [[gnu::always_inline]] + static inline void (log_error) (LogCategories category, const char *message) noexcept + { + log_write (category, LogLevel::Error, message); + } + + [[gnu::always_inline]] + static inline void (log_fatal) (LogCategories category, const char *message) noexcept + { + log_write (category, LogLevel::Fatal, message); + } + + void log_debug_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_info_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_warn_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_error_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_fatal_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + } #if !defined(XA_HOST_NATIVEAOT) diff --git a/src/native/clr/runtime-base/android-system-shared.cc b/src/native/clr/runtime-base/android-system-shared.cc index f4b42bda4de..ed0c30b982a 100644 --- a/src/native/clr/runtime-base/android-system-shared.cc +++ b/src/native/clr/runtime-base/android-system-shared.cc @@ -39,7 +39,7 @@ AndroidSystem::monodroid__system_property_get (std::string_view const& name, cha char *buf = nullptr; if (sp_value_len < Constants::PROPERTY_VALUE_BUFFER_LEN) { size_t alloc_size = Helpers::add_with_overflow_check (Constants::PROPERTY_VALUE_BUFFER_LEN, 1uz); - log_writef (LOG_DEFAULT, LogLevel::Warn, "Buffer to store system property may be too small, will copy only %zu bytes", sp_value_len); + log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Buffer to store system property may be too small, will copy only %zu bytes", sp_value_len); buf = static_cast (std::malloc (alloc_size)); abort_unless (buf != nullptr, "Failed to allocate system property buffer"); } @@ -85,10 +85,10 @@ AndroidSystem::get_max_gref_count_from_system () noexcept -> long } if (*e) { - log_writef (LOG_GC, LogLevel::Warn, "Unsupported '%s' value '%s'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); + log_write_fmt (LOG_GC, LogLevel::Warn, "Unsupported '%s' value '%s'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); } - log_writef (LOG_GC, LogLevel::Warn, "Overriding max JNI Global Reference count to %ld", max); + log_write_fmt (LOG_GC, LogLevel::Warn, "Overriding max JNI Global Reference count to %ld", max); } return max; diff --git a/src/native/clr/runtime-base/logger.cc b/src/native/clr/runtime-base/logger.cc index 58deabfed59..0078aa7fe6a 100644 --- a/src/native/clr/runtime-base/logger.cc +++ b/src/native/clr/runtime-base/logger.cc @@ -60,7 +60,7 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p auto log_and_return = [&category](FILE *f, std::string_view const& path) -> FILE* { if (f != nullptr) { if ((log_categories & category) != 0) { - log_writef (category, LogLevel::Debug, "Opened file '%s' for logging.", path.data ()); + log_write_fmt (category, LogLevel::Debug, "Opened file '%s' for logging.", path.data ()); } } return f; @@ -172,7 +172,7 @@ Logger::init_logging_categories () noexcept if (!file_name.has_value ()) { std::string_view error = to_string (file_name.error ()); - log_writef ( + log_write_fmt ( LOG_DEFAULT, LogLevel::Warn, "Unable to set path to %.*s log file: %.*s", diff --git a/src/native/clr/runtime-base/util.cc b/src/native/clr/runtime-base/util.cc index 98d26503b8f..6b568921294 100644 --- a/src/native/clr/runtime-base/util.cc +++ b/src/native/clr/runtime-base/util.cc @@ -59,7 +59,7 @@ Util::create_public_directory (std::string_view const& dir) // Try to change the mode, just in case chmod (dir.data (), 0777); } else { - log_writef (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s'. %s", dir.data (), std::strerror (errno)); + log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s'. %s", dir.data (), std::strerror (errno)); } } umask (m); @@ -73,7 +73,7 @@ Util::monodroid_fopen (std::string_view const& filename, std::string_view const& */ FILE *ret = fopen (filename.data (), mode.data ()); if (ret == nullptr) { - log_writef (LOG_DEFAULT, LogLevel::Error, "fopen failed for file %s: %s", filename.data (), strerror (errno)); + log_write_fmt (LOG_DEFAULT, LogLevel::Error, "fopen failed for file %s: %s", filename.data (), strerror (errno)); return nullptr; } @@ -88,7 +88,7 @@ void Util::set_world_accessable (std::string_view const& path) } while (r == -1 && errno == EINTR); if (r == -1) { - log_writef (LOG_DEFAULT, LogLevel::Error, "chmod(\"%s\", 0664) failed: %s", path.data (), strerror (errno)); + log_write_fmt (LOG_DEFAULT, LogLevel::Error, "chmod(\"%s\", 0664) failed: %s", path.data (), strerror (errno)); } } @@ -100,7 +100,7 @@ auto Util::set_world_accessible (int fd) noexcept -> bool } while (r == -1 && errno == EINTR); if (r == -1) { - log_writef (LOG_DEFAULT, LogLevel::Error, "fchmod() failed: %s", strerror (errno)); + log_write_fmt (LOG_DEFAULT, LogLevel::Error, "fchmod() failed: %s", strerror (errno)); return false; } diff --git a/src/native/clr/shared/helpers.cc b/src/native/clr/shared/helpers.cc index 61f0cbcfef0..e7d62723880 100644 --- a/src/native/clr/shared/helpers.cc +++ b/src/native/clr/shared/helpers.cc @@ -47,7 +47,7 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo } } - log_writef ( + log_write_fmt ( category, LogLevel::Fatal, "Abort at %s:%u:%u ('%s')", diff --git a/src/native/clr/shared/log_functions.cc b/src/native/clr/shared/log_functions.cc index fc174dbf8ad..ea94cf9016b 100644 --- a/src/native/clr/shared/log_functions.cc +++ b/src/native/clr/shared/log_functions.cc @@ -130,7 +130,7 @@ namespace xamarin::android { } void - log_writef (LogCategories category, LogLevel level, const char *format, ...) noexcept + log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept { size_t map_index = static_cast(level); android_LogPriority priority; @@ -141,10 +141,61 @@ namespace xamarin::android { priority = loglevel_map[map_index]; } - va_list args; const char *safe_format = format == nullptr ? "" : format; - va_start (args, format); __android_log_vprint (priority, category_name (category), safe_format, args); + } + + void + log_write_fmt (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_debug_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Debug, format, args); + va_end (args); + } + + void + log_info_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Info, format, args); + va_end (args); + } + + void + log_warn_fmt (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_error_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Error, format, args); + va_end (args); + } + + void + log_fatal_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Fatal, format, args); va_end (args); } } From 6a4feea3a84da8c8eab11169b011966f64a0d8a4 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 27 May 2026 18:12:26 +0200 Subject: [PATCH 05/16] Address native logging API feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 10 +++--- src/native/clr/host/fastdev-assemblies.cc | 10 +++--- src/native/clr/host/gc-bridge.cc | 12 +++---- .../include/runtime-base/android-system.hh | 32 ++----------------- src/native/clr/include/shared/log_types.hh | 31 +----------------- 5 files changed, 20 insertions(+), 75 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 93cc13c74fc..2a1467c6fb8 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -359,7 +359,7 @@ void CrossReferenceTarget::mark_refs_added_if_needed () noexcept [[gnu::always_inline]] void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] jclass java_class) noexcept { - (xamarin::android::log_error) (LOG_DEFAULT, "Failed to find monodroidAddReferences method"); + (log_error) (LOG_DEFAULT, "Failed to find monodroidAddReferences method"); #if DEBUG abort_if_invalid_pointer_argument (java_class, "java_class"); if (!Logger::gc_spew_enabled ()) [[likely]] { @@ -367,7 +367,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - xamarin::android::log_error_fmt (LOG_GC, "Missing monodroidAddReferences method for object of class %s", optional_string (class_name)); + (log_error) (LOG_GC, "Missing monodroidAddReferences method for object of class %s", optional_string (class_name)); free (class_name); #endif } @@ -375,7 +375,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] [[gnu::always_inline]] void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused]] jclass java_class) noexcept { - (xamarin::android::log_error) (LOG_DEFAULT, "Failed to find monodroidClearReferences method"); + (log_error) (LOG_DEFAULT, "Failed to find monodroidClearReferences method"); #if DEBUG abort_if_invalid_pointer_argument (java_class, "java_class"); if (!Logger::gc_spew_enabled ()) [[likely]] { @@ -383,7 +383,7 @@ void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused } char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - xamarin::android::log_error_fmt (LOG_GC, "Missing monodroidClearReferences method for object of class %s", optional_string (class_name)); + (log_error) (LOG_GC, "Missing monodroidClearReferences method for object of class %s", optional_string (class_name)); free (class_name); #endif } @@ -487,5 +487,5 @@ void BridgeProcessingShared::log_gc_summary () noexcept } } - xamarin::android::log_info_fmt (LOG_GC, "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); + log_info_fmt (LOG_GC, "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive); } diff --git a/src/native/clr/host/fastdev-assemblies.cc b/src/native/clr/host/fastdev-assemblies.cc index dea16523aaf..299ac8a35c1 100644 --- a/src/native/clr/host/fastdev-assemblies.cc +++ b/src/native/clr/host/fastdev-assemblies.cc @@ -18,9 +18,9 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si { size = 0; - std::string const& override_dir_path = AndroidSystem::get_primary_override_dir (); + const char *override_dir_path = AndroidSystem::get_primary_override_dir (); if (!Util::dir_exists (override_dir_path)) [[unlikely]] { - log_debug (LOG_ASSEMBLY, "Override directory '{}' does not exist"sv, override_dir_path); + log_debug (LOG_ASSEMBLY, "Override directory '{}' does not exist"sv, optional_string (override_dir_path)); return nullptr; } @@ -29,9 +29,9 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si if (override_dir_fd < 0) [[unlikely]] { std::lock_guard dir_lock { override_dir_lock }; if (override_dir_fd < 0) [[likely]] { - override_dir = opendir (override_dir_path.c_str ()); + override_dir = opendir (override_dir_path); if (override_dir == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Failed to open override dir '{}'. {}"sv, override_dir_path, strerror (errno)); + log_warn (LOG_ASSEMBLY, "Failed to open override dir '{}'. {}"sv, optional_string (override_dir_path), strerror (errno)); return nullptr; } override_dir_fd = dirfd (override_dir); @@ -42,7 +42,7 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si LOG_ASSEMBLY, "Attempting to load FastDev assembly '{}' from override directory '{}'"sv, name, - override_dir_path + optional_string (override_dir_path) ); if (!Util::file_exists (override_dir_fd, name)) { diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 98d77c5a178..95d3d533cc9 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -49,7 +49,7 @@ void GCBridge::trigger_java_gc (JNIEnv *env) noexcept env->ExceptionDescribe (); env->ExceptionClear (); - (xamarin::android::log_error) (LOG_DEFAULT, "Java GC failed"); + (log_error) (LOG_DEFAULT, "Java GC failed"); } void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept @@ -101,13 +101,13 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg return; } - xamarin::android::log_info_fmt (LOG_GC, "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount); + log_info_fmt (LOG_GC, "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount); JNIEnv *env = OSBridge::ensure_jnienv (); for (size_t i = 0; i < args->ComponentCount; ++i) { const StronglyConnectedComponent &scc = args->Components [i]; - xamarin::android::log_info_fmt (LOG_GC, "group %zu with %zu objects", i, scc.Count); + log_info_fmt (LOG_GC, "group %zu with %zu objects", i, scc.Count); for (size_t j = 0; j < scc.Count; ++j) { log_handle_context (env, scc.Contexts [j]); } @@ -120,7 +120,7 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg for (size_t i = 0; i < args->CrossReferenceCount; ++i) { size_t source_index = args->CrossReferences [i].SourceGroupIndex; size_t dest_index = args->CrossReferences [i].DestinationGroupIndex; - xamarin::android::log_info_fmt (LOG_GC, "xref [%zu] %zu -> %zu", i, source_index, dest_index); + log_info_fmt (LOG_GC, "xref [%zu] %zu -> %zu", i, source_index, dest_index); } } @@ -134,9 +134,9 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept jclass java_class = env->GetObjectClass (handle); if (java_class != nullptr) { char *class_name = Host::get_java_class_name_for_TypeManager (java_class); - xamarin::android::log_info_fmt (LOG_GC, "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name)); + log_info_fmt (LOG_GC, "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name)); free (class_name); } else { - xamarin::android::log_info_fmt (LOG_GC, "gref %p [unknown class]", reinterpret_cast(handle)); + log_info_fmt (LOG_GC, "gref %p [unknown class]", reinterpret_cast(handle)); } } diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh index a2c03962510..ff698da376c 100644 --- a/src/native/clr/include/runtime-base/android-system.hh +++ b/src/native/clr/include/runtime-base/android-system.hh @@ -63,25 +63,14 @@ namespace xamarin::android { running_in_emulator = yesno; } -#if defined(XA_HOST_NATIVEAOT) static auto get_primary_override_dir () noexcept -> const char* { return primary_override_dir; } -#else - static auto get_primary_override_dir () noexcept -> std::string const& - { - return primary_override_dir; - } -#endif static void set_primary_override_dir (jstring_wrapper& home) noexcept { -#if defined(XA_HOST_NATIVEAOT) determine_primary_override_dir (home, primary_override_dir, sizeof (primary_override_dir)); -#else - primary_override_dir = determine_primary_override_dir (home); -#endif } #if !defined(XA_HOST_NATIVEAOT) @@ -90,7 +79,7 @@ namespace xamarin::android { return native_libraries_dir; } - static void create_update_dir (std::string const& override_dir) noexcept + static void create_update_dir (const char *override_dir) noexcept { if constexpr (Constants::is_release_build) { /* @@ -106,7 +95,7 @@ namespace xamarin::android { } } - log_debug (LOG_DEFAULT, "Creating public update directory: `{}`", override_dir); + log_debug (LOG_DEFAULT, "Creating public update directory: `{}`", optional_string (override_dir)); Util::create_public_directory (override_dir); } #endif @@ -145,7 +134,6 @@ namespace xamarin::android { embedded_dso_mode_enabled = yesno; } -#if defined(XA_HOST_NATIVEAOT) static void determine_primary_override_dir (jstring_wrapper &home, char *buffer, size_t buffer_size) noexcept { dynamic_local_string name { home.get_cstr () }; @@ -156,27 +144,13 @@ namespace xamarin::android { snprintf (buffer, buffer_size, "%s", name.get ()); } -#else - static auto determine_primary_override_dir (jstring_wrapper &home) noexcept -> std::string - { - dynamic_local_string name { home.get_cstr () }; - name.append ("/") - .append (Constants::OVERRIDE_DIRECTORY_NAME) - .append ("/") - .append (Constants::android_lib_abi); - - return {name.get (), name.length ()}; - } -#endif private: static inline long max_gref_count = 0; static inline bool running_in_emulator = false; static inline bool embedded_dso_mode_enabled = false; -#if defined(XA_HOST_NATIVEAOT) static inline char primary_override_dir[SENSIBLE_PATH_MAX] {}; -#else - static inline std::string primary_override_dir; +#if !defined(XA_HOST_NATIVEAOT) static inline std::string native_libraries_dir; #if defined (DEBUG) diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh index 6dc4819158b..330f1e546b7 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 #if !defined(XA_HOST_NATIVEAOT) #include @@ -69,36 +70,6 @@ namespace xamarin::android { log_write (category, level, message.data ()); } - [[gnu::always_inline]] - static inline void (log_debug) (LogCategories category, const char *message) noexcept - { - log_write (category, LogLevel::Debug, message); - } - - [[gnu::always_inline]] - static inline void (log_info) (LogCategories category, const char *message) noexcept - { - log_write (category, LogLevel::Info, message); - } - - [[gnu::always_inline]] - static inline void (log_warn) (LogCategories category, const char *message) noexcept - { - log_write (category, LogLevel::Warn, message); - } - - [[gnu::always_inline]] - static inline void (log_error) (LogCategories category, const char *message) noexcept - { - log_write (category, LogLevel::Error, message); - } - - [[gnu::always_inline]] - static inline void (log_fatal) (LogCategories category, const char *message) noexcept - { - log_write (category, LogLevel::Fatal, message); - } - void log_debug_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); void log_info_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); void log_warn_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); From 948f0fc8f5c8a206754c8dfffa4ecac7db703c67 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 27 May 2026 20:58:44 +0200 Subject: [PATCH 06/16] Avoid string_view overreads in native logs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/include/runtime-base/util.hh | 24 ++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/native/clr/include/runtime-base/util.hh b/src/native/clr/include/runtime-base/util.hh index b94746c679f..718b4f5d2e8 100644 --- a/src/native/clr/include/runtime-base/util.hh +++ b/src/native/clr/include/runtime-base/util.hh @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,17 @@ namespace xamarin::android { void *area; size_t size; }; + + static constexpr auto printf_precision (std::string_view const& value) noexcept -> int + { + constexpr size_t max_precision = static_cast(std::numeric_limits::max ()); + return value.length () > max_precision ? std::numeric_limits::max () : static_cast(value.length ()); + } + + static constexpr auto printf_string (std::string_view const& value) noexcept -> const char* + { + return value.data () == nullptr ? "" : value.data (); + } } class Util @@ -214,10 +226,11 @@ namespace xamarin::android { Helpers::abort_applicationf ( LOG_ASSEMBLY, std::source_location::current (), - "Could not mmap APK fd %d: %s; File=%s", + "Could not mmap APK fd %d: %s; File=%.*s", fd, strerror (errno), - filename.data () + detail::printf_precision (filename), + detail::printf_string (filename) ); } @@ -229,7 +242,7 @@ namespace xamarin::android { log_write_fmt ( LOG_ASSEMBLY, LogLevel::Info, - " mmap_start: %-8p; mmap_end: %-8p mmap_len: %-12zu file_start: %-8p file_end: %-8p file_len: %-12zu apk descriptor: %d file: %s", + " mmap_start: %-8p; mmap_end: %-8p mmap_len: %-12zu file_start: %-8p file_end: %-8p file_len: %-12zu apk descriptor: %d file: %.*s", mmap_info.area, pointer_add (mmap_info.area, mmap_info.size), mmap_info.size, @@ -237,7 +250,8 @@ namespace xamarin::android { pointer_add (file_info.area, file_info.size), file_info.size, fd, - filename.data () + detail::printf_precision (filename), + detail::printf_string (filename) ); } @@ -262,7 +276,7 @@ namespace xamarin::android { elf_header->e_ident[EI_MAG2] != ELFMAG2 || elf_header->e_ident[EI_MAG3] != ELFMAG3) { if (should_log (LOG_ASSEMBLY)) { - log_write_fmt (LOG_ASSEMBLY, LogLevel::Debug, "Not an ELF image: %s", file_name.data ()); + log_write_fmt (LOG_ASSEMBLY, LogLevel::Debug, "Not an ELF image: %.*s", detail::printf_precision (file_name), detail::printf_string (file_name)); } // Not an ELF image, just return what we mmapped before return { map_info.area, map_info.size }; From 60f1a7b00cd647d49c04818a5163502e5f636907 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 27 May 2026 23:07:30 +0200 Subject: [PATCH 07/16] Fix native header compile dependencies Add the missing standard and logging declarations needed by the native runtime headers when they are compiled directly by the Android runtime ninja builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/include/runtime-base/android-system.hh | 1 + src/native/common/include/runtime-base/strings.hh | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh index ff698da376c..f3d2969bab2 100644 --- a/src/native/clr/include/runtime-base/android-system.hh +++ b/src/native/clr/include/runtime-base/android-system.hh @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include diff --git a/src/native/common/include/runtime-base/strings.hh b/src/native/common/include/runtime-base/strings.hh index 561b558d7ad..87098365967 100644 --- a/src/native/common/include/runtime-base/strings.hh +++ b/src/native/common/include/runtime-base/strings.hh @@ -12,6 +12,7 @@ #include #include +#include #if defined(XA_HOST_MONOVM) #include @@ -22,6 +23,8 @@ using Constants = xamarin::android::internal::SharedConstants; #endif namespace xamarin::android { + void log_write (LogCategories category, LogLevel level, const char *message) noexcept; + static constexpr size_t SENSIBLE_TYPE_NAME_LENGTH = 128uz; static constexpr size_t SENSIBLE_PATH_MAX = 256uz; From 5dfa7f9350274175427080b0fffb66e0e6056526 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 00:20:50 +0200 Subject: [PATCH 08/16] Fix native runtime local build failures Avoid ambiguous gref log overload resolution, insert temporary GC bridge peers into robin_map without mutating through the iterator proxy, and include robin_map headers in the NativeAOT host build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 10 ++++++---- src/native/clr/host/os-bridge.cc | 2 +- src/native/clr/include/host/os-bridge.hh | 9 ++++++++- src/native/nativeaot/host/CMakeLists.txt | 1 + 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 2a1467c6fb8..539fdbd261b 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -87,11 +87,13 @@ void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, { // Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one if (scc.Count == 0) { - auto [peer_entry, inserted] = temporary_peers.emplace (scc_index, nullptr); - abort_unless (inserted, "Temporary peer must not already exist"); + abort_unless (temporary_peers.find (scc_index) == temporary_peers.end (), "Temporary peer must not already exist"); + + jobject temporary_peer = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); + abort_unless (temporary_peer != nullptr, "Failed to create GC bridge temporary peer"); - peer_entry->second = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); - abort_unless (peer_entry->second != nullptr, "Failed to create GC bridge temporary peer"); + auto [peer_entry, inserted] = temporary_peers.emplace (scc_index, temporary_peer); + abort_unless (inserted, "Temporary peer must not already exist"); return; } diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc index 80d88df5a08..e089afd767e 100644 --- a/src/native/clr/host/os-bridge.cc +++ b/src/native/clr/host/os-bridge.cc @@ -175,7 +175,7 @@ void OSBridge::_monodroid_gref_log (const char *message) noexcept write_gref_message (message, gref_log); } -void OSBridge::_monodroid_gref_log (const char *format, ...) noexcept +void OSBridge::_monodroid_gref_log_fmt (const char *format, ...) noexcept { FILE *gref_log = Logger::gref_log (); if (!Logger::gref_to_logcat () && gref_log == nullptr) { diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh index 4ffbf57a3bf..a7324f52d47 100644 --- a/src/native/clr/include/host/os-bridge.hh +++ b/src/native/clr/include/host/os-bridge.hh @@ -31,7 +31,13 @@ namespace xamarin::android { static auto _monodroid_weak_gref_dec () noexcept -> int; static void _monodroid_gref_log (const char *message) noexcept; - static void _monodroid_gref_log (const char *format, ...) noexcept __attribute__ ((format (printf, 1, 2))); + + template + static void _monodroid_gref_log (const char *format, Arg arg, Args... args) noexcept + { + _monodroid_gref_log_fmt (format, arg, args...); + } + static auto _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) noexcept -> int; static void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from) noexcept; static void _monodroid_weak_gref_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from); @@ -58,6 +64,7 @@ namespace xamarin::android { private: static void _write_stack_trace (FILE *to, const char *const from, LogCategories = LOG_NONE) noexcept; + static void _monodroid_gref_log_fmt (const char *format, ...) noexcept __attribute__ ((format (printf, 1, 2))); static void log_it (LogCategories category, const char *line, FILE *to, const char *const from, bool logcat_enabled) noexcept; static void log_itf (LogCategories category, FILE *to, const char *from, bool logcat_enabled, const char *format, ...) noexcept __attribute__ ((format (printf, 5, 6))); diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index e072da41620..9a3cdc56b58 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -106,6 +106,7 @@ macro(lib_target_options TARGET_NAME) ${TARGET_NAME} BEFORE PRIVATE ${EXTERNAL_DIR} + ${ROBIN_MAP_DIR}/include ) target_include_directories( From e38bcec6c5ef0169092dc3eeb7d73465b2cbbd0d Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 06:46:36 +0200 Subject: [PATCH 09/16] Avoid robin_map in NativeAOT bridge processing Keep the robin_map-backed temporary peer lookup for the CLR host, but use a simple indexed JNI handle table for the NativeAOT host so app-linked static runtimes do not pull in libc++/c++abi symbols. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 80 +++++++++++++++++-- .../include/host/bridge-processing-shared.hh | 12 ++- src/native/nativeaot/host/CMakeLists.txt | 1 - 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 539fdbd261b..3a706e4a482 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -37,11 +37,18 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, c Helpers::abort_application (LOG_GC, "CrossReferences member of the cross references arguments structure is NULL"sv); } +#if defined (XA_HOST_NATIVEAOT) + if (args->ComponentCount > 0) { + temporary_peers = static_cast (std::calloc (args->ComponentCount, sizeof (jobject))); + abort_unless (temporary_peers != nullptr, "Failed to allocate GC bridge temporary peer map"); + } +#endif } BridgeProcessingShared::~BridgeProcessingShared () noexcept { release_temporary_peers (); + free_temporary_peer_map (); } void BridgeProcessingShared::process () noexcept @@ -87,13 +94,12 @@ void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, { // Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one if (scc.Count == 0) { - abort_unless (temporary_peers.find (scc_index) == temporary_peers.end (), "Temporary peer must not already exist"); + abort_unless (!has_temporary_peer (scc_index), "Temporary peer must not already exist"); jobject temporary_peer = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); abort_unless (temporary_peer != nullptr, "Failed to create GC bridge temporary peer"); - auto [peer_entry, inserted] = temporary_peers.emplace (scc_index, temporary_peer); - abort_unless (inserted, "Temporary peer must not already exist"); + add_temporary_peer (scc_index, temporary_peer); return; } @@ -112,18 +118,69 @@ CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size const StronglyConnectedComponent &scc = cross_refs->Components [scc_index]; if (scc.Count == 0) { - auto peer_entry = temporary_peers.find (scc_index); - abort_unless (peer_entry != temporary_peers.end (), "Temporary peer must be found in the map"); - abort_unless (peer_entry->second != nullptr, "Temporary peer must not be null"); - return { .is_temporary_peer = true, .temporary_peer = peer_entry->second }; + jobject temporary_peer = get_temporary_peer (scc_index); + abort_unless (temporary_peer != nullptr, "Temporary peer must not be null"); + return { .is_temporary_peer = true, .temporary_peer = temporary_peer }; } abort_unless (scc.Contexts [0] != nullptr, "SCC must have at least one context"); return { .is_temporary_peer = false, .context = scc.Contexts [0] }; } +bool BridgeProcessingShared::has_temporary_peer (size_t scc_index) noexcept +{ +#if defined (XA_HOST_NATIVEAOT) + abort_unless (scc_index < cross_refs->ComponentCount, "Temporary peer index must be in range"); + abort_unless (temporary_peers != nullptr, "Temporary peer map must not be null"); + return temporary_peers [scc_index] != nullptr; +#else + return temporary_peers.find (scc_index) != temporary_peers.end (); +#endif +} + +void BridgeProcessingShared::add_temporary_peer (size_t scc_index, jobject temporary_peer) noexcept +{ +#if defined (XA_HOST_NATIVEAOT) + abort_unless (scc_index < cross_refs->ComponentCount, "Temporary peer index must be in range"); + abort_unless (temporary_peers != nullptr, "Temporary peer map must not be null"); + abort_unless (temporary_peers [scc_index] == nullptr, "Temporary peer must not already exist"); + temporary_peers [scc_index] = temporary_peer; +#else + auto [peer_entry, inserted] = temporary_peers.emplace (scc_index, temporary_peer); + abort_unless (inserted, "Temporary peer must not already exist"); +#endif +} + +jobject BridgeProcessingShared::get_temporary_peer (size_t scc_index) noexcept +{ +#if defined (XA_HOST_NATIVEAOT) + abort_unless (scc_index < cross_refs->ComponentCount, "Temporary peer index must be in range"); + abort_unless (temporary_peers != nullptr, "Temporary peer map must not be null"); + jobject temporary_peer = temporary_peers [scc_index]; + abort_unless (temporary_peer != nullptr, "Temporary peer must be found in the map"); + return temporary_peer; +#else + auto peer_entry = temporary_peers.find (scc_index); + abort_unless (peer_entry != temporary_peers.end (), "Temporary peer must be found in the map"); + return peer_entry->second; +#endif +} + void BridgeProcessingShared::release_temporary_peers () noexcept { +#if defined (XA_HOST_NATIVEAOT) + if (temporary_peers == nullptr) { + return; + } + + for (size_t i = 0; i < cross_refs->ComponentCount; i++) { + jobject temporary_peer = temporary_peers [i]; + if (temporary_peer != nullptr) { + env->DeleteLocalRef (temporary_peer); + temporary_peers [i] = nullptr; + } + } +#else for (const auto &entry : temporary_peers) { jobject temporary_peer = entry.second; if (temporary_peer != nullptr) { @@ -131,6 +188,15 @@ void BridgeProcessingShared::release_temporary_peers () noexcept } } temporary_peers.clear (); +#endif +} + +void BridgeProcessingShared::free_temporary_peer_map () noexcept +{ +#if defined (XA_HOST_NATIVEAOT) + std::free (temporary_peers); + temporary_peers = nullptr; +#endif } // caller must ensure that scc.Count > 1 diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh index 277c8371378..a8b4659af13 100644 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ b/src/native/clr/include/host/bridge-processing-shared.hh @@ -4,6 +4,7 @@ #include +#if !defined (XA_HOST_NATIVEAOT) // NDEBUG causes robin_map.h not to include which, in turn, prevents indirect inclusion of . // conflicts with our std::mutex definition in cppcompat.hh #if !defined (NDEBUG) @@ -27,6 +28,7 @@ #undef NDEBUG #undef NDEBUG_UNDEFINE #endif +#endif // !defined (XA_HOST_NATIVEAOT) #include #include @@ -54,7 +56,11 @@ struct BridgeProcessingCallbacks class BridgeProcessingShared { +#if defined (XA_HOST_NATIVEAOT) + using temporary_peer_map = jobject*; +#else using temporary_peer_map = tsl::robin_map; +#endif public: explicit BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *callbacks = nullptr) noexcept; @@ -65,7 +71,7 @@ public: private: JNIEnv* env; MarkCrossReferencesArgs *cross_refs; - temporary_peer_map temporary_peers; + temporary_peer_map temporary_peers {}; BridgeProcessingCallbacks callbacks; static inline jclass GCUserPeer_class = nullptr; @@ -73,7 +79,11 @@ private: void prepare_for_java_collection () noexcept; void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept; + bool has_temporary_peer (size_t scc_index) noexcept; + void add_temporary_peer (size_t scc_index, jobject temporary_peer) noexcept; + jobject get_temporary_peer (size_t scc_index) noexcept; void release_temporary_peers () noexcept; + void free_temporary_peer_map () noexcept; void take_weak_global_ref (const HandleContext &context) noexcept; void add_circular_references (const StronglyConnectedComponent &scc) noexcept; diff --git a/src/native/nativeaot/host/CMakeLists.txt b/src/native/nativeaot/host/CMakeLists.txt index 9a3cdc56b58..e072da41620 100644 --- a/src/native/nativeaot/host/CMakeLists.txt +++ b/src/native/nativeaot/host/CMakeLists.txt @@ -106,7 +106,6 @@ macro(lib_target_options TARGET_NAME) ${TARGET_NAME} BEFORE PRIVATE ${EXTERNAL_DIR} - ${ROBIN_MAP_DIR}/include ) target_include_directories( From 8ada8d24a64f5b87326bb2ee2679c397e2c77f65 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 07:10:01 +0200 Subject: [PATCH 10/16] Use sparse temporary peer list for NativeAOT Avoid allocating a temporary peer slot for every GC bridge component in the NativeAOT host. The NativeAOT path now stores only the temporary peers it creates, while the CLR host continues to use robin_map. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 61 ++++++++++++------- .../include/host/bridge-processing-shared.hh | 13 +++- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 3a706e4a482..b9d1f87938f 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -38,9 +38,16 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, c } #if defined (XA_HOST_NATIVEAOT) - if (args->ComponentCount > 0) { - temporary_peers = static_cast (std::calloc (args->ComponentCount, sizeof (jobject))); - abort_unless (temporary_peers != nullptr, "Failed to allocate GC bridge temporary peer map"); + for (size_t i = 0; i < args->ComponentCount; i++) { + const StronglyConnectedComponent &scc = args->Components [i]; + if (scc.Count == 0) { + temporary_peers.capacity++; + } + } + + if (temporary_peers.capacity > 0) { + temporary_peers.peers = static_cast (std::calloc (temporary_peers.capacity, sizeof (TemporaryPeer))); + abort_unless (temporary_peers.peers != nullptr, "Failed to allocate GC bridge temporary peer map"); } #endif } @@ -130,9 +137,13 @@ CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size bool BridgeProcessingShared::has_temporary_peer (size_t scc_index) noexcept { #if defined (XA_HOST_NATIVEAOT) - abort_unless (scc_index < cross_refs->ComponentCount, "Temporary peer index must be in range"); - abort_unless (temporary_peers != nullptr, "Temporary peer map must not be null"); - return temporary_peers [scc_index] != nullptr; + for (size_t i = 0; i < temporary_peers.count; i++) { + if (temporary_peers.peers [i].scc_index == scc_index) { + return true; + } + } + + return false; #else return temporary_peers.find (scc_index) != temporary_peers.end (); #endif @@ -141,10 +152,12 @@ bool BridgeProcessingShared::has_temporary_peer (size_t scc_index) noexcept void BridgeProcessingShared::add_temporary_peer (size_t scc_index, jobject temporary_peer) noexcept { #if defined (XA_HOST_NATIVEAOT) - abort_unless (scc_index < cross_refs->ComponentCount, "Temporary peer index must be in range"); - abort_unless (temporary_peers != nullptr, "Temporary peer map must not be null"); - abort_unless (temporary_peers [scc_index] == nullptr, "Temporary peer must not already exist"); - temporary_peers [scc_index] = temporary_peer; + abort_unless (temporary_peers.peers != nullptr, "Temporary peer map must not be null"); + abort_unless (temporary_peers.count < temporary_peers.capacity, "Temporary peer map must not be full"); + + TemporaryPeer &entry = temporary_peers.peers [temporary_peers.count++]; + entry.scc_index = scc_index; + entry.peer = temporary_peer; #else auto [peer_entry, inserted] = temporary_peers.emplace (scc_index, temporary_peer); abort_unless (inserted, "Temporary peer must not already exist"); @@ -154,11 +167,15 @@ void BridgeProcessingShared::add_temporary_peer (size_t scc_index, jobject tempo jobject BridgeProcessingShared::get_temporary_peer (size_t scc_index) noexcept { #if defined (XA_HOST_NATIVEAOT) - abort_unless (scc_index < cross_refs->ComponentCount, "Temporary peer index must be in range"); - abort_unless (temporary_peers != nullptr, "Temporary peer map must not be null"); - jobject temporary_peer = temporary_peers [scc_index]; - abort_unless (temporary_peer != nullptr, "Temporary peer must be found in the map"); - return temporary_peer; + for (size_t i = 0; i < temporary_peers.count; i++) { + TemporaryPeer &entry = temporary_peers.peers [i]; + if (entry.scc_index == scc_index) { + return entry.peer; + } + } + + abort_unless (false, "Temporary peer must be found in the map"); + return nullptr; #else auto peer_entry = temporary_peers.find (scc_index); abort_unless (peer_entry != temporary_peers.end (), "Temporary peer must be found in the map"); @@ -169,17 +186,19 @@ jobject BridgeProcessingShared::get_temporary_peer (size_t scc_index) noexcept void BridgeProcessingShared::release_temporary_peers () noexcept { #if defined (XA_HOST_NATIVEAOT) - if (temporary_peers == nullptr) { + if (temporary_peers.peers == nullptr) { return; } - for (size_t i = 0; i < cross_refs->ComponentCount; i++) { - jobject temporary_peer = temporary_peers [i]; + for (size_t i = 0; i < temporary_peers.count; i++) { + TemporaryPeer &entry = temporary_peers.peers [i]; + jobject temporary_peer = entry.peer; if (temporary_peer != nullptr) { env->DeleteLocalRef (temporary_peer); - temporary_peers [i] = nullptr; + entry.peer = nullptr; } } + temporary_peers.count = 0; #else for (const auto &entry : temporary_peers) { jobject temporary_peer = entry.second; @@ -194,8 +213,8 @@ void BridgeProcessingShared::release_temporary_peers () noexcept void BridgeProcessingShared::free_temporary_peer_map () noexcept { #if defined (XA_HOST_NATIVEAOT) - std::free (temporary_peers); - temporary_peers = nullptr; + std::free (temporary_peers.peers); + temporary_peers = {}; #endif } diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh index a8b4659af13..83a21f42a7b 100644 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ b/src/native/clr/include/host/bridge-processing-shared.hh @@ -57,7 +57,18 @@ struct BridgeProcessingCallbacks class BridgeProcessingShared { #if defined (XA_HOST_NATIVEAOT) - using temporary_peer_map = jobject*; + struct TemporaryPeer + { + size_t scc_index; + jobject peer; + }; + + struct temporary_peer_map + { + TemporaryPeer *peers; + size_t count; + size_t capacity; + }; #else using temporary_peer_map = tsl::robin_map; #endif From 4c2dc5b05f434f79d65684872cfddab9f69397b9 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 07:15:53 +0200 Subject: [PATCH 11/16] Share temporary peer storage across runtimes Remove the CoreCLR-only robin_map implementation so bridge processing uses the same sparse temporary peer list for both CoreCLR and NativeAOT. Leave a note about a possible future lookup optimization using the SCC Count field. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 31 ++----------------- .../include/host/bridge-processing-shared.hh | 30 ------------------ 2 files changed, 2 insertions(+), 59 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index b9d1f87938f..74a4a849f59 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -37,7 +37,6 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, c Helpers::abort_application (LOG_GC, "CrossReferences member of the cross references arguments structure is NULL"sv); } -#if defined (XA_HOST_NATIVEAOT) for (size_t i = 0; i < args->ComponentCount; i++) { const StronglyConnectedComponent &scc = args->Components [i]; if (scc.Count == 0) { @@ -49,7 +48,6 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, c temporary_peers.peers = static_cast (std::calloc (temporary_peers.capacity, sizeof (TemporaryPeer))); abort_unless (temporary_peers.peers != nullptr, "Failed to allocate GC bridge temporary peer map"); } -#endif } BridgeProcessingShared::~BridgeProcessingShared () noexcept @@ -136,7 +134,6 @@ CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size bool BridgeProcessingShared::has_temporary_peer (size_t scc_index) noexcept { -#if defined (XA_HOST_NATIVEAOT) for (size_t i = 0; i < temporary_peers.count; i++) { if (temporary_peers.peers [i].scc_index == scc_index) { return true; @@ -144,29 +141,22 @@ bool BridgeProcessingShared::has_temporary_peer (size_t scc_index) noexcept } return false; -#else - return temporary_peers.find (scc_index) != temporary_peers.end (); -#endif } void BridgeProcessingShared::add_temporary_peer (size_t scc_index, jobject temporary_peer) noexcept { -#if defined (XA_HOST_NATIVEAOT) abort_unless (temporary_peers.peers != nullptr, "Temporary peer map must not be null"); abort_unless (temporary_peers.count < temporary_peers.capacity, "Temporary peer map must not be full"); TemporaryPeer &entry = temporary_peers.peers [temporary_peers.count++]; entry.scc_index = scc_index; entry.peer = temporary_peer; -#else - auto [peer_entry, inserted] = temporary_peers.emplace (scc_index, temporary_peer); - abort_unless (inserted, "Temporary peer must not already exist"); -#endif } jobject BridgeProcessingShared::get_temporary_peer (size_t scc_index) noexcept { -#if defined (XA_HOST_NATIVEAOT) + // If this lookup ever shows up in profiles, we can try storing the peer-list index in the + // empty SCC's Count field as a negative value. Keep the bridge input immutable for now. for (size_t i = 0; i < temporary_peers.count; i++) { TemporaryPeer &entry = temporary_peers.peers [i]; if (entry.scc_index == scc_index) { @@ -176,16 +166,10 @@ jobject BridgeProcessingShared::get_temporary_peer (size_t scc_index) noexcept abort_unless (false, "Temporary peer must be found in the map"); return nullptr; -#else - auto peer_entry = temporary_peers.find (scc_index); - abort_unless (peer_entry != temporary_peers.end (), "Temporary peer must be found in the map"); - return peer_entry->second; -#endif } void BridgeProcessingShared::release_temporary_peers () noexcept { -#if defined (XA_HOST_NATIVEAOT) if (temporary_peers.peers == nullptr) { return; } @@ -199,23 +183,12 @@ void BridgeProcessingShared::release_temporary_peers () noexcept } } temporary_peers.count = 0; -#else - for (const auto &entry : temporary_peers) { - jobject temporary_peer = entry.second; - if (temporary_peer != nullptr) { - env->DeleteLocalRef (temporary_peer); - } - } - temporary_peers.clear (); -#endif } void BridgeProcessingShared::free_temporary_peer_map () noexcept { -#if defined (XA_HOST_NATIVEAOT) std::free (temporary_peers.peers); temporary_peers = {}; -#endif } // caller must ensure that scc.Count > 1 diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh index 83a21f42a7b..3d165094306 100644 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ b/src/native/clr/include/host/bridge-processing-shared.hh @@ -4,32 +4,6 @@ #include -#if !defined (XA_HOST_NATIVEAOT) -// NDEBUG causes robin_map.h not to include which, in turn, prevents indirect inclusion of . -// conflicts with our std::mutex definition in cppcompat.hh -#if !defined (NDEBUG) -#define NDEBUG -#define NDEBUG_UNDEFINE -#endif - -// hush some compiler warnings -#if defined (__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" -#endif // __clang__ - -#include - -#if defined (__clang__) -#pragma clang diagnostic pop -#endif // __clang__ - -#if defined (NDEBUG_UNDEFINE) -#undef NDEBUG -#undef NDEBUG_UNDEFINE -#endif -#endif // !defined (XA_HOST_NATIVEAOT) - #include #include #include @@ -56,7 +30,6 @@ struct BridgeProcessingCallbacks class BridgeProcessingShared { -#if defined (XA_HOST_NATIVEAOT) struct TemporaryPeer { size_t scc_index; @@ -69,9 +42,6 @@ class BridgeProcessingShared size_t count; size_t capacity; }; -#else - using temporary_peer_map = tsl::robin_map; -#endif public: explicit BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *callbacks = nullptr) noexcept; From 4c6f100f207f88d9a4f212a88952fe465326eafd Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 08:39:54 +0200 Subject: [PATCH 12/16] Use RAII temporary peer map for GC bridge Encapsulate temporary peer storage in a dedicated RAII type and encode temporary peer indexes in the SCC count while the bridge owns the GC cross-reference arguments. Share the implementation across CoreCLR and NativeAOT, keeping only the NativeAOT GCUserPeerable callbacks runtime-specific. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 238 ++++++++++-------- src/native/clr/host/gc-bridge.cc | 15 +- .../include/host/bridge-processing-shared.hh | 63 +++-- src/native/clr/include/host/gc-bridge.hh | 1 + .../nativeaot/host/bridge-processing.cc | 14 +- src/native/nativeaot/host/host.cc | 10 +- 6 files changed, 181 insertions(+), 160 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 74a4a849f59..c45c6b4c543 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -1,4 +1,3 @@ -#include #include #include @@ -9,15 +8,107 @@ using namespace xamarin::android; -void BridgeProcessingShared::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept +TemporaryPeerMap::TemporaryPeerMap (JNIEnv *jni_env, MarkCrossReferencesArgs *args) noexcept + : env{ jni_env }, + cross_refs{ args } +{ + size_t map_capacity = 0; + for (size_t i = 0; i < cross_refs->ComponentCount; i++) { + const StronglyConnectedComponent &scc = cross_refs->Components [i]; + abort_unless (!is_temporary_peer_index (scc.Count), "SCC count must not use the temporary peer marker bit"); + if (scc.Count == 0) { + map_capacity++; + } + } + + if (map_capacity == 0) { + return; + } + + capacity = map_capacity; + peers = static_cast (std::calloc (capacity, sizeof (jobject))); + abort_unless (peers != nullptr, "Failed to allocate GC bridge temporary peer map"); +} + +TemporaryPeerMap::~TemporaryPeerMap () noexcept +{ + if (peers == nullptr) { + return; + } + + for (size_t i = 0; i < count; i++) { + jobject temporary_peer = peers [i]; + if (temporary_peer != nullptr) { + env->DeleteLocalRef (temporary_peer); + peers [i] = nullptr; + } + } + + for (size_t i = 0; i < cross_refs->ComponentCount; i++) { + StronglyConnectedComponent &scc = cross_refs->Components [i]; + if (is_temporary_peer_index (scc.Count)) { + scc.Count = 0; + } + } + + count = 0; + std::free (peers); + peers = nullptr; + capacity = 0; +} + +void TemporaryPeerMap::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept { abort_if_invalid_pointer_argument (env, "env"); abort_if_invalid_pointer_argument (runtimeClass, "runtimeClass"); - GCUserPeer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true); - GCUserPeer_ctor = env->GetMethodID (GCUserPeer_class, "", "()V"); + peer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true); + peer_ctor = env->GetMethodID (peer_class, "", "()V"); - abort_unless (GCUserPeer_class != nullptr && GCUserPeer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!"); + abort_unless (peer_class != nullptr && peer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!"); +} + +void TemporaryPeerMap::add (StronglyConnectedComponent &scc) noexcept +{ + abort_unless (peers != nullptr, "Temporary peer map must not be null"); + abort_unless (count < capacity, "Temporary peer map must not be full"); + + jobject temporary_peer = env->NewObject (peer_class, peer_ctor); + abort_unless (temporary_peer != nullptr, "Failed to create GC bridge temporary peer"); + + size_t temporary_peer_index = count++; + peers [temporary_peer_index] = temporary_peer; + scc.Count = encode_temporary_peer_index (temporary_peer_index); +} + +bool TemporaryPeerMap::has_temporary_peer (const StronglyConnectedComponent &scc) const noexcept +{ + return is_temporary_peer_index (scc.Count); +} + +jobject TemporaryPeerMap::get (const StronglyConnectedComponent &scc) const noexcept +{ + size_t temporary_peer_index = decode_temporary_peer_index (scc.Count); + abort_unless (temporary_peer_index < count, "Temporary peer index must be in range"); + + return peers [temporary_peer_index]; +} + +bool TemporaryPeerMap::is_temporary_peer_index (size_t count) noexcept +{ + return (count & temporary_peer_index_sign_bit) != 0; +} + +size_t TemporaryPeerMap::encode_temporary_peer_index (size_t index) noexcept +{ + abort_unless (!is_temporary_peer_index (index), "Temporary peer index is too large"); + return ~index; +} + +size_t TemporaryPeerMap::decode_temporary_peer_index (size_t count) noexcept +{ + abort_unless (is_temporary_peer_index (count), "Temporary peer index must be negative"); + return ~count; } BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *host_callbacks) noexcept @@ -36,24 +127,6 @@ BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, c if (args->CrossReferenceCount > 0 && args->CrossReferences == nullptr) [[unlikely]] { Helpers::abort_application (LOG_GC, "CrossReferences member of the cross references arguments structure is NULL"sv); } - - for (size_t i = 0; i < args->ComponentCount; i++) { - const StronglyConnectedComponent &scc = args->Components [i]; - if (scc.Count == 0) { - temporary_peers.capacity++; - } - } - - if (temporary_peers.capacity > 0) { - temporary_peers.peers = static_cast (std::calloc (temporary_peers.capacity, sizeof (TemporaryPeer))); - abort_unless (temporary_peers.peers != nullptr, "Failed to allocate GC bridge temporary peer map"); - } -} - -BridgeProcessingShared::~BridgeProcessingShared () noexcept -{ - release_temporary_peers (); - free_temporary_peer_map (); } void BridgeProcessingShared::process () noexcept @@ -66,23 +139,9 @@ void BridgeProcessingShared::process () noexcept void BridgeProcessingShared::prepare_for_java_collection () noexcept { - // Before looking at xrefs, scan the SCCs. During collection, an SCC has to behave like a - // single object. If the number of objects in the SCC is anything other than 1, the SCC - // must be doctored to mimic that one-object nature. - for (size_t i = 0; i < cross_refs->ComponentCount; i++) { - const StronglyConnectedComponent &scc = cross_refs->Components [i]; - prepare_scc_for_java_collection (i, scc); - } - - // Add the cross scc refs - for (size_t i = 0; i < cross_refs->CrossReferenceCount; i++) { - const ComponentCrossReference &xref = cross_refs->CrossReferences [i]; - add_cross_reference (xref.SourceGroupIndex, xref.DestinationGroupIndex); - } - - // With cross references processed, the temporary peer list can be released - release_temporary_peers (); + prepare_sccs_and_cross_references_for_java_collection (); + // Temporary peer indexes have been reset, so SCC counts are safe to use normally again. // Switch global to weak references for (size_t i = 0; i < cross_refs->ComponentCount; i++) { const StronglyConnectedComponent &scc = cross_refs->Components [i]; @@ -95,16 +154,32 @@ void BridgeProcessingShared::prepare_for_java_collection () noexcept } } -void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept +void BridgeProcessingShared::prepare_sccs_and_cross_references_for_java_collection () noexcept { - // Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one - if (scc.Count == 0) { - abort_unless (!has_temporary_peer (scc_index), "Temporary peer must not already exist"); + TemporaryPeerMap temporary_peers { env, cross_refs }; - jobject temporary_peer = env->NewObject (GCUserPeer_class, GCUserPeer_ctor); - abort_unless (temporary_peer != nullptr, "Failed to create GC bridge temporary peer"); + // Before looking at xrefs, scan the SCCs. During collection, an SCC has to behave like a + // single object. If the number of objects in the SCC is anything other than 1, the SCC + // must be doctored to mimic that one-object nature. + for (size_t i = 0; i < cross_refs->ComponentCount; i++) { + const StronglyConnectedComponent &scc = cross_refs->Components [i]; + prepare_scc_for_java_collection (i, scc, temporary_peers); + } + + // Add the cross scc refs + for (size_t i = 0; i < cross_refs->CrossReferenceCount; i++) { + const ComponentCrossReference &xref = cross_refs->CrossReferences [i]; + add_cross_reference (xref.SourceGroupIndex, xref.DestinationGroupIndex, temporary_peers); + } +} - add_temporary_peer (scc_index, temporary_peer); +void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc, TemporaryPeerMap &temporary_peers) noexcept +{ + // Before looking at xrefs, scan the SCCs. During collection, an SCC has to behave like a + // single object. If the number of objects in the SCC is anything other than 1, the SCC + // must be doctored to mimic that one-object nature. + if (scc.Count == 0) { + temporary_peers.add (cross_refs->Components [scc_index]); return; } @@ -118,12 +193,12 @@ void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, add_circular_references (scc); } -CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size_t scc_index) noexcept +CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size_t scc_index, TemporaryPeerMap &temporary_peers) noexcept { const StronglyConnectedComponent &scc = cross_refs->Components [scc_index]; - if (scc.Count == 0) { - jobject temporary_peer = get_temporary_peer (scc_index); + if (temporary_peers.has_temporary_peer (scc)) { + jobject temporary_peer = temporary_peers.get (scc); abort_unless (temporary_peer != nullptr, "Temporary peer must not be null"); return { .is_temporary_peer = true, .temporary_peer = temporary_peer }; } @@ -132,65 +207,6 @@ CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size return { .is_temporary_peer = false, .context = scc.Contexts [0] }; } -bool BridgeProcessingShared::has_temporary_peer (size_t scc_index) noexcept -{ - for (size_t i = 0; i < temporary_peers.count; i++) { - if (temporary_peers.peers [i].scc_index == scc_index) { - return true; - } - } - - return false; -} - -void BridgeProcessingShared::add_temporary_peer (size_t scc_index, jobject temporary_peer) noexcept -{ - abort_unless (temporary_peers.peers != nullptr, "Temporary peer map must not be null"); - abort_unless (temporary_peers.count < temporary_peers.capacity, "Temporary peer map must not be full"); - - TemporaryPeer &entry = temporary_peers.peers [temporary_peers.count++]; - entry.scc_index = scc_index; - entry.peer = temporary_peer; -} - -jobject BridgeProcessingShared::get_temporary_peer (size_t scc_index) noexcept -{ - // If this lookup ever shows up in profiles, we can try storing the peer-list index in the - // empty SCC's Count field as a negative value. Keep the bridge input immutable for now. - for (size_t i = 0; i < temporary_peers.count; i++) { - TemporaryPeer &entry = temporary_peers.peers [i]; - if (entry.scc_index == scc_index) { - return entry.peer; - } - } - - abort_unless (false, "Temporary peer must be found in the map"); - return nullptr; -} - -void BridgeProcessingShared::release_temporary_peers () noexcept -{ - if (temporary_peers.peers == nullptr) { - return; - } - - for (size_t i = 0; i < temporary_peers.count; i++) { - TemporaryPeer &entry = temporary_peers.peers [i]; - jobject temporary_peer = entry.peer; - if (temporary_peer != nullptr) { - env->DeleteLocalRef (temporary_peer); - entry.peer = nullptr; - } - } - temporary_peers.count = 0; -} - -void BridgeProcessingShared::free_temporary_peer_map () noexcept -{ - std::free (temporary_peers.peers); - temporary_peers = {}; -} - // caller must ensure that scc.Count > 1 void BridgeProcessingShared::add_circular_references (const StronglyConnectedComponent &scc) noexcept { @@ -226,10 +242,10 @@ void BridgeProcessingShared::add_circular_references (const StronglyConnectedCom } } -void BridgeProcessingShared::add_cross_reference (size_t source_index, size_t dest_index) noexcept +void BridgeProcessingShared::add_cross_reference (size_t source_index, size_t dest_index, TemporaryPeerMap &temporary_peers) noexcept { - CrossReferenceTarget from = select_cross_reference_target (source_index); - CrossReferenceTarget to = select_cross_reference_target (dest_index); + CrossReferenceTarget from = select_cross_reference_target (source_index, temporary_peers); + CrossReferenceTarget to = select_cross_reference_target (dest_index, temporary_peers); if (add_reference (from.get_handle(), to.get_handle())) { from.mark_refs_added_if_needed (); diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index 95d3d533cc9..bd1b8fb9ca7 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -35,7 +35,7 @@ void GCBridge::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noe abort_if_invalid_pointer_argument (env, "env"); abort_if_invalid_pointer_argument (runtimeClass, "runtimeClass"); - BridgeProcessing::initialize_on_runtime_init (env, runtimeClass); + TemporaryPeerMap::initialize_on_runtime_init (env, runtimeClass); } void GCBridge::trigger_java_gc (JNIEnv *env) noexcept @@ -64,6 +64,14 @@ void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept abort_unless (ret == 0, "Failed to release GC bridge semaphore"); } +void GCBridge::process_bridge_args (MarkCrossReferencesArgs *args) noexcept +{ + // Bridge processing temporarily mutates args while it owns them. Make sure any + // changes are reverted before returning args to the GC bridge finish callback. + BridgeProcessing bridge_processing {args}; + bridge_processing.process (); +} + void GCBridge::bridge_processing () noexcept { abort_unless (bridge_processing_started_callback != nullptr, "GC bridge processing started callback is not set"); @@ -80,10 +88,7 @@ void GCBridge::bridge_processing () noexcept MarkCrossReferencesArgs *args = __atomic_load_n (&shared_args, __ATOMIC_ACQUIRE); bridge_processing_started_callback (args); - - BridgeProcessing bridge_processing {args}; - bridge_processing.process (); - + process_bridge_args (args); bridge_processing_finished_callback (args); } } diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh index 3d165094306..612d13c1a59 100644 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ b/src/native/clr/include/host/bridge-processing-shared.hh @@ -28,48 +28,59 @@ struct BridgeProcessingCallbacks bool (*maybe_call_gc_user_peerable_clear_managed_references) (void *context, JNIEnv *env, jobject handle) noexcept; }; -class BridgeProcessingShared +class TemporaryPeerMap { - struct TemporaryPeer - { - size_t scc_index; - jobject peer; - }; +public: + explicit TemporaryPeerMap (JNIEnv *env, MarkCrossReferencesArgs *cross_refs) noexcept; + ~TemporaryPeerMap () noexcept; - struct temporary_peer_map - { - TemporaryPeer *peers; - size_t count; - size_t capacity; - }; + static void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept; + + void add (StronglyConnectedComponent &scc) noexcept; + bool has_temporary_peer (const StronglyConnectedComponent &scc) const noexcept; + jobject get (const StronglyConnectedComponent &scc) const noexcept; + +private: + // Count is unsigned, so encode the temporary peer index as ~index. This stores the same bit + // pattern as -(index + 1), giving us a sign bit marker while preserving index 0. + // The .NET 11 GC bridge implementation appears safe to temporarily mutate here: once it + // hands us MarkCrossReferencesArgs, it does not inspect Count again before freeing the data. + // The destructor always resets temporary markers before returning cross_refs to the runtime. + static constexpr size_t temporary_peer_index_sign_bit = ~(~size_t { 0 } >> 1); + static bool is_temporary_peer_index (size_t count) noexcept; + static size_t encode_temporary_peer_index (size_t index) noexcept; + static size_t decode_temporary_peer_index (size_t count) noexcept; + + static inline jclass peer_class = nullptr; + static inline jmethodID peer_ctor = nullptr; + + JNIEnv *env; + MarkCrossReferencesArgs *cross_refs; + jobject *peers {}; + size_t count {}; + size_t capacity {}; +}; + +class BridgeProcessingShared +{ public: explicit BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *callbacks = nullptr) noexcept; - ~BridgeProcessingShared () noexcept; - static void initialize_on_runtime_init (JNIEnv *jniEnv, jclass runtimeClass) noexcept; void process () noexcept; private: JNIEnv* env; MarkCrossReferencesArgs *cross_refs; - temporary_peer_map temporary_peers {}; BridgeProcessingCallbacks callbacks; - static inline jclass GCUserPeer_class = nullptr; - static inline jmethodID GCUserPeer_ctor = nullptr; - void prepare_for_java_collection () noexcept; - void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept; - bool has_temporary_peer (size_t scc_index) noexcept; - void add_temporary_peer (size_t scc_index, jobject temporary_peer) noexcept; - jobject get_temporary_peer (size_t scc_index) noexcept; - void release_temporary_peers () noexcept; - void free_temporary_peer_map () noexcept; + void prepare_sccs_and_cross_references_for_java_collection () noexcept; + void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc, TemporaryPeerMap &temporary_peers) noexcept; void take_weak_global_ref (const HandleContext &context) noexcept; void add_circular_references (const StronglyConnectedComponent &scc) noexcept; - void add_cross_reference (size_t source_index, size_t dest_index) noexcept; - CrossReferenceTarget select_cross_reference_target (size_t scc_index) noexcept; + void add_cross_reference (size_t source_index, size_t dest_index, TemporaryPeerMap &temporary_peers) noexcept; + CrossReferenceTarget select_cross_reference_target (size_t scc_index, TemporaryPeerMap &temporary_peers) noexcept; bool add_reference (jobject from, jobject to) noexcept; void cleanup_after_java_collection () noexcept; diff --git a/src/native/clr/include/host/gc-bridge.hh b/src/native/clr/include/host/gc-bridge.hh index f9f4972e212..f04180337af 100644 --- a/src/native/clr/include/host/gc-bridge.hh +++ b/src/native/clr/include/host/gc-bridge.hh @@ -98,6 +98,7 @@ namespace xamarin::android { static void bridge_processing () noexcept; static auto bridge_processing_thread_entry (void *arg) noexcept -> void*; static void mark_cross_references (MarkCrossReferencesArgs *args) noexcept; + static void process_bridge_args (MarkCrossReferencesArgs *args) noexcept; static void log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept; static void log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept; diff --git a/src/native/nativeaot/host/bridge-processing.cc b/src/native/nativeaot/host/bridge-processing.cc index 4b6d77deb8a..b59dae7c163 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 @@ -33,19 +31,13 @@ void BridgeProcessing::naot_initialize_on_runtime_init (JNIEnv *env) noexcept if (GCUserPeerable_jiAddManagedReference == nullptr || GCUserPeerable_jiClearManagedReferences == nullptr) [[unlikely]] { constexpr char ABSENT[] = "absent"; constexpr char PRESENT[] = "present"; - char message[128]; - snprintf ( - message, - sizeof (message), + Helpers::abort_applicationf ( + LOG_DEFAULT, + std::source_location::current (), "Failed to find GCUserPeerable method(s): jiAddManagedReference (%s); jiClearManagedReferences (%s)", GCUserPeerable_jiAddManagedReference == nullptr ? ABSENT : PRESENT, GCUserPeerable_jiClearManagedReferences == nullptr ? ABSENT : PRESENT ); - - Helpers::abort_application ( - LOG_DEFAULT, - message - ); } } diff --git a/src/native/nativeaot/host/host.cc b/src/native/nativeaot/host/host.cc index c3714e0ac8d..7c823f13439 100644 --- a/src/native/nativeaot/host/host.cc +++ b/src/native/nativeaot/host/host.cc @@ -1,5 +1,3 @@ -#include - #include #include #include @@ -36,15 +34,13 @@ 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++) { if ((log_categories & LOG_ASSEMBLY) != 0) { - char message[256]; - snprintf ( - message, - sizeof (message), + log_write_fmt ( + LOG_ASSEMBLY, + LogLevel::Debug, "Calling JNI on-load init func '%s' (%p)", optional_string (__jni_on_load_handler_names[i]), reinterpret_cast(__jni_on_load_handlers[i]) ); - log_write (LOG_ASSEMBLY, LogLevel::Debug, message); } __jni_on_load_handlers[i] (vm, reserved); } From 4b2ce0e24b34f9a4b716a8bcc75c01acf0282e31 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 09:25:46 +0200 Subject: [PATCH 13/16] Unify native logging helpers Convert native runtime logging macros to use printf-style helpers across CLR, Mono, and NativeAOT. This removes the NativeAOT-only preformatted logging branch while preserving category gating for debug and info logs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/assembly-store.cc | 22 +-- src/native/clr/host/bridge-processing.cc | 4 +- src/native/clr/host/fastdev-assemblies.cc | 28 +-- src/native/clr/host/gc-bridge.cc | 27 ++- src/native/clr/host/host.cc | 68 +++---- src/native/clr/host/typemap.cc | 90 +++++----- .../include/host/bridge-processing-shared.hh | 5 +- src/native/clr/include/host/gc-bridge.hh | 2 + .../clr/include/host/pinvoke-override-impl.hh | 14 +- .../include/runtime-base/android-system.hh | 2 +- .../clr/include/runtime-base/monodroid-dl.hh | 20 ++- src/native/clr/include/shared/log_types.hh | 167 ++++++++---------- src/native/clr/pinvoke-override/dynamic.cc | 12 +- .../clr/pinvoke-override/precompiled.cc | 16 +- src/native/clr/runtime-base/android-system.cc | 40 ++--- src/native/clr/startup/zip.cc | 41 ++--- .../common/include/runtime-base/dso-loader.hh | 20 ++- .../runtime-base/mainthread-dso-loader.hh | 11 +- .../common/include/runtime-base/strings.hh | 27 ++- .../system-loadlibrary-wrapper.hh | 2 +- .../include/runtime-base/timing-internal.hh | 25 +-- .../common/include/runtime-base/timing.hh | 10 +- src/native/common/include/shared/cpp-util.hh | 11 +- .../common/runtime-base/timing-internal.cc | 8 +- src/native/mono/monodroid/debug.cc | 56 +++--- .../mono/monodroid/embedded-assemblies-zip.cc | 50 +++--- .../mono/monodroid/embedded-assemblies.cc | 128 +++++++------- .../mono/monodroid/embedded-assemblies.hh | 6 +- .../mono/monodroid/mono-image-loader.hh | 2 +- .../mono/monodroid/monodroid-glue-internal.hh | 2 +- src/native/mono/monodroid/monodroid-glue.cc | 72 ++++---- .../mono/monodroid/monodroid-tracing.cc | 2 +- src/native/mono/monodroid/osbridge.cc | 48 ++--- .../monodroid/xamarin-android-app-context.cc | 10 +- .../pinvoke-override-api-impl.hh | 12 +- .../mono/pinvoke-override/precompiled.cc | 10 +- .../mono/runtime-base/android-system.cc | 58 +++--- src/native/mono/runtime-base/logger.cc | 8 +- src/native/mono/runtime-base/monodroid-dl.hh | 18 +- src/native/mono/runtime-base/util.cc | 10 +- src/native/mono/runtime-base/util.hh | 4 +- src/native/mono/shared/cpp-util.hh | 2 +- src/native/mono/shared/helpers.cc | 4 +- src/native/mono/shared/log_functions.cc | 71 ++++++++ src/native/mono/shared/log_types.hh | 143 +++++++++------ .../debug-app-helper.cc | 32 ++-- .../nativeaot/host/bridge-processing.cc | 5 +- .../include/host/bridge-processing.hh | 4 +- 48 files changed, 769 insertions(+), 660 deletions(-) diff --git a/src/native/clr/host/assembly-store.cc b/src/native/clr/host/assembly-store.cc index bc79b646596..4121f0d1be4 100644 --- a/src/native/clr/host/assembly-store.cc +++ b/src/native/clr/host/assembly-store.cc @@ -1,3 +1,4 @@ +#include #include #if defined (HAVE_LZ4) @@ -29,7 +30,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co #if defined (HAVE_LZ4) && defined (RELEASE) auto header = reinterpret_cast(e.image_data); if (header->magic == COMPRESSED_DATA_MAGIC) { - log_debug (LOG_ASSEMBLY, "Decompressing assembly '{}' from the assembly store"sv, name); + log_debug (LOG_ASSEMBLY, "Decompressing assembly '%.*s' from the assembly store", static_cast(name.length ()), name.data ()); if (FastTiming::enabled ()) [[unlikely]] { internal_timing.start_event (TimingEventKind::AssemblyDecompression); @@ -108,7 +109,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co ) ); } else { - log_debug (LOG_ASSEMBLY, "Compressed assembly '{}' is smaller than when the application was built. Adjusting accordingly."sv, name); + log_debug (LOG_ASSEMBLY, "Compressed assembly '%.*s' is smaller than when the application was built. Adjusting accordingly.", static_cast(name.length ()), name.data ()); } cad.uncompressed_file_size = header->uncompressed_length; } @@ -149,7 +150,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co } else #endif // def HAVE_LZ4 && def RELEASE { - log_debug (LOG_ASSEMBLY, "Assembly '{}' is not compressed in the assembly store"sv, name); + log_debug (LOG_ASSEMBLY, "Assembly '%.*s' is not compressed in the assembly store", static_cast(name.length ()), name.data ()); // HACK! START // Currently, MAUI crashes when we return a pointer to read-only data, so we must copy @@ -199,7 +200,7 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size) if constexpr (Constants::is_debug_build) { // In fastdev mode we might not have any assembly store. if (assembly_store_hashes == nullptr) { - log_warn (LOG_ASSEMBLY, "Assembly store not registered. Unable to look up assembly '{}'"sv, name); + log_warn (LOG_ASSEMBLY, "Assembly store not registered. Unable to look up assembly '%.*s'", static_cast(name.length ()), name.data ()); return nullptr; } } @@ -207,13 +208,13 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size) const AssemblyStoreIndexEntry *hash_entry = find_assembly_store_entry (name_hash, assembly_store_hashes, assembly_store.index_entry_count); if (hash_entry == nullptr) [[unlikely]] { size = 0; - log_warn (LOG_ASSEMBLY, "Assembly '{}' (hash 0x{:x}) not found"sv, name, name_hash); + log_warn (LOG_ASSEMBLY, "Assembly '%.*s' (hash 0x%zx) not found", static_cast(name.length ()), name.data (), static_cast(name_hash)); return nullptr; } if (hash_entry->ignore != 0) { size = 0; - log_debug (LOG_ASSEMBLY, "Assembly '{}' ignored"sv, name); + log_debug (LOG_ASSEMBLY, "Assembly '%.*s' ignored", static_cast(name.length ()), name.data ()); return nullptr; } @@ -242,7 +243,7 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size) log_debug ( LOG_ASSEMBLY, - "Mapped: image_data == {:p}; debug_info_data == {:p}; config_data == {:p}; descriptor == {:p}; data size == {}; debug data size == {}; config data size == {}; name == '{}'"sv, + "Mapped: image_data == %p; debug_info_data == %p; config_data == %p; descriptor == %p; data size == %u; debug data size == %u; config data size == %u; name == '%.*s'", static_cast(assembly_runtime_info.image_data), static_cast(assembly_runtime_info.debug_info_data), static_cast(assembly_runtime_info.config_data), @@ -250,7 +251,8 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size) assembly_runtime_info.descriptor->data_size, assembly_runtime_info.descriptor->debug_data_size, assembly_runtime_info.descriptor->config_data_size, - name + static_cast(name.length ()), + name.data () ); } @@ -264,7 +266,7 @@ void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_v detail::mmap_info assembly_store_map = Util::mmap_file (fd, offset, size, store_path); auto [payload_start, payload_size] = Util::get_wrapper_dso_payload_pointer_and_size (assembly_store_map, store_path); - log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: {:p}; size: {}"sv, payload_start, payload_size); + log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: %p; size: %zu", payload_start, payload_size); auto header = static_cast(payload_start); auto get_full_store_path = [&apk_path, &store_path]() -> std::string { @@ -312,5 +314,5 @@ void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_v assembly_store.assemblies = reinterpret_cast(assembly_store.data_start + header_size + header->index_size); assembly_store_hashes = reinterpret_cast(assembly_store.data_start + header_size); - log_debug (LOG_ASSEMBLY, "Mapped assembly store {}"sv, get_full_store_path ()); + log_debug (LOG_ASSEMBLY, "Mapped assembly store %s", get_full_store_path ().c_str ()); } diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index c45c6b4c543..21d85dfaf7d 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -325,7 +325,7 @@ bool BridgeProcessingShared::maybe_call_gc_user_peerable_add_managed_reference ( return false; } - return callbacks.maybe_call_gc_user_peerable_add_managed_reference (callbacks.context, jni_env, from, to); + return callbacks.maybe_call_gc_user_peerable_add_managed_reference (jni_env, from, to); } bool BridgeProcessingShared::maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *jni_env, jobject handle) noexcept @@ -334,7 +334,7 @@ bool BridgeProcessingShared::maybe_call_gc_user_peerable_clear_managed_reference return false; } - return callbacks.maybe_call_gc_user_peerable_clear_managed_references (callbacks.context, jni_env, handle); + return callbacks.maybe_call_gc_user_peerable_clear_managed_references (jni_env, handle); } void BridgeProcessingShared::take_global_ref (HandleContext &context) noexcept diff --git a/src/native/clr/host/fastdev-assemblies.cc b/src/native/clr/host/fastdev-assemblies.cc index 299ac8a35c1..00c351d515e 100644 --- a/src/native/clr/host/fastdev-assemblies.cc +++ b/src/native/clr/host/fastdev-assemblies.cc @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -20,7 +21,7 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si const char *override_dir_path = AndroidSystem::get_primary_override_dir (); if (!Util::dir_exists (override_dir_path)) [[unlikely]] { - log_debug (LOG_ASSEMBLY, "Override directory '{}' does not exist"sv, optional_string (override_dir_path)); + log_debug (LOG_ASSEMBLY, "Override directory '%s' does not exist", optional_string (override_dir_path)); return nullptr; } @@ -31,7 +32,7 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si if (override_dir_fd < 0) [[likely]] { override_dir = opendir (override_dir_path); if (override_dir == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Failed to open override dir '{}'. {}"sv, optional_string (override_dir_path), strerror (errno)); + log_warn (LOG_ASSEMBLY, "Failed to open override dir '%s'. %s", optional_string (override_dir_path), strerror (errno)); return nullptr; } override_dir_fd = dirfd (override_dir); @@ -40,20 +41,21 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si log_debug ( LOG_ASSEMBLY, - "Attempting to load FastDev assembly '{}' from override directory '{}'"sv, - name, + "Attempting to load FastDev assembly '%.*s' from override directory '%s'", + static_cast(name.length ()), + name.data (), optional_string (override_dir_path) ); if (!Util::file_exists (override_dir_fd, name)) { - log_warn (LOG_ASSEMBLY, "FastDev assembly '{}' not found."sv, name); + log_warn (LOG_ASSEMBLY, "FastDev assembly '%.*s' not found.", static_cast(name.length ()), name.data ()); return nullptr; } - log_debug (LOG_ASSEMBLY, "Found FastDev assembly '{}'"sv, name); + log_debug (LOG_ASSEMBLY, "Found FastDev assembly '%.*s'", static_cast(name.length ()), name.data ()); auto file_size = Util::get_file_size_at (override_dir_fd, name); if (!file_size) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Unable to determine FastDev assembly '{}' file size"sv, name); + log_warn (LOG_ASSEMBLY, "Unable to determine FastDev assembly '%.*s' file size", static_cast(name.length ()), name.data ()); return nullptr; } @@ -74,8 +76,9 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si if (asm_fd < 0) { log_warn ( LOG_ASSEMBLY, - "Failed to open FastDev assembly '{}' for reading. {}"sv, - name, + "Failed to open FastDev assembly '%.*s' for reading. %s", + static_cast(name.length ()), + name.data (), strerror (errno) ); @@ -99,15 +102,16 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si log_warn ( LOG_ASSEMBLY, - "Failed to read FastDev assembly '{}' data. {}"sv, - name, + "Failed to read FastDev assembly '%.*s' data. %s", + static_cast(name.length ()), + name.data (), strerror (errno) ); size = 0; return nullptr; } - log_debug (LOG_ASSEMBLY, "Read {} bytes of FastDev assembly '{}'"sv, nread, name); + log_debug (LOG_ASSEMBLY, "Read %zd bytes of FastDev assembly '%.*s'", nread, static_cast(name.length ()), name.data ()); return reinterpret_cast(buffer); } diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc index bd1b8fb9ca7..99457d3d05f 100644 --- a/src/native/clr/host/gc-bridge.cc +++ b/src/native/clr/host/gc-bridge.cc @@ -59,11 +59,28 @@ void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept abort_unless (args->CrossReferences != nullptr || args->CrossReferenceCount == 0, "CrossReferences must not be null if CrossReferenceCount is greater than 0"); log_mark_cross_references_args_if_enabled (args); + send_and_signal (args); +} + +void GCBridge::send_and_signal (MarkCrossReferencesArgs *args) noexcept +{ __atomic_store_n (&shared_args, args, __ATOMIC_RELEASE); int ret = sem_post (&shared_args_semaphore); abort_unless (ret == 0, "Failed to release GC bridge semaphore"); } +auto GCBridge::enter_bridge_processing () noexcept -> MarkCrossReferencesArgs* +{ + // wait until mark cross references args are set by the GC callback + int ret; + do { + ret = sem_wait (&shared_args_semaphore); + } while (ret == -1 && errno == EINTR); + abort_unless (ret == 0, "Failed to acquire GC bridge semaphore"); + + return __atomic_load_n (&shared_args, __ATOMIC_ACQUIRE); +} + void GCBridge::process_bridge_args (MarkCrossReferencesArgs *args) noexcept { // Bridge processing temporarily mutates args while it owns them. Make sure any @@ -78,15 +95,7 @@ void GCBridge::bridge_processing () noexcept abort_unless (bridge_processing_finished_callback != nullptr, "GC bridge processing finished callback is not set"); while (true) { - // wait until mark cross references args are set by the GC callback - int ret; - do { - ret = sem_wait (&shared_args_semaphore); - } while (ret == -1 && errno == EINTR); - abort_unless (ret == 0, "Failed to acquire GC bridge semaphore"); - - MarkCrossReferencesArgs *args = __atomic_load_n (&shared_args, __ATOMIC_ACQUIRE); - + MarkCrossReferencesArgs *args = enter_bridge_processing (); bridge_processing_started_callback (args); process_bridge_args (args); bridge_processing_finished_callback (args); diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc index 3e84ef930d8..f98b3180f92 100644 --- a/src/native/clr/host/host.cc +++ b/src/native/clr/host/host.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -35,14 +36,14 @@ using namespace xamarin::android; void Host::clr_error_writer (const char *message) noexcept { - log_error (LOG_DEFAULT, "CLR error: {}", optional_string (message)); + log_error (LOG_DEFAULT, "CLR error: %s", optional_string (message)); } size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size_t value_buffer_size, [[maybe_unused]] void *contract_context) noexcept { // NOTE: this code was tested locally, but it's **not** used by CoreCLR yet, so there's been no // "live" testing. - log_debug (LOG_DEFAULT, "clr_get_runtime_property (\"{}\"...)"sv, optional_string (key)); + log_debug (LOG_DEFAULT, "clr_get_runtime_property (\"%s\"...)", optional_string (key)); if (application_config.number_of_runtime_properties == 0) [[unlikely]] { log_debug (LOG_DEFAULT, "No runtime properties defined"sv); return 0; @@ -52,7 +53,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size if (key == nullptr || value_buffer == nullptr || value_buffer_size <= 1) [[unlikely]] { log_warn ( LOG_DEFAULT, - "runtime property retrieval API called with invalid arguments. key == {:p}; value_buffer == {:p}; value_buffer_size == {}"sv, + "runtime property retrieval API called with invalid arguments. key == %p; value_buffer == %p; value_buffer_size == %zu", static_cast(key), static_cast(value_buffer), value_buffer_size @@ -66,7 +67,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size auto less_than = [](RuntimePropertyIndexEntry const& entry, hash_t key) -> bool { return entry.key_hash < key; }; ssize_t idx = Search::binary_search (key_hash, runtime_property_index, application_config.number_of_runtime_properties); if (idx < 0) { - log_debug (LOG_DEFAULT, "Runtime property '{}' not found"sv, key); + log_debug (LOG_DEFAULT, "Runtime property '%s' not found", key); return 0; } @@ -77,7 +78,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size if (prop.value_size > value_buffer_size) { log_warn ( LOG_DEFAULT, - "Value of property '{}' is longer than available buffer space. Need {}b, available {}b"sv, + "Value of property '%s' is longer than available buffer space. Need %u b, available %zu b", key, prop.value_size, value_buffer_size @@ -91,7 +92,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size bool Host::clr_external_assembly_probe (const char *path, void **data_start, int64_t *size) noexcept { // TODO: `path` might be a full path, make sure it isn't - log_debug (LOG_DEFAULT, "clr_external_assembly_probe (\"{}\"...)"sv, path); + log_debug (LOG_DEFAULT, "clr_external_assembly_probe (\"%s\"...)", optional_string (path)); if (data_start == nullptr || size == nullptr) { return false; // TODO: abort instead? } @@ -108,11 +109,11 @@ bool Host::clr_external_assembly_probe (const char *path, void **data_start, int log_debug ( LOG_ASSEMBLY, - "Assembly '{}' data {}mapped ({:p}, {} bytes)", + "Assembly '%s' data %smapped (%p, %lld bytes)", optional_string (name), - data_start == nullptr ? "not "sv : ""sv, + data_start == nullptr ? "not " : "", data_start, - size + static_cast(size) ); return data_start != nullptr && size > 0; @@ -126,7 +127,7 @@ bool Host::clr_external_assembly_probe (const char *path, void **data_start, int log_warn ( LOG_ASSEMBLY, - "Assembly '{}' not found in FastDev override directory. Attempting to load from assembly store"sv, + "Assembly '%s' not found in FastDev override directory. Attempting to load from assembly store", optional_string (path) ); } @@ -138,11 +139,11 @@ bool Host::clr_external_assembly_probe (const char *path, void **data_start, int auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dynamic_local_string const& entry_name, uint32_t offset, uint32_t size) -> bool { - log_debug (LOG_ASSEMBLY, "zip entry: {}"sv, entry_name.get ()); + log_debug (LOG_ASSEMBLY, "zip entry: %s", entry_name.get ()); if (!found_assembly_store) { found_assembly_store = Zip::assembly_store_file_path.compare (0, entry_name.length (), entry_name.get ()) == 0; if (found_assembly_store) { - log_debug (LOG_ASSEMBLY, "Found assembly store in '{}': {}"sv, apk_path, Zip::assembly_store_file_path); + log_debug (LOG_ASSEMBLY, "Found assembly store in '%.*s': %.*s", static_cast(apk_path.length ()), apk_path.data (), static_cast(Zip::assembly_store_file_path.length ()), Zip::assembly_store_file_path.data ()); AssemblyStore::map (apk_fd, apk_path, Zip::assembly_store_file_path, offset, size); return false; // This will make the scanner keep the APK open } @@ -152,10 +153,10 @@ auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dyna return false; } - log_debug (LOG_ASSEMBLY, "Found shared library in '{}': {}"sv, apk_path, entry_name.get ()); + log_debug (LOG_ASSEMBLY, "Found shared library in '%.*s': %s", static_cast(apk_path.length ()), apk_path.data (), entry_name.get ()); std::string_view lib_name { entry_name.get () + Zip::lib_prefix.length () }; hash_t name_hash = xxhash::hash (lib_name.data (), lib_name.length ()); - log_debug (LOG_ASSEMBLY, "Library name is: {}; hash == 0x{:x}", lib_name, name_hash); + log_debug (LOG_ASSEMBLY, "Library name is: %.*s; hash == 0x%zx", static_cast(lib_name.length ()), lib_name.data (), static_cast(name_hash)); DSOApkEntry *apk_entry = MonodroidDl::find_dso_apk_entry (name_hash); if (apk_entry == nullptr) { @@ -172,7 +173,7 @@ auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dyna void Host::scan_filesystem_for_assemblies_and_libraries () noexcept { std::string const& native_lib_dir = AndroidSystem::get_native_libraries_dir (); - log_debug (LOG_ASSEMBLY, "Looking for assemblies in '{}'"sv, native_lib_dir); + log_debug (LOG_ASSEMBLY, "Looking for assemblies in '%s'", native_lib_dir.c_str ()); DIR *lib_dir = opendir (native_lib_dir.c_str ()); if (lib_dir == nullptr) [[unlikely]] { @@ -203,7 +204,7 @@ void Host::scan_filesystem_for_assemblies_and_libraries () noexcept dirent *cur = readdir (lib_dir); if (cur == nullptr) { if (errno != 0) { - log_warn (LOG_ASSEMBLY, "Failed to open a directory entry from '{}': {}"sv, native_lib_dir, std::strerror (errno)); + log_warn (LOG_ASSEMBLY, "Failed to open a directory entry from '%s': %s", native_lib_dir.c_str (), std::strerror (errno)); continue; // No harm, keep going } break; // we're done @@ -220,7 +221,7 @@ void Host::scan_filesystem_for_assemblies_and_libraries () noexcept continue; } - log_debug (LOG_ASSEMBLY, "Found assembly store in '{}/{}'"sv, native_lib_dir, Constants::assembly_store_file_name); + log_debug (LOG_ASSEMBLY, "Found assembly store in '%s/%.*s'", native_lib_dir.c_str (), static_cast(Constants::assembly_store_file_name.length ()), Constants::assembly_store_file_name.data ()); int store_fd = openat (dir_fd, cur->d_name, O_RDONLY); if (store_fd < 0) { Helpers::abort_application ( @@ -312,10 +313,13 @@ auto Host::create_delegate ( &delegate ); log_debug (LOG_ASSEMBLY, - "{}@{}.{} delegate creation result == {:x}; delegate == {:p}"sv, - assembly_name, - type_name, - method_name, + "%.*s@%.*s.%.*s delegate creation result == %x; delegate == %p", + static_cast(assembly_name.length ()), + assembly_name.data (), + static_cast(type_name.length ()), + type_name.data (), + static_cast(method_name.length ()), + method_name.data (), static_cast(hr), delegate ); @@ -344,7 +348,7 @@ void Host::preload_jni_libraries () noexcept return; } - log_debug (LOG_ASSEMBLY, "DSO jni preloads index stride == {}", dso_jni_preloads_idx_stride); + log_debug (LOG_ASSEMBLY, "DSO jni preloads index stride == %u", dso_jni_preloads_idx_stride); if ((dso_jni_preloads_idx_count % dso_jni_preloads_idx_stride) != 0) [[unlikely]] { Helpers::abort_application ( @@ -364,11 +368,12 @@ void Host::preload_jni_libraries () noexcept log_debug ( LOG_ASSEMBLY, - "Preloading JNI shared library: {} (entry's index: {}; real name hash: {:x}; name hash: {:x})", - dso_name, + "Preloading JNI shared library: %.*s (entry's index: %zu; real name hash: %zx; name hash: %zx)", + static_cast(dso_name.length ()), + dso_name.data (), entry_index, - entry.real_name_hash, - entry.hash + static_cast(entry.real_name_hash), + static_cast(entry.hash) ); void *handle = MonodroidDl::monodroid_dlopen (&entry, dso_name, RTLD_NOW); @@ -381,9 +386,10 @@ void Host::preload_jni_libraries () noexcept log_debug ( LOG_ASSEMBLY, - "Putting JNI library handle in alias entry at index {}: {}", + "Putting JNI library handle in alias entry at index %zu: %.*s", entry_alias_index, - entry_alias_name + static_cast(entry_alias_name.length ()), + entry_alias_name.data () ); entry_alias.handle = handle; } @@ -519,7 +525,7 @@ void Host::Java_mono_android_Runtime_initInternal ( init.grefIGCUserPeer = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_IGCUserPeer"sv, true); init.grefGCUserPeerable = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "net_dot_jni_GCUserPeerable"sv, true); - log_info (LOG_GC, "GREF GC Threshold: {}"sv, init.grefGcThreshold); + log_info (LOG_GC, "GREF GC Threshold: %d", init.grefGcThreshold); OSBridge::initialize_on_runtime_init (env, runtimeClass); GCBridge::initialize_on_runtime_init (env, runtimeClass); @@ -528,7 +534,7 @@ void Host::Java_mono_android_Runtime_initInternal ( internal_timing.start_event (TimingEventKind::NativeToManagedTransition); } - log_debug (LOG_ASSEMBLY, "Creating UCO delegate to {}.Initialize"sv, Constants::JNIENVINIT_FULL_TYPE_NAME); + log_debug (LOG_ASSEMBLY, "Creating UCO delegate to %.*s.Initialize", static_cast(Constants::JNIENVINIT_FULL_TYPE_NAME.length ()), Constants::JNIENVINIT_FULL_TYPE_NAME.data ()); void *delegate = nullptr; delegate = FastTiming::time_call ("create_delegate for Initialize"sv, create_delegate, Constants::MONO_ANDROID_ASSEMBLY_NAME, Constants::JNIENVINIT_FULL_TYPE_NAME, "Initialize"sv); auto initialize = reinterpret_cast (delegate); @@ -573,7 +579,7 @@ void Host::Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, dynamic_local_string managed_type_name; const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr); managed_type_name.assign (mt_ptr, strlen (mt_ptr)); - log_debug (LOG_ASSEMBLY, "Registering type: '{}'"sv, managed_type_name.get ()); + log_debug (LOG_ASSEMBLY, "Registering type: '%s'", managed_type_name.get ()); env->ReleaseStringUTFChars (managedType, mt_ptr); // TODO: must attach thread to the runtime here diff --git a/src/native/clr/host/typemap.cc b/src/native/clr/host/typemap.cc index 85e5a37a3a5..3bfd47b58db 100644 --- a/src/native/clr/host/typemap.cc +++ b/src/native/clr/host/typemap.cc @@ -67,7 +67,7 @@ namespace { [[gnu::always_inline, gnu::flatten]] auto TypeMapper::find_index_by_name (const char *typeName, const TypeMapEntry *map, const char (&name_map)[], std::string_view const& from_name, std::string_view const& to_name) noexcept -> ssize_t { - log_debug (LOG_ASSEMBLY, "typemap: map {} -> {} uses strings", from_name, to_name); + log_debug (LOG_ASSEMBLY, "typemap: map %.*s -> %.*s uses strings", static_cast(from_name.length ()), from_name.data (), static_cast(to_name.length ()), to_name.data ()); auto equal = [](TypeMapEntry const& entry, const char *key, const char (&name_map)[]) -> bool { if (entry.from == std::numeric_limits::max ()) [[unlikely]] { @@ -97,7 +97,7 @@ auto TypeMapper::find_index_by_hash (const char *typeName, const TypeMapEntry *m return find_index_by_name (typeName, map, name_map, from_name, to_name); } - log_debug (LOG_ASSEMBLY, "typemap: map {} -> {} uses hashes"sv, from_name, to_name); + log_debug (LOG_ASSEMBLY, "typemap: map %.*s -> %.*s uses hashes", static_cast(from_name.length ()), from_name.data (), static_cast(to_name.length ()), to_name.data ()); auto equal = [](TypeMapEntry const& entry, hash_t key) -> bool { if (entry.from == std::numeric_limits::max ()) [[unlikely]] { @@ -122,7 +122,7 @@ auto TypeMapper::find_index_by_hash (const char *typeName, const TypeMapEntry *m auto TypeMapper::index_to_name (ssize_t idx, const char* typeName, const TypeMapEntry *map, const char (&name_map)[], std::string_view const& from_name, std::string_view const& to_name) -> const char* { if (idx < 0) [[unlikely]] { - log_debug (LOG_ASSEMBLY, "typemap: unable to map from {} type '{}' to {} type"sv, from_name, typeName, to_name); + log_debug (LOG_ASSEMBLY, "typemap: unable to map from %.*s type '%s' to %.*s type", static_cast(from_name.length ()), from_name.data (), optional_string (typeName), static_cast(to_name.length ()), to_name.data ()); return nullptr; } @@ -131,10 +131,12 @@ auto TypeMapper::index_to_name (ssize_t idx, const char* typeName, const TypeMap log_debug ( LOG_ASSEMBLY, - "typemap: {} type '{}' maps to {} type '{}'"sv, - from_name, + "typemap: %.*s type '%s' maps to %.*s type '%s'", + static_cast(from_name.length ()), + from_name.data (), optional_string (typeName), - to_name, + static_cast(to_name.length ()), + to_name.data (), optional_string (mapped_name) ); return mapped_name; @@ -159,7 +161,7 @@ auto TypeMapper::managed_to_java_debug (const char *typeName, const uint8_t *mvi // We explicitly trust the build process here, with regards to validity of offsets full_type_name.append (&type_map_assembly_names[assm.name_offset], assm.name_length); } else { - log_warn (LOG_ASSEMBLY, "typemap: unable to look up assembly name for type '{}', trying without it."sv, typeName); + log_warn (LOG_ASSEMBLY, "typemap: unable to look up assembly name for type '%s', trying without it.", optional_string (typeName)); } // If hashes are used for matching, the type names array is not used. If, however, string-based matching is in @@ -220,12 +222,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m if (mvid == nullptr) { log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"sv); } else { - log_info (LOG_ASSEMBLY, "typemap: module matching MVID [{}] not found."sv, MonoGuidString (mvid).c_str ()); + log_info (LOG_ASSEMBLY, "typemap: module matching MVID [%s] not found.", MonoGuidString (mvid).c_str ()); } return nullptr; } - log_debug (LOG_ASSEMBLY, "typemap: found module matching MVID [{}]"sv, MonoGuidString (mvid).c_str ()); + log_debug (LOG_ASSEMBLY, "typemap: found module matching MVID [%s]", MonoGuidString (mvid).c_str ()); hash_t name_hash = xxhash::hash (typeName, strlen (typeName)); // We implicitly trust the build process that the indexes are correct. This is by design, the libxamarin-app.so built @@ -236,10 +238,10 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m if (match->duplicate_count > 0 && match->duplicate_map_index < std::numeric_limitsduplicate_map_index)>::max ()) { log_debug ( LOG_ASSEMBLY, - "typemap: searching module [{}] duplicate map for type '{}' (hash {:x})"sv, + "typemap: searching module [%s] duplicate map for type '%s' (hash %zx)", MonoGuidString (mvid).c_str (), optional_string (typeName), - name_hash + static_cast(name_hash) ); const TypeMapModuleEntry *const duplicate_map = &modules_duplicates_data[match->duplicate_map_index]; @@ -249,11 +251,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m if (entry == nullptr) { log_warn ( LOG_ASSEMBLY, - "typemap: managed type '{}' (hash {:x}) not found in module [{}] ({})."sv, + "typemap: managed type '%s' (hash %zx) not found in module [%s] (%.*s).", optional_string (typeName), - name_hash, + static_cast(name_hash), MonoGuidString (mvid).c_str (), - std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length) + static_cast(match->assembly_name_length), + &managed_assembly_names[match->assembly_name_index] ); return nullptr; } @@ -262,11 +265,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m if (entry->java_map_index >= java_type_count) [[unlikely]] { log_warn ( LOG_ASSEMBLY, - "typemap: managed type '{}' (hash {:x}) in module [{}] ({}) has invalid Java type index {}"sv, + "typemap: managed type '%s' (hash %zx) in module [%s] (%.*s) has invalid Java type index %u", optional_string (typeName), - name_hash, + static_cast(name_hash), MonoGuidString (mvid).c_str (), - std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length), + static_cast(match->assembly_name_length), + &managed_assembly_names[match->assembly_name_index], entry->java_map_index ); return nullptr; @@ -276,11 +280,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m if (java_entry.java_name_index >= java_type_names_size) [[unlikely]] { log_warn ( LOG_ASSEMBLY, - "typemap: managed type '{}' (hash {:x}) in module [{}] ({}) points to invalid Java type at index {} (invalid type name index {})"sv, + "typemap: managed type '%s' (hash %zx) in module [%s] (%.*s) points to invalid Java type at index %u (invalid type name index %u)", optional_string (typeName), - name_hash, + static_cast(name_hash), MonoGuidString (mvid).c_str (), - std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length), + static_cast(match->assembly_name_length), + &managed_assembly_names[match->assembly_name_index], entry->java_map_index, java_entry.java_name_index ); @@ -290,17 +295,18 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m const char *ret = &java_type_names[java_entry.java_name_index]; if (ret == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index {}"sv, entry->java_map_index); + log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index %u", entry->java_map_index); } log_debug ( LOG_ASSEMBLY, - "typemap: managed type '{}' (hash {:x}) in module [{}] ({}) corresponds to Java type '{}'"sv, + "typemap: managed type '%s' (hash %zx) in module [%s] (%.*s) corresponds to Java type '%s'", optional_string (typeName), - name_hash, + static_cast(name_hash), MonoGuidString (mvid).c_str (), - std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length), - ret + static_cast(match->assembly_name_length), + &managed_assembly_names[match->assembly_name_index], + optional_string (ret) ); return ret; @@ -310,7 +316,7 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m [[gnu::flatten]] auto TypeMapper::managed_to_java (const char *typeName, const uint8_t *mvid) noexcept -> const char* { - log_debug (LOG_ASSEMBLY, "managed_to_java: looking up type '{}'"sv, optional_string (typeName)); + log_debug (LOG_ASSEMBLY, "managed_to_java: looking up type '%s'", optional_string (typeName)); if (FastTiming::enabled ()) [[unlikely]] { internal_timing.start_event (TimingEventKind::ManagedToJava); } @@ -363,10 +369,10 @@ auto TypeMapper::java_to_managed_debug (const char *java_type_name, char const** log_debug ( LOG_ASSEMBLY, - "Mapped Java type '{}' to managed type '{}' in assembly '{}' and with token '{:x}'"sv, + "Mapped Java type '%s' to managed type '%s' in assembly '%s' and with token '%x'", optional_string (java_type_name), - name, - *assembly_name, + optional_string (name), + optional_string (*assembly_name), *managed_type_token_id ); @@ -392,8 +398,8 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const if (java_type_name == nullptr) { log_warn ( LOG_ASSEMBLY, - "typemap: required parameter `{}` not passed to {}"sv, - "java_type_name"sv, + "typemap: required parameter `%s` not passed to %s", + "java_type_name", __PRETTY_FUNCTION__ ); } @@ -401,8 +407,8 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const if (assembly_name == nullptr) { log_warn ( LOG_ASSEMBLY, - "typemap: required parameter `{}` not passed to {}"sv, - "assembly_name"sv, + "typemap: required parameter `%s` not passed to %s", + "assembly_name", __PRETTY_FUNCTION__ ); } @@ -410,8 +416,8 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const if (managed_type_token_id == nullptr) { log_warn ( LOG_ASSEMBLY, - "typemap: required parameter `{}` not passed to {}"sv, - "managed_type_token_id"sv, + "typemap: required parameter `%s` not passed to %s", + "managed_type_token_id", __PRETTY_FUNCTION__ ); } @@ -424,9 +430,9 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const if (java_entry == nullptr) { log_info ( LOG_ASSEMBLY, - "typemap: unable to find mapping to a managed type from Java type '{}' (hash {:x})"sv, + "typemap: unable to find mapping to a managed type from Java type '%s' (hash %zx)", optional_string (java_type_name), - name_hash + static_cast(name_hash) ); return false; @@ -438,11 +444,13 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const log_debug ( LOG_ASSEMBLY, - "Java type '{}' corresponds to managed type '{}' (token 0x{:x} in assembly '{}')"sv, + "Java type '%s' corresponds to managed type '%.*s' (token 0x%x in assembly '%.*s')", optional_string (java_type_name), - std::string_view (&managed_type_names[java_entry->managed_type_name_index], java_entry->managed_type_name_length), + static_cast(java_entry->managed_type_name_length), + &managed_type_names[java_entry->managed_type_name_index], *managed_type_token_id, - std::string_view (&managed_assembly_names[module.assembly_name_index], module.assembly_name_length) + static_cast(module.assembly_name_length), + &managed_assembly_names[module.assembly_name_index] ); return true; @@ -452,7 +460,7 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const [[gnu::flatten]] auto TypeMapper::java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept -> bool { - log_debug (LOG_ASSEMBLY, "java_to_managed: looking up type '{}'"sv, optional_string (java_type_name)); + log_debug (LOG_ASSEMBLY, "java_to_managed: looking up type '%s'", optional_string (java_type_name)); if (FastTiming::enabled ()) [[unlikely]] { internal_timing.start_event (TimingEventKind::JavaToManaged); } diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh index 612d13c1a59..35863ca4e26 100644 --- a/src/native/clr/include/host/bridge-processing-shared.hh +++ b/src/native/clr/include/host/bridge-processing-shared.hh @@ -23,9 +23,8 @@ struct CrossReferenceTarget struct BridgeProcessingCallbacks { - void *context; - bool (*maybe_call_gc_user_peerable_add_managed_reference) (void *context, JNIEnv *env, jobject from, jobject to) noexcept; - bool (*maybe_call_gc_user_peerable_clear_managed_references) (void *context, JNIEnv *env, jobject handle) noexcept; + bool (*maybe_call_gc_user_peerable_add_managed_reference) (JNIEnv *env, jobject from, jobject to) noexcept; + bool (*maybe_call_gc_user_peerable_clear_managed_references) (JNIEnv *env, jobject handle) noexcept; }; class TemporaryPeerMap diff --git a/src/native/clr/include/host/gc-bridge.hh b/src/native/clr/include/host/gc-bridge.hh index f04180337af..41be04bb6ee 100644 --- a/src/native/clr/include/host/gc-bridge.hh +++ b/src/native/clr/include/host/gc-bridge.hh @@ -98,6 +98,8 @@ namespace xamarin::android { static void bridge_processing () noexcept; static auto bridge_processing_thread_entry (void *arg) noexcept -> void*; static void mark_cross_references (MarkCrossReferencesArgs *args) noexcept; + static MarkCrossReferencesArgs* enter_bridge_processing () noexcept; + static void send_and_signal (MarkCrossReferencesArgs *args) noexcept; static void process_bridge_args (MarkCrossReferencesArgs *args) noexcept; static void log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept; diff --git a/src/native/clr/include/host/pinvoke-override-impl.hh b/src/native/clr/include/host/pinvoke-override-impl.hh index 97608211f37..5301cfbe470 100644 --- a/src/native/clr/include/host/pinvoke-override-impl.hh +++ b/src/native/clr/include/host/pinvoke-override-impl.hh @@ -34,7 +34,7 @@ namespace xamarin::android { short_library_name.append (Constants::dso_suffix); } - log_debug (LOG_ASSEMBLY, "Modified p/invoke library name to '{}'", short_library_name.get ()); + log_debug (LOG_ASSEMBLY, "Modified p/invoke library name to '%s'", short_library_name.get ()); lib_handle = MonodroidDl::monodroid_dlopen (short_library_name.get (), microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY); } @@ -43,21 +43,21 @@ namespace xamarin::android { } if (lib_handle == nullptr) { - log_warn (LOG_ASSEMBLY, "Shared library '{}' not loaded, p/invoke '{}' may fail", library_name, symbol_name); + log_warn (LOG_ASSEMBLY, "Shared library '%.*s' not loaded, p/invoke '%.*s' may fail", static_cast(library_name.length ()), library_name.data (), static_cast(symbol_name.length ()), symbol_name.data ()); return nullptr; } if (dso_handle != nullptr) { void *expected_null = nullptr; if (!__atomic_compare_exchange (dso_handle, &expected_null, &lib_handle, false /* weak */, __ATOMIC_ACQUIRE /* success_memorder */, __ATOMIC_RELAXED /* xxxfailure_memorder */)) { - log_debug (LOG_ASSEMBLY, "Library '{}' handle already cached by another thread", library_name); + log_debug (LOG_ASSEMBLY, "Library '%.*s' handle already cached by another thread", static_cast(library_name.length ()), library_name.data ()); } } } void *entry_handle = MonodroidDl::monodroid_dlsym (lib_handle, symbol_name); if (entry_handle == nullptr) { - log_warn (LOG_ASSEMBLY, "Symbol '{}' not found in shared library '{}', p/invoke may fail", symbol_name, library_name); + log_warn (LOG_ASSEMBLY, "Symbol '%.*s' not found in shared library '%.*s', p/invoke may fail", static_cast(symbol_name.length ()), symbol_name.data (), static_cast(library_name.length ()), library_name.data ()); return nullptr; } @@ -79,7 +79,7 @@ namespace xamarin::android { return nullptr; } - log_debug (LOG_ASSEMBLY, "Caching p/invoke entry {} @ {}", library_name, entrypoint_name); + log_debug (LOG_ASSEMBLY, "Caching p/invoke entry %s @ %s", library_name.c_str (), entrypoint_name.c_str ()); (*api_map)[entrypoint_name] = entry_handle; return entry_handle; } @@ -100,7 +100,7 @@ namespace xamarin::android { ); if (already_loaded) { - log_debug (LOG_ASSEMBLY, "Entry '{}' from library '{}' already loaded by another thread", entrypoint_name, library_name); + log_debug (LOG_ASSEMBLY, "Entry '%.*s' from library '%.*s' already loaded by another thread", static_cast(entrypoint_name.length ()), entrypoint_name.data (), static_cast(library_name.length ()), library_name.data ()); } } @@ -165,7 +165,7 @@ namespace xamarin::android { handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, lib_map, /* need_lock */ false); } else { if (iter->second == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '{}'", library_name); + log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '%.*s'", static_cast(library_name.length ()), library_name.data ()); return nullptr; // fall back to `monodroid_dlopen` } diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh index f3d2969bab2..3de8138522d 100644 --- a/src/native/clr/include/runtime-base/android-system.hh +++ b/src/native/clr/include/runtime-base/android-system.hh @@ -96,7 +96,7 @@ namespace xamarin::android { } } - log_debug (LOG_DEFAULT, "Creating public update directory: `{}`", optional_string (override_dir)); + log_debug (LOG_DEFAULT, "Creating public update directory: `%s`", optional_string (override_dir)); Util::create_public_directory (override_dir); } #endif diff --git a/src/native/clr/include/runtime-base/monodroid-dl.hh b/src/native/clr/include/runtime-base/monodroid-dl.hh index cc2973b4515..5f39aed3d24 100644 --- a/src/native/clr/include/runtime-base/monodroid-dl.hh +++ b/src/native/clr/include/runtime-base/monodroid-dl.hh @@ -41,11 +41,11 @@ namespace xamarin::android size_t arr_size; if constexpr (WhichCache == CacheKind::AOT) { - log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in AOT cache", hash); + log_debug (LOG_ASSEMBLY, "Looking for hash %zx in AOT cache", static_cast(hash)); arr = aot_dso_cache; arr_size = application_config.number_of_aot_cache_entries; } else if constexpr (WhichCache == CacheKind::DSO) { - log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in DSO cache", hash); + log_debug (LOG_ASSEMBLY, "Looking for hash %zx in DSO cache", static_cast(hash)); arr = dso_cache; arr_size = application_config.number_of_dso_cache_entries; } @@ -104,23 +104,24 @@ namespace xamarin::android [[gnu::flatten]] static auto monodroid_dlopen (DSOCacheEntry *dso, std::string_view const& name, int flags) noexcept -> void* { - log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash match {}found, DSO name is '{}'", dso == nullptr ? "not "sv : ""sv, get_dso_name (dso)); + std::string_view dso_name = get_dso_name (dso); + std::string_view match_status = dso == nullptr ? "not "sv : ""sv; + log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash match %.*sfound, DSO name is '%.*s'", static_cast(match_status.length ()), match_status.data (), static_cast(dso_name.length ()), dso_name.data ()); if (dso == nullptr) { // DSO not known at build time, try to load it. Since we don't know whether or not the library uses // JNI, we're going to assume it does and thus use System.loadLibrary eventually. return DsoLoader::load (name, flags, true /* is_jni */); } else if (dso->handle != nullptr) { - log_debug (LOG_ASSEMBLY, "monodroid_dlopen: library {} already loaded, returning handle {:p}", name, dso->handle); + log_debug (LOG_ASSEMBLY, "monodroid_dlopen: library %.*s already loaded, returning handle %p", static_cast(name.length ()), name.data (), dso->handle); return dso->handle; } if (dso->ignore) { - log_info (LOG_ASSEMBLY, "Request to load '{}' ignored, it is known not to exist", get_dso_name (dso)); + log_info (LOG_ASSEMBLY, "Request to load '%.*s' ignored, it is known not to exist", static_cast(dso_name.length ()), dso_name.data ()); return nullptr; } - std::string_view dso_name = get_dso_name (dso); StartupAwareLock lock (dso_handle_write_lock); #if defined (RELEASE) if (AndroidSystem::is_embedded_dso_mode_enabled ()) { @@ -153,7 +154,7 @@ namespace xamarin::android } hash_t name_hash = xxhash::hash (name.data (), name.size ()); - log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash for name '{}' is {:x}", name, name_hash); + log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash for name '%.*s' is %zx", static_cast(name.length ()), name.data (), static_cast(name_hash)); DSOCacheEntry *dso = nullptr; if constexpr (PREFER_AOT_CACHE) { @@ -194,8 +195,9 @@ namespace xamarin::android if (s == nullptr) { log_error ( LOG_ASSEMBLY, - "Could not find symbol '{}': {}", - name, + "Could not find symbol '%.*s': %s", + static_cast(name.length ()), + name.data (), optional_string (e) ); } diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh index 330f1e546b7..7b5d32b76e4 100644 --- a/src/native/clr/include/shared/log_types.hh +++ b/src/native/clr/include/shared/log_types.hh @@ -2,11 +2,7 @@ #include #include -#if !defined(XA_HOST_NATIVEAOT) -#include #include -#include -#endif #include #include "java-interop-logger.h" @@ -21,41 +17,27 @@ #undef log_info #endif -#if defined(XA_HOST_NATIVEAOT) #define DO_LOG_FMT(_level, _category_, _fmt_, ...) \ do { \ - static_assert (sizeof (#__VA_ARGS__) == 1, "NativeAOT logging must use preformatted messages"); \ if ((log_categories & ((_category_))) != 0) { \ - ::log_ ## _level ## _nocheck ((_category_), _fmt_); \ + ::xamarin::android::log_ ## _level ## _fmt ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \ } \ } while (0) -#else -#define DO_LOG_FMT(_level, _category_, _fmt_, ...) \ - do { \ - if ((log_categories & ((_category_))) != 0) { \ - ::log_ ## _level ## _nocheck_fmt ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \ - } \ - } while (0) -#endif -// -// For std::format spec, see https://en.cppreference.com/w/cpp/utility/format/spec -// - -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style +// NOTE: _fmt_ takes arguments in POSIX printf style. #define log_debug(_category_, _fmt_, ...) DO_LOG_FMT (debug, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style +// NOTE: _fmt_ takes arguments in POSIX printf style. #define log_info(_category_, _fmt_, ...) DO_LOG_FMT (info, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_warn(_category_, _fmt_, ...) ::log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +// NOTE: _fmt_ takes arguments in POSIX printf style. +#define log_warn(_category_, _fmt_, ...) ::xamarin::android::log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_error(_category_, _fmt_, ...) ::log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +// NOTE: _fmt_ takes arguments in POSIX printf style. +#define log_error(_category_, _fmt_, ...) ::xamarin::android::log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_fatal(_category_, _fmt_, ...) ::log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +// NOTE: _fmt_ takes arguments in POSIX printf style. +#define log_fatal(_category_, _fmt_, ...) ::xamarin::android::log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) namespace xamarin::android { // A slightly faster alternative to other log functions as it doesn't parse the message @@ -76,98 +58,95 @@ namespace xamarin::android { void log_error_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); void log_fatal_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); -} + [[gnu::always_inline]] + static inline void log_debug_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Debug, message); + } -#if !defined(XA_HOST_NATIVEAOT) -template [[gnu::always_inline]] -static inline constexpr void log_debug_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args) -{ - log_write (category, xamarin::android::LogLevel::Debug, std::format (fmt, std::forward(args)...).c_str ()); -} + [[gnu::always_inline]] + static inline void log_info_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Info, message); + } -[[gnu::always_inline]] -static inline constexpr void log_debug_nocheck (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Debug, message.data ()); -} + [[gnu::always_inline]] + static inline void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Warn, message); + } -template [[gnu::always_inline]] -static inline constexpr void log_info_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args) -{ - log_write (category, xamarin::android::LogLevel::Info, std::format (fmt, std::forward(args)...).c_str ()); -} + [[gnu::always_inline]] + static inline void log_error_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Error, message); + } -[[gnu::always_inline]] -static inline constexpr void log_info_nocheck (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Info, message.data ()); -} + [[gnu::always_inline]] + static inline void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Fatal, message); + } -template [[gnu::always_inline]] -static inline constexpr void log_warn_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept -{ - log_write (category, xamarin::android::LogLevel::Warn, std::format (fmt, std::forward(args)...).c_str ()); -} + [[gnu::always_inline]] + static inline void log_debug_fmt (LogCategories category, std::string const& message) noexcept + { + log_debug_fmt (category, std::string_view { message }); + } -[[gnu::always_inline]] -static inline constexpr void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Warn, message.data ()); -} + [[gnu::always_inline]] + static inline void log_info_fmt (LogCategories category, std::string const& message) noexcept + { + log_info_fmt (category, std::string_view { message }); + } -template [[gnu::always_inline]] -static inline constexpr void log_error_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept -{ - log_write (category, xamarin::android::LogLevel::Error, std::format (fmt, std::forward(args)...).c_str ()); -} + [[gnu::always_inline]] + static inline void log_warn_fmt (LogCategories category, std::string const& message) noexcept + { + log_warn_fmt (category, std::string_view { message }); + } -[[gnu::always_inline]] -static inline constexpr void log_error_fmt (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Error, message.data ()); -} + [[gnu::always_inline]] + static inline void log_error_fmt (LogCategories category, std::string const& message) noexcept + { + log_error_fmt (category, std::string_view { message }); + } -template [[gnu::always_inline]] -static inline constexpr void log_fatal_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept -{ - log_write (category, xamarin::android::LogLevel::Fatal, std::format (fmt, std::forward(args)...).c_str ()); + [[gnu::always_inline]] + static inline void log_fatal_fmt (LogCategories category, std::string const& message) noexcept + { + log_fatal_fmt (category, std::string_view { message }); + } } -[[gnu::always_inline]] -static inline constexpr void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Fatal, message.data ()); -} -#else [[gnu::always_inline]] static inline constexpr void log_debug_nocheck (LogCategories category, std::string_view const& message) noexcept { - log_write (category, xamarin::android::LogLevel::Debug, message.data ()); + xamarin::android::log_write (category, xamarin::android::LogLevel::Debug, message.data ()); } [[gnu::always_inline]] static inline constexpr void log_info_nocheck (LogCategories category, std::string_view const& message) noexcept { - log_write (category, xamarin::android::LogLevel::Info, message.data ()); + xamarin::android::log_write (category, xamarin::android::LogLevel::Info, message.data ()); } -[[gnu::always_inline]] -static inline constexpr void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept +static inline void log_debug_nocheck_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); +static inline void log_debug_nocheck_fmt (LogCategories category, const char *format, ...) noexcept { - log_write (category, xamarin::android::LogLevel::Warn, message.data ()); + va_list args; + va_start (args, format); + xamarin::android::log_writev (category, xamarin::android::LogLevel::Debug, format, args); + va_end (args); } -[[gnu::always_inline]] -static inline constexpr void log_error_fmt (LogCategories category, std::string_view const& message) noexcept +static inline void log_info_nocheck_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); +static inline void log_info_nocheck_fmt (LogCategories category, const char *format, ...) noexcept { - log_write (category, xamarin::android::LogLevel::Error, message.data ()); + va_list args; + va_start (args, format); + xamarin::android::log_writev (category, xamarin::android::LogLevel::Info, format, args); + va_end (args); } -[[gnu::always_inline]] -static inline constexpr void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Fatal, message.data ()); -} -#endif - extern unsigned int log_categories; diff --git a/src/native/clr/pinvoke-override/dynamic.cc b/src/native/clr/pinvoke-override/dynamic.cc index 5352aacab68..6633baeef24 100644 --- a/src/native/clr/pinvoke-override/dynamic.cc +++ b/src/native/clr/pinvoke-override/dynamic.cc @@ -24,7 +24,7 @@ extern "C" { [[gnu::flatten]] auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept -> void* { - log_debug (LOG_ASSEMBLY, "library_name == '{}'; entrypoint_name == '{}'"sv, optional_string (library_name), optional_string (entrypoint_name)); + log_debug (LOG_ASSEMBLY, "library_name == '%s'; entrypoint_name == '%s'", optional_string (library_name), optional_string (entrypoint_name)); if (library_name == nullptr || entrypoint_name == nullptr) [[unlikely]] { Helpers::abort_application ( @@ -39,12 +39,12 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons hash_t library_name_hash = xxhash::hash (library_name, strlen (library_name)); hash_t entrypoint_hash = xxhash::hash (entrypoint_name, strlen (entrypoint_name)); - log_debug (LOG_ASSEMBLY, "library_name_hash == 0x{:x}; entrypoint_hash == 0x{:x}"sv, library_name_hash, entrypoint_hash); + log_debug (LOG_ASSEMBLY, "library_name_hash == 0x%zx; entrypoint_hash == 0x%zx", static_cast(library_name_hash), static_cast(entrypoint_hash)); bool known_library = true; void *pinvoke_ptr = find_pinvoke (library_name_hash, entrypoint_hash, known_library); if (pinvoke_ptr != nullptr) [[likely]] { - log_debug (LOG_ASSEMBLY, "pinvoke_ptr == {:p}"sv, pinvoke_ptr); + log_debug (LOG_ASSEMBLY, "pinvoke_ptr == %p", pinvoke_ptr); return pinvoke_ptr; } @@ -70,7 +70,7 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons log_debug (LOG_ASSEMBLY, "p/invoke not from a known library, slow path taken."sv); pinvoke_ptr = handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash); - log_debug (LOG_ASSEMBLY, "foreign library pinvoke_ptr == {:p}"sv, pinvoke_ptr); + log_debug (LOG_ASSEMBLY, "foreign library pinvoke_ptr == %p", pinvoke_ptr); return pinvoke_ptr; } @@ -96,8 +96,8 @@ void PinvokeOverride::handle_jni_on_load (JavaVM *vm, void *reserved) noexcept const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept { - log_debug (LOG_ASSEMBLY, "[dynamic] clr_pinvoke_override (\"{}\", \"{}\")"sv, optional_string (library_name), optional_string (entry_point_name)); + log_debug (LOG_ASSEMBLY, "[dynamic] clr_pinvoke_override (\"%s\", \"%s\")", optional_string (library_name), optional_string (entry_point_name)); void *ret = PinvokeOverride::monodroid_pinvoke_override (library_name, entry_point_name); - log_debug (LOG_DEFAULT, "[dynamic] p/invoke {}found", ret == nullptr ? "not "sv : ""sv); + log_debug (LOG_DEFAULT, "[dynamic] p/invoke %sfound", ret == nullptr ? "not " : ""); return ret; } diff --git a/src/native/clr/pinvoke-override/precompiled.cc b/src/native/clr/pinvoke-override/precompiled.cc index 25c63b53d45..c4c48615ff3 100644 --- a/src/native/clr/pinvoke-override/precompiled.cc +++ b/src/native/clr/pinvoke-override/precompiled.cc @@ -1,3 +1,5 @@ +#include + #define PINVOKE_OVERRIDE_INLINE [[gnu::always_inline]] #include @@ -22,12 +24,12 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons PinvokeEntry *entry = find_pinvoke_address (entrypoint_hash, internal_pinvokes.data (), internal_pinvokes_count); if (entry == nullptr) [[unlikely]] { - log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '{} @ {}' (hash: {:x}) not found in compile-time map."sv, - optional_string (library_name), optional_string (entrypoint_name), entrypoint_hash); + log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '%s @ %s' (hash: %zx) not found in compile-time map.", + optional_string (library_name), optional_string (entrypoint_name), static_cast(entrypoint_hash)); log_fatal (LOG_ASSEMBLY, "compile-time map contents:"sv); for (size_t i = 0uz; i < internal_pinvokes_count; i++) { PinvokeEntry const& e = internal_pinvokes[i]; - log_fatal (LOG_ASSEMBLY, "\t'{}'={:p} (hash: {:x})"sv, optional_string (e.name), e.func, e.hash); + log_fatal (LOG_ASSEMBLY, "\t'%s'=%p (hash: %zx)", optional_string (e.name), e.func, static_cast(e.hash)); } Helpers::abort_application ( LOG_ASSEMBLY, @@ -68,7 +70,7 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons load_library_entry (library_name, entrypoint_name, *entry, dotnet_dso_handle); if (entry->func == nullptr) { - log_fatal (LOG_ASSEMBLY, "Failed to load symbol '{}' from shared library '{}'"sv, + log_fatal (LOG_ASSEMBLY, "Failed to load symbol '%s' from shared library '%s'", optional_string (entrypoint_name), optional_string (library_name)); return nullptr; // let Mono deal with the fallout } @@ -79,7 +81,7 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons // It's possible we don't have an entry for some `dotnet` p/invoke, fall back to the slow path below log_debug ( LOG_ASSEMBLY, - "Symbol '{}' in library '{}' not found in the generated tables, falling back to slow path"sv, + "Symbol '%s' in library '%s' not found in the generated tables, falling back to slow path", optional_string (entrypoint_name), optional_string (library_name) ); @@ -100,8 +102,8 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept { - log_debug (LOG_ASSEMBLY, "[precompiled] clr_pinvoke_override (\"{}\", \"{}\")"sv, library_name, entry_point_name); + log_debug (LOG_ASSEMBLY, "[precompiled] clr_pinvoke_override (\"%s\", \"%s\")", optional_string (library_name), optional_string (entry_point_name)); void *ret = PinvokeOverride::monodroid_pinvoke_override (library_name, entry_point_name); - log_debug (LOG_DEFAULT, "[precompiled] p/invoke {}found"sv, ret == nullptr ? "not"sv : ""sv); + log_debug (LOG_DEFAULT, "[precompiled] p/invoke %sfound", ret == nullptr ? "not" : ""); return ret; } diff --git a/src/native/clr/runtime-base/android-system.cc b/src/native/clr/runtime-base/android-system.cc index aee00900d55..b6c06e90a70 100644 --- a/src/native/clr/runtime-base/android-system.cc +++ b/src/native/clr/runtime-base/android-system.cc @@ -49,7 +49,7 @@ AndroidSystem::setup_environment (const char *name, const char *value) noexcept if (isupper (name [0]) || name [0] == '_') { if (setenv (name, v, 1) < 0) { - log_warn (LOG_DEFAULT, "(Debug) Failed to set environment variable: {}", strerror (errno)); + log_warn (LOG_DEFAULT, "(Debug) Failed to set environment variable: %s", strerror (errno)); } return; } @@ -64,13 +64,13 @@ AndroidSystem::setup_environment_from_override_file (dynamic_local_string::max () && errno == ERANGE) || (buf[0] != '\0' && *endptr != '\0')) { - log_warn (LOG_DEFAULT, "Malformed header of the environment override file {}: name width has invalid format", path.get ()); + log_warn (LOG_DEFAULT, "Malformed header of the environment override file %s: name width has invalid format", path.get ()); return; } unsigned long value_width = strtoul (buf.get () + 11, &endptr, 16); if ((value_width == std::numeric_limits::max () && errno == ERANGE) || (buf[0] != '\0' && *endptr != '\0')) { - log_warn (LOG_DEFAULT, "Malformed header of the environment override file {}: value width has invalid format", path.get ()); + log_warn (LOG_DEFAULT, "Malformed header of the environment override file %s: value width has invalid format", path.get ()); return; } uint64_t data_width = name_width + value_width; if (data_width > file_size - Constants::OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE || (file_size - Constants::OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE) % data_width != 0) { - log_warn (LOG_DEFAULT, "Malformed environment override file {}: invalid data size", path.get ()); + log_warn (LOG_DEFAULT, "Malformed environment override file %s: invalid data size", path.get ()); return; } @@ -136,11 +136,11 @@ AndroidSystem::setup_environment_from_override_file (dynamic_local_string 0 && data_size >= data_width) { if (*name == '\0') { - log_warn (LOG_DEFAULT, "Malformed environment override file {}: name at offset {} is empty", path.get (), name - buf.get ()); + log_warn (LOG_DEFAULT, "Malformed environment override file %s: name at offset %td is empty", path.get (), name - buf.get ()); return; } - log_debug (LOG_DEFAULT, "Setting environment variable from the override file {}: '{}' = '{}'", path.get (), name, name + name_width); + log_debug (LOG_DEFAULT, "Setting environment variable from the override file %s: '%s' = '%s'", path.get (), name, name + name_width); setup_environment (name, name + name_width); name += data_width; data_size -= data_width; @@ -161,7 +161,7 @@ AndroidSystem::add_apk_libdir (std::string_view const& apk, size_t &index, std:: dir.append (lib_prefix); dir.append (abi); app_lib_directories [index] = dir; - log_debug (LOG_ASSEMBLY, "Added APK DSO lookup location: {}", dir); + log_debug (LOG_ASSEMBLY, "Added APK DSO lookup location: %s", dir.c_str ()); index++; } @@ -196,7 +196,7 @@ AndroidSystem::setup_apk_directories (unsigned short running_on_cpu, jstring_arr add_apk_libdir (base_apk, number_of_added_directories, abi); } - log_debug (LOG_DEFAULT, "Number of added dirs: {}", number_of_added_directories); + log_debug (LOG_DEFAULT, "Number of added dirs: %zu", number_of_added_directories); if (app_lib_directories.size () == number_of_added_directories) [[likely]] { return; } @@ -213,7 +213,7 @@ AndroidSystem::setup_app_library_directories (jstring_array_wrapper& runtimeApks app_lib_directories = std::span (single_app_lib_directory); app_lib_directories [0] = std::string (appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); - log_debug (LOG_ASSEMBLY, "Added filesystem DSO lookup location: {}", app_lib_directories [0]); + log_debug (LOG_ASSEMBLY, "Added filesystem DSO lookup location: %s", app_lib_directories [0].c_str ()); return; } @@ -237,7 +237,7 @@ void AndroidSystem::setup_environment () noexcept { if (application_config.environment_variable_count > 0) { - log_debug (LOG_DEFAULT, "Setting environment variables ({})", application_config.environment_variable_count); + log_debug (LOG_DEFAULT, "Setting environment variables (%u)", application_config.environment_variable_count); HostEnvironment::set_values ( application_config.environment_variable_count, app_environment_variables, @@ -246,7 +246,7 @@ AndroidSystem::setup_environment () noexcept } if (application_config.system_property_count > 0) { - log_debug (LOG_DEFAULT, "Setting system properties ({})", application_config.system_property_count); + log_debug (LOG_DEFAULT, "Setting system properties (%u)", application_config.system_property_count); HostEnvironment::set_values ( application_config.system_property_count, app_system_properties, @@ -259,9 +259,9 @@ AndroidSystem::setup_environment () noexcept dynamic_local_string env_override_file; Util::path_combine (env_override_file, std::string_view {primary_override_dir}, Constants::OVERRIDE_ENVIRONMENT_FILE_NAME); - log_debug (LOG_DEFAULT, "{}", env_override_file.get ()); + log_debug (LOG_DEFAULT, "%s", env_override_file.get ()); if (Util::file_exists (env_override_file)) { - log_debug (LOG_DEFAULT, "Loading {}"sv, env_override_file.get ()); + log_debug (LOG_DEFAULT, "Loading %s", env_override_file.get ()); setup_environment_from_override_file (env_override_file); } #endif // def DEBUG @@ -274,12 +274,12 @@ AndroidSystem::detect_embedded_dso_mode (jstring_array_wrapper& appDirs) noexcep dynamic_local_string libmonodroid_path; Util::path_combine (libmonodroid_path, appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_string_view (), "libmonodroid.so"sv); - log_debug (LOG_ASSEMBLY, "Checking if libmonodroid was unpacked to {}", libmonodroid_path.get ()); + log_debug (LOG_ASSEMBLY, "Checking if libmonodroid was unpacked to %s", libmonodroid_path.get ()); if (!Util::file_exists (libmonodroid_path)) { - log_debug (LOG_ASSEMBLY, "{} not found, assuming application/android:extractNativeLibs == false", libmonodroid_path.get ()); + log_debug (LOG_ASSEMBLY, "%s not found, assuming application/android:extractNativeLibs == false", libmonodroid_path.get ()); set_embedded_dso_mode_enabled (true); } else { - log_debug (LOG_ASSEMBLY, "Native libs extracted to {}, assuming application/android:extractNativeLibs == true", appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); + log_debug (LOG_ASSEMBLY, "Native libs extracted to %s, assuming application/android:extractNativeLibs == true", appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); set_embedded_dso_mode_enabled (false); native_libraries_dir.assign (appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); } diff --git a/src/native/clr/startup/zip.cc b/src/native/clr/startup/zip.cc index 0c5413fb2d6..9dc2bf1eb1f 100644 --- a/src/native/clr/startup/zip.cc +++ b/src/native/clr/startup/zip.cc @@ -28,8 +28,8 @@ bool Zip::zip_adjust_data_offset (int fd, zip_scan_state &state) noexcept if (result < 0) { log_error ( LOG_ASSEMBLY, - "Failed to seek to archive entry local header at offset {}. {} (result: {}; errno: {})", - state.local_header_offset, std::strerror (errno), result, errno + "Failed to seek to archive entry local header at offset %u. %s (result: %lld; errno: %d)", + state.local_header_offset, std::strerror (errno), static_cast(result), errno ); return false; } @@ -39,32 +39,32 @@ bool Zip::zip_adjust_data_offset (int fd, zip_scan_state &state) noexcept ssize_t nread = ::read (fd, local_header.data (), local_header.size ()); if (nread < 0 || nread != ZIP_LOCAL_LEN) { - log_error (LOG_ASSEMBLY, "Failed to read local header at offset {}: {} (nread: {}; errno: {})", state.local_header_offset, std::strerror (errno), nread, errno); + log_error (LOG_ASSEMBLY, "Failed to read local header at offset %u: %s (nread: %zd; errno: %d)", state.local_header_offset, std::strerror (errno), nread, errno); return false; } size_t index = 0; if (!zip_read_field (local_header, index, signature)) { - log_error (LOG_ASSEMBLY, "Failed to read Local Header entry signature at offset {}", state.local_header_offset); + log_error (LOG_ASSEMBLY, "Failed to read Local Header entry signature at offset %u", state.local_header_offset); return false; } if (memcmp (signature.data (), ZIP_LOCAL_MAGIC.data (), signature.size ()) != 0) { - log_error (LOG_ASSEMBLY, "Invalid Local Header entry signature at offset {}", state.local_header_offset); + log_error (LOG_ASSEMBLY, "Invalid Local Header entry signature at offset %u", state.local_header_offset); return false; } uint16_t file_name_length; index = LH_FILE_NAME_LENGTH_OFFSET; if (!zip_read_field (local_header, index, file_name_length)) { - log_error (LOG_ASSEMBLY, "Failed to read Local Header 'file name length' field at offset {}", (state.local_header_offset + index)); + log_error (LOG_ASSEMBLY, "Failed to read Local Header 'file name length' field at offset %zu", (state.local_header_offset + index)); return false; } uint16_t extra_field_length; index = LH_EXTRA_LENGTH_OFFSET; if (!zip_read_field (local_header, index, extra_field_length)) { - log_error (LOG_ASSEMBLY, "Failed to read Local Header 'extra field length' field at offset {}", (state.local_header_offset + index)); + log_error (LOG_ASSEMBLY, "Failed to read Local Header 'extra field length' field at offset %zu", (state.local_header_offset + index)); return false; } @@ -154,7 +154,7 @@ bool Zip::zip_load_entry_common (size_t entry_index, std::vector const& bool result = zip_read_entry_info (buf, entry_name, state); - log_debug (LOG_ASSEMBLY, "{} entry: {}", state.file_name, optional_string (entry_name.get (), "unknown")); + log_debug (LOG_ASSEMBLY, "%.*s entry: %s", static_cast(state.file_name.length ()), state.file_name.data (), optional_string (entry_name.get (), "unknown")); if (!result || entry_name.empty ()) { Helpers::abort_application ( LOG_ASSEMBLY, @@ -177,7 +177,7 @@ bool Zip::zip_load_entry_common (size_t entry_index, std::vector const& ); } - log_debug (LOG_ASSEMBLY, " ZIP: local header offset: {}; data offset: {}; file size: {}", state.local_header_offset, state.data_offset, state.file_size); + log_debug (LOG_ASSEMBLY, " ZIP: local header offset: %u; data offset: %u; file size: %u", state.local_header_offset, state.data_offset, state.file_size); if (state.compression_method != 0) { return false; } @@ -249,7 +249,7 @@ template [[gnu::always_inline]] bool Zip::zip_ensure_valid_params (T const& buf, size_t index, size_t to_read) noexcept { if (index + to_read > buf.size ()) { - log_error (LOG_ASSEMBLY, "Buffer too short to read {} bytes of data", to_read); + log_error (LOG_ASSEMBLY, "Buffer too short to read %zu bytes of data", to_read); return false; } @@ -311,14 +311,14 @@ bool Zip::zip_read_cd_info (int apk_fd, uint32_t& cd_offset, uint32_t& cd_size, // The simplest case - no file comment off_t ret = ::lseek (apk_fd, -ZIP_EOCD_LEN, SEEK_END); if (ret < 0) { - log_error (LOG_ASSEMBLY, "Unable to seek into the APK to find ECOD: {} (ret: {}; errno: {})", std::strerror (errno), ret, errno); + log_error (LOG_ASSEMBLY, "Unable to seek into the APK to find ECOD: %s (ret: %lld; errno: %d)", std::strerror (errno), static_cast(ret), errno); return false; } std::array eocd; ssize_t nread = ::read (apk_fd, eocd.data (), eocd.size ()); if (nread < 0 || nread != eocd.size ()) { - log_error (LOG_ASSEMBLY, "Failed to read EOCD from the APK: {} (nread: {}; errno: {})", std::strerror (errno), nread, errno); + log_error (LOG_ASSEMBLY, "Failed to read EOCD from the APK: %s (nread: %zd; errno: %d)", std::strerror (errno), nread, errno); return false; } @@ -338,7 +338,7 @@ bool Zip::zip_read_cd_info (int apk_fd, uint32_t& cd_offset, uint32_t& cd_size, constexpr size_t alloc_size = 65535uz + ZIP_EOCD_LEN; // 64k is the biggest comment size allowed ret = ::lseek (apk_fd, static_cast(-alloc_size), SEEK_END); if (ret < 0) { - log_error (LOG_ASSEMBLY, "Unable to seek into the file to find ECOD before APK comment: {} (ret: {}; errno: {})", std::strerror (errno), ret, errno); + log_error (LOG_ASSEMBLY, "Unable to seek into the file to find ECOD before APK comment: %s (ret: %lld; errno: %d)", std::strerror (errno), static_cast(ret), errno); return false; } @@ -347,7 +347,7 @@ bool Zip::zip_read_cd_info (int apk_fd, uint32_t& cd_offset, uint32_t& cd_size, nread = ::read (apk_fd, buf.data (), buf.size ()); if (nread < 0 || static_cast(nread) != alloc_size) { - log_error (LOG_ASSEMBLY, "Failed to read EOCD and comment from the APK: {} (nread: {}; errno: {})", std::strerror (errno), nread, errno); + log_error (LOG_ASSEMBLY, "Failed to read EOCD and comment from the APK: %s (nread: %zd; errno: %d)", std::strerror (errno), nread, errno); return false; } @@ -388,9 +388,9 @@ bool Zip::zip_scan_entries (int apk_fd, std::string_view const& apk_path, ScanCa ); } - log_debug (LOG_ASSEMBLY, "Central directory offset: {}", cd_offset); - log_debug (LOG_ASSEMBLY, "Central directory size: {}", cd_size); - log_debug (LOG_ASSEMBLY, "Central directory entries: {}", cd_entries); + log_debug (LOG_ASSEMBLY, "Central directory offset: %u", cd_offset); + log_debug (LOG_ASSEMBLY, "Central directory size: %u", cd_size); + log_debug (LOG_ASSEMBLY, "Central directory entries: %u", cd_entries); off_t retval = ::lseek (apk_fd, static_cast(cd_offset), SEEK_SET); if (retval < 0) { @@ -472,7 +472,7 @@ void Zip::scan_archive (std::string_view const& apk_path, ScanCallbackFn entry_c ) ); } - log_debug (LOG_ASSEMBLY, "APK {} FD: {}", apk_path, fd); + log_debug (LOG_ASSEMBLY, "APK %.*s FD: %d", static_cast(apk_path.length ()), apk_path.data (), fd); if (!zip_scan_entries (fd, apk_path, entry_cb)) { return; } @@ -480,8 +480,9 @@ void Zip::scan_archive (std::string_view const& apk_path, ScanCallbackFn entry_c if (close (fd) < 0) { log_warn ( LOG_ASSEMBLY, - "Failed to close file descriptor for {}. {}", - apk_path, + "Failed to close file descriptor for %.*s. %s", + static_cast(apk_path.length ()), + apk_path.data (), strerror (errno) ); } diff --git a/src/native/common/include/runtime-base/dso-loader.hh b/src/native/common/include/runtime-base/dso-loader.hh index f44947453b4..753d91361bb 100644 --- a/src/native/common/include/runtime-base/dso-loader.hh +++ b/src/native/common/include/runtime-base/dso-loader.hh @@ -41,10 +41,10 @@ namespace xamarin::android { return load_jni (path, true /* name_is_path */); } - log_info (LOG_ASSEMBLY, "[filesystem] Trying to load shared library '{}'", path); + log_info (LOG_ASSEMBLY, "[filesystem] Trying to load shared library '%.*s'", static_cast(path.length ()), path.data ()); if constexpr (!SkipExistsCheck) { if (!AndroidSystem::is_embedded_dso_mode_enabled () && !Util::file_exists (path)) { - log_info (LOG_ASSEMBLY, "Shared library '{}' not found", path); + log_info (LOG_ASSEMBLY, "Shared library '%.*s' not found", static_cast(path.length ()), path.data ()); return nullptr; } } @@ -60,7 +60,7 @@ namespace xamarin::android { return load_jni (name, true /* name_is_path */); } - log_info (LOG_ASSEMBLY, "[apk] Trying to load shared library '{}', offset in the apk == {}", name, offset); + log_info (LOG_ASSEMBLY, "[apk] Trying to load shared library '%.*s', offset in the apk == %lld", static_cast(name.length ()), name.data (), static_cast(offset)); android_dlextinfo dli; dli.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET; @@ -75,7 +75,7 @@ namespace xamarin::android { static auto log_and_return (void *handle, std::string_view const& full_name) -> void* { if (handle != nullptr) [[likely]] { - log_debug (LOG_ASSEMBLY, "Shared library {} loaded (handle {:p})", full_name, handle); + log_debug (LOG_ASSEMBLY, "Shared library %.*s loaded (handle %p)", static_cast(full_name.length ()), full_name.data (), handle); return handle; } @@ -85,8 +85,9 @@ namespace xamarin::android { } log_error ( LOG_ASSEMBLY, - "Could not load library '{}'. {}"sv, - full_name, + "Could not load library '%.*s'. %s", + static_cast(full_name.length ()), + full_name.data (), load_error ); @@ -95,7 +96,7 @@ namespace xamarin::android { static auto load_jni (std::string_view const& name, bool name_is_path) -> void* { - log_debug (LOG_ASSEMBLY, "Trying to load loading shared JNI library {} with System.loadLibrary", name); + log_debug (LOG_ASSEMBLY, "Trying to load loading shared JNI library %.*s with System.loadLibrary", static_cast(name.length ()), name.data ()); auto get_file_name = [](std::string_view const& full_name, bool is_path) -> std::string_view { if (!is_path) { @@ -175,8 +176,9 @@ namespace xamarin::android { // way :( // We must use full name of the library, because dlopen won't accept an undecorated one without kicking up // a fuss. - log_debug (LOG_ASSEMBLY, "Attempting to get library {} handle after System.loadLibrary. Will try to load using '{}'", name, get_file_name (name, name_is_path)); - return log_and_return (dlopen (get_file_name (name, name_is_path).data (), RTLD_NOLOAD), name); + std::string_view file_name = get_file_name (name, name_is_path); + log_debug (LOG_ASSEMBLY, "Attempting to get library %.*s handle after System.loadLibrary. Will try to load using '%.*s'", static_cast(name.length ()), name.data (), static_cast(file_name.length ()), file_name.data ()); + return log_and_return (dlopen (file_name.data (), RTLD_NOLOAD), name); } private: diff --git a/src/native/common/include/runtime-base/mainthread-dso-loader.hh b/src/native/common/include/runtime-base/mainthread-dso-loader.hh index 144ad67db81..fe7e9d635b0 100644 --- a/src/native/common/include/runtime-base/mainthread-dso-loader.hh +++ b/src/native/common/include/runtime-base/mainthread-dso-loader.hh @@ -74,7 +74,7 @@ namespace xamarin::android { if (!undecorated_library_name.empty ()) [[unlikely]] { Helpers::abort_application ("Main thread DSO loader object reused! DO NOT DO THAT!"sv); } - log_debug (LOG_ASSEMBLY, "Running DSO loader on thread {}, dispatching to main thread"sv, gettid ()); + log_debug (LOG_ASSEMBLY, "Running DSO loader on thread %d, dispatching to main thread", gettid ()); undecorated_library_name = undecorated_name; load_success = false; @@ -83,7 +83,7 @@ namespace xamarin::android { if (nbytes == -1) { log_warn ( LOG_ASSEMBLY, - "Write failure when posting a DSO load event to main thread. {}"sv, + "Write failure when posting a DSO load event to main thread. %s", strerror (errno) ); return false; @@ -95,7 +95,7 @@ namespace xamarin::android { // We'll wait for up to 3s, it should be more than enough time for the library to load bool success = load_complete_sem.try_acquire_for (3s); if (!success) { - log_warn (LOG_ASSEMBLY, "Timeout while waiting for shared library '{}' to load."sv, full_name); + log_warn (LOG_ASSEMBLY, "Timeout while waiting for shared library '%.*s' to load.", static_cast(full_name.length ()), full_name.data ()); return false; } @@ -136,9 +136,10 @@ namespace xamarin::android { log_debug ( LOG_ASSEMBLY, - "Looper CB called on thread {}. Will attempt to load DSO '{}'"sv, + "Looper CB called on thread %d. Will attempt to load DSO '%.*s'", gettid (), - self->undecorated_library_name + static_cast(self->undecorated_library_name.length ()), + self->undecorated_library_name.data () ); self->load_success = SystemLoadLibraryWrapper::load (main_thread_jni_env /* RuntimeEnvironment::get_jnienv () */, self->undecorated_library_name); diff --git a/src/native/common/include/runtime-base/strings.hh b/src/native/common/include/runtime-base/strings.hh index 87098365967..91e7fddf070 100644 --- a/src/native/common/include/runtime-base/strings.hh +++ b/src/native/common/include/runtime-base/strings.hh @@ -5,12 +5,12 @@ #include #include #include -#include #include #include #include #include +#include "java-interop-logger.h" #include #include @@ -23,7 +23,7 @@ using Constants = xamarin::android::internal::SharedConstants; #endif namespace xamarin::android { - void log_write (LogCategories category, LogLevel level, const char *message) noexcept; + void log_write_fmt (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); static constexpr size_t SENSIBLE_TYPE_NAME_LENGTH = 128uz; static constexpr size_t SENSIBLE_PATH_MAX = 256uz; @@ -209,9 +209,7 @@ namespace xamarin::android { } if (!can_access (start_index)) { - char message[128]; - snprintf (message, sizeof (message), "Cannot convert string to integer, index %zu is out of range", start_index); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_write_fmt (LOG_DEFAULT, LogLevel::Error, "Cannot convert string to integer, index %zu is out of range", start_index); return false; } @@ -235,23 +233,24 @@ namespace xamarin::android { } if (out_of_range || errno == ERANGE) { - char message[256]; - snprintf (message, sizeof (message), "Value %s is out of range of this type (%lld..%llu)", reinterpret_cast(s), static_cast(min), static_cast(max)); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_write_fmt ( + LOG_DEFAULT, + LogLevel::Error, + "Value %s is out of range of this type (%lld..%llu)", + reinterpret_cast(s), + static_cast(min), + static_cast(max) + ); return false; } if (endp == s) { - char message[256]; - snprintf (message, sizeof (message), "Value %s does not represent a base %d integer", reinterpret_cast(s), base); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_write_fmt (LOG_DEFAULT, LogLevel::Error, "Value %s does not represent a base %d integer", reinterpret_cast(s), base); return false; } if (*endp != '\0') { - char message[256]; - snprintf (message, sizeof (message), "Value %s has non-numeric characters at the end", reinterpret_cast(s)); - log_write (LOG_DEFAULT, LogLevel::Error, message); + log_write_fmt (LOG_DEFAULT, LogLevel::Error, "Value %s has non-numeric characters at the end", reinterpret_cast(s)); return false; } diff --git a/src/native/common/include/runtime-base/system-loadlibrary-wrapper.hh b/src/native/common/include/runtime-base/system-loadlibrary-wrapper.hh index 577d6ba8e6f..44ba4d34092 100644 --- a/src/native/common/include/runtime-base/system-loadlibrary-wrapper.hh +++ b/src/native/common/include/runtime-base/system-loadlibrary-wrapper.hh @@ -32,7 +32,7 @@ namespace xamarin::android { // std::string is needed because we must pass a NUL-terminated string to Java, otherwise // strange things happen (and std::string_view is not necessarily such a string) const std::string lib_name { undecorated_lib_name }; - log_debug (LOG_ASSEMBLY, "Undecorated library name: {}", lib_name); + log_debug (LOG_ASSEMBLY, "Undecorated library name: %s", lib_name.c_str ()); jstring java_lib_name = jni_env->NewStringUTF (lib_name.c_str ()); if (java_lib_name == nullptr) [[unlikely]] { diff --git a/src/native/common/include/runtime-base/timing-internal.hh b/src/native/common/include/runtime-base/timing-internal.hh index 4d52be9c334..113388a952b 100644 --- a/src/native/common/include/runtime-base/timing-internal.hh +++ b/src/native/common/include/runtime-base/timing-internal.hh @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -261,9 +260,7 @@ namespace xamarin::android { // likely we'll run out of memory way, way, way before that happens size_t old_size = events.capacity (); events.reserve (old_size << 1); - char message[128]; - snprintf (message, sizeof (message), "Reallocated timing event buffer from %zu to %zu", old_size, events.size ()); - log_write (LOG_TIMING, LogLevel::Warn, message); + log_write_fmt (LOG_TIMING, LogLevel::Warn, "Reallocated timing event buffer from %zu to %zu", old_size, events.size ()); } } @@ -288,7 +285,7 @@ namespace xamarin::android { } if (!index.has_value ()) [[unlikely]] { - log_warn (LOG_TIMING, "FastTiming::end_event called without prior FastTiming::start_event called"sv); + log_warn (LOG_TIMING, "FastTiming::end_event called without prior FastTiming::start_event called"); return; } @@ -304,7 +301,7 @@ namespace xamarin::android { { auto index = pop_valid_sequence_index (); if (!index.has_value ()) [[unlikely]] { - log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"sv); + log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"); return; } @@ -317,7 +314,7 @@ namespace xamarin::android { { auto index = pop_valid_sequence_index (); if (!index.has_value ()) [[unlikely]] { - log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"sv); + log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"); return; } @@ -330,7 +327,7 @@ namespace xamarin::android { { auto index = pop_valid_sequence_index (); if (!index.has_value ()) [[unlikely]] { - log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"sv); + log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"); return; } @@ -381,9 +378,7 @@ namespace xamarin::android { { struct timespec t; if (clock_gettime (CLOCK_MONOTONIC_RAW, &t) != 0) [[unlikely]] { - char message[128]; - snprintf (message, sizeof (message), "clock_gettime failed for CLOCK_MONOTONIC_RAW: %s", optional_string (strerror (errno))); - log_write (LOG_TIMING, LogLevel::Warn, message); + log_write_fmt (LOG_TIMING, LogLevel::Warn, "clock_gettime failed for CLOCK_MONOTONIC_RAW: %s", optional_string (strerror (errno))); return {}; // Results will be nonsensical, but no point in aborting the app } return time_point (chrono::seconds (t.tv_sec) + chrono::nanoseconds (t.tv_nsec)); @@ -499,9 +494,7 @@ namespace xamarin::android { return; } - char warning[128]; - snprintf (warning, sizeof (warning), "Unknown event kind '%u' logged", static_cast(kind)); - log_write (LOG_TIMING, LogLevel::Warn, warning); + log_write_fmt (LOG_TIMING, LogLevel::Warn, "Unknown event kind '%u' logged", static_cast(kind)); append_desc ("unknown event kind"sv); } @@ -513,9 +506,7 @@ namespace xamarin::android { auto is_valid_event_index (size_t index, std::source_location sloc = std::source_location::current ()) const noexcept -> bool { if (index >= events.capacity ()) [[unlikely]] { - char message[256]; - snprintf (message, sizeof (message), "Invalid event index passed to method '%s'", sloc.function_name ()); - log_write (LOG_TIMING, LogLevel::Warn, message); + log_write_fmt (LOG_TIMING, LogLevel::Warn, "Invalid event index passed to method '%s'", sloc.function_name ()); return false; } diff --git a/src/native/common/include/runtime-base/timing.hh b/src/native/common/include/runtime-base/timing.hh index d6d60246096..584d69c266e 100644 --- a/src/native/common/include/runtime-base/timing.hh +++ b/src/native/common/include/runtime-base/timing.hh @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -88,18 +87,15 @@ namespace xamarin::android auto seconds = static_cast((std::chrono::duration_cast(interval).count ())); auto milliseconds = static_cast((std::chrono::duration_cast(interval)).count ()); auto nanoseconds = static_cast((interval % std::chrono::milliseconds (1)).count ()); - char text[256]; - snprintf ( - text, - sizeof (text), + log_write_fmt ( + LOG_TIMING, + level, "%s; elapsed: %llu:%llu::%llu", message == nullptr ? "" : message, static_cast(seconds), static_cast(milliseconds), static_cast(nanoseconds) ); - - log_write (LOG_TIMING, level, text); } private: diff --git a/src/native/common/include/shared/cpp-util.hh b/src/native/common/include/shared/cpp-util.hh index ebdc4d4d720..13e6d915218 100644 --- a/src/native/common/include/shared/cpp-util.hh +++ b/src/native/common/include/shared/cpp-util.hh @@ -11,9 +11,14 @@ #include #include -#include +#include "java-interop-logger.h" #include +#include + +namespace xamarin::android { + void log_write_fmt (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); +} namespace xamarin::android::detail { [[gnu::always_inline, gnu::flatten]] @@ -96,9 +101,7 @@ abort_if_negative_integer_argument (int arg, const char *arg_name, std::source_l [[gnu::always_inline]] inline void pd_log_location (std::source_location sloc = std::source_location::current ()) noexcept { - char message[512]; - snprintf (message, sizeof (message), "loc: %s:%u ('%s')", sloc.file_name (), sloc.line (), sloc.function_name ()); - __android_log_write (ANDROID_LOG_WARN, "monodroid", message); + xamarin::android::log_write_fmt (LOG_DEFAULT, xamarin::android::LogLevel::Warn, "loc: %s:%u ('%s')", sloc.file_name (), sloc.line (), sloc.function_name ()); } namespace xamarin::android diff --git a/src/native/common/runtime-base/timing-internal.cc b/src/native/common/runtime-base/timing-internal.cc index ebf8d111893..818c08b8ba1 100644 --- a/src/native/common/runtime-base/timing-internal.cc +++ b/src/native/common/runtime-base/timing-internal.cc @@ -63,7 +63,7 @@ void FastTiming::parse_options (dynamic_local_property_string const& value) noex if (param.starts_with (OPT_DURATION)) { if (!param.to_integer (duration_ms, OPT_DURATION.length ())) { - log_warn (LOG_TIMING, "Failed to parse duration in milliseconds from '%s'"sv, param.start ()); + log_warn (LOG_TIMING, "Failed to parse duration in milliseconds from '%s'", param.start ()); duration_ms = default_duration_milliseconds; } continue; @@ -200,16 +200,16 @@ void FastTiming::dump_to_file (size_t entries) noexcept FILE *timing_log = Util::monodroid_fopen (timing_log_path.get (), "w"); if (timing_log == nullptr) { - log_error (LOG_TIMING, "[2/2] Unable to create the performance measurements file '{}'"sv, timing_log_path.get ()); + log_error (LOG_TIMING, "[2/2] Unable to create the performance measurements file '%s'", timing_log_path.get ()); return; } if (!Util::set_world_accessible (fileno (timing_log))) { - log_warn (LOG_TIMING, "[2/2] Failed to make performance measurements file '{}' world-readable"sv, timing_log_path.get ()); + log_warn (LOG_TIMING, "[2/2] Failed to make performance measurements file '%s' world-readable", timing_log_path.get ()); return; } - log_info (LOG_TIMING, "[2/2] Performance measurement results logged to file: {}"sv, timing_log_path.get ()); + log_info (LOG_TIMING, "[2/2] Performance measurement results logged to file: %s", timing_log_path.get ()); auto line_writer = [=](std::string_view const& msg) { if (!msg.empty ()) { diff --git a/src/native/mono/monodroid/debug.cc b/src/native/mono/monodroid/debug.cc index d37ad8cf5ff..67ec1838970 100644 --- a/src/native/mono/monodroid/debug.cc +++ b/src/native/mono/monodroid/debug.cc @@ -99,7 +99,7 @@ Debug::monodroid_profiler_load (const char *libmono_path, const char *desc, cons if (!found) log_warn (LOG_DEFAULT, - "The '{}' profiler wasn't found in the main executable nor could it be loaded from '{}'.", + "The '%s' profiler wasn't found in the main executable nor could it be loaded from '%s'.", optional_string (mname.get ()), optional_string (libname.get ()) ); @@ -113,7 +113,7 @@ bool Debug::load_profiler (void *handle, const char *desc, const char *symbol) { ProfilerInitializer func = reinterpret_cast (java_interop_lib_symbol (handle, symbol, nullptr)); - log_warn (LOG_DEFAULT, "Looking for profiler init symbol '{}'? {:p}", optional_string (symbol), reinterpret_cast(func)); + log_warn (LOG_DEFAULT, "Looking for profiler init symbol '%s'? %p", optional_string (symbol), reinterpret_cast(func)); if (func != nullptr) { func (desc); @@ -143,7 +143,7 @@ Debug::parse_options (char *options, ConnOptions *opts) { char **args, **ptr; - log_info (LOG_DEFAULT, "Connection options: '{}'", optional_string (options)); + log_info (LOG_DEFAULT, "Connection options: '%s'", optional_string (options)); args = Util::monodroid_strsplit (options, ",", 0); @@ -153,12 +153,12 @@ Debug::parse_options (char *options, ConnOptions *opts) if (strstr (arg, "port=") == arg) { int port = atoi (arg + strlen ("port=")); if (port < 0 || port > std::numeric_limits::max ()) { - log_error (LOG_DEFAULT, "Invalid debug port value {}", port); + log_error (LOG_DEFAULT, "Invalid debug port value %u", port); continue; } conn_port = static_cast(port); - log_info (LOG_DEFAULT, "XS port = {}", conn_port); + log_info (LOG_DEFAULT, "XS port = %u", conn_port); } else if (strstr (arg, "timeout=") == arg) { char *endp; @@ -167,7 +167,7 @@ Debug::parse_options (char *options, ConnOptions *opts) if ((endp == arg) || (*endp != '\0')) log_error (LOG_DEFAULT, "Invalid --timeout argument."sv); } else { - log_info (LOG_DEFAULT, "Unknown connection option: '{}'", optional_string (arg)); + log_info (LOG_DEFAULT, "Unknown connection option: '%s'", optional_string (arg)); } } } @@ -191,7 +191,7 @@ Debug::start_connection (char *options) cur_time = time (nullptr); if (opts.timeout_time && cur_time > opts.timeout_time) { - log_warn (LOG_DEBUGGER, "Not connecting to IDE as the timeout value has been reached; current-time: {} timeout: {}", cur_time, opts.timeout_time); + log_warn (LOG_DEBUGGER, "Not connecting to IDE as the timeout value has been reached; current-time: %lld timeout: %lld", static_cast(cur_time), static_cast(opts.timeout_time)); return DebuggerConnectionStatus::Unconnected; } @@ -202,7 +202,7 @@ Debug::start_connection (char *options) res = pthread_create (&conn_thread_id, nullptr, xamarin::android::conn_thread, this); if (res) { - log_error (LOG_DEFAULT, "Failed to create connection thread: {}", strerror (errno)); + log_error (LOG_DEFAULT, "Failed to create connection thread: %s", strerror (errno)); return DebuggerConnectionStatus::Error; } @@ -265,20 +265,20 @@ Debug::process_connection (int fd) return false; } if (rv <= 0) { - log_info (LOG_DEFAULT, "Error while receiving command from XS ({})", strerror (errno)); + log_info (LOG_DEFAULT, "Error while receiving command from XS (%s)", strerror (errno)); return false; } rv = Util::recv_uninterrupted (fd, command, cmd_len); if (rv <= 0) { - log_info (LOG_DEFAULT, "Error while receiving command from XS ({})", strerror (errno)); + log_info (LOG_DEFAULT, "Error while receiving command from XS (%s)", strerror (errno)); return false; } // null-terminate command [cmd_len] = 0; - log_info (LOG_DEFAULT, "Received cmd: '{}'.", optional_string (command)); + log_info (LOG_DEFAULT, "Received cmd: '%s'.", optional_string (command)); if (process_cmd (fd, command)) return true; @@ -290,14 +290,14 @@ Debug::handle_server_connection (void) { int listen_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (listen_socket == -1) { - log_info (LOG_DEFAULT, "Could not create socket for XS to connect to: {}", strerror (errno)); + log_info (LOG_DEFAULT, "Could not create socket for XS to connect to: %s", strerror (errno)); return 1; } int flags = 1; int rv = setsockopt (listen_socket, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof (flags)); if (rv == -1 && Util::should_log (LOG_DEFAULT)) { - log_info_nocheck_fmt (LOG_DEFAULT, "Could not set SO_REUSEADDR on the listening socket ({})", strerror (errno)); + log_info_nocheck_fmt (LOG_DEFAULT, "Could not set SO_REUSEADDR on the listening socket (%s)", strerror (errno)); // not a fatal failure } @@ -311,7 +311,7 @@ Debug::handle_server_connection (void) listen_addr.sin_addr.s_addr = INADDR_ANY; rv = bind (listen_socket, (struct sockaddr *) &listen_addr, sizeof (listen_addr)); if (rv == -1) { - log_info (LOG_DEFAULT, "Could not bind to address: {}", strerror (errno)); + log_info (LOG_DEFAULT, "Could not bind to address: %s", strerror (errno)); rv = 2; goto cleanup; } @@ -323,7 +323,7 @@ Debug::handle_server_connection (void) rv = listen (listen_socket, 1); if (rv == -1) { - log_info (LOG_DEFAULT, "Could not listen for XS: {}", strerror (errno)); + log_info (LOG_DEFAULT, "Could not listen for XS: %s", strerror (errno)); rv = 2; goto cleanup; } @@ -373,7 +373,7 @@ Debug::handle_server_connection (void) } while (rv == -1 && errno == EINTR); if (rv == -1) { - log_info (LOG_DEFAULT, "Failed while waiting for XS to connect: {}", strerror (errno)); + log_info (LOG_DEFAULT, "Failed while waiting for XS to connect: %s", strerror (errno)); rv = 2; goto cleanup; } @@ -381,18 +381,18 @@ Debug::handle_server_connection (void) socklen_t len = sizeof (struct sockaddr_in); int fd = accept (listen_socket, (struct sockaddr *) &listen_addr, &len); if (fd == -1) { - log_info (LOG_DEFAULT, "Failed to accept connection from XS: {}", strerror (errno)); + log_info (LOG_DEFAULT, "Failed to accept connection from XS: %s", strerror (errno)); rv = 3; goto cleanup; } flags = 1; if (setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flags, sizeof (flags)) < 0) { - log_info (LOG_DEFAULT, "Could not set TCP_NODELAY on socket ({})", strerror (errno)); + log_info (LOG_DEFAULT, "Could not set TCP_NODELAY on socket (%s)", strerror (errno)); // not a fatal failure } - log_info (LOG_DEFAULT, "Successfully received connection from XS on port {}, fd: {}", listen_port, fd); + log_info (LOG_DEFAULT, "Successfully received connection from XS on port %u, fd: %d", listen_port, fd); need_new_conn = process_connection (fd); } @@ -444,7 +444,7 @@ Debug::process_cmd (int fd, char *cmd) constexpr std::string_view PONG_REPLY { "pong" }; if (strcmp (cmd, PING_CMD.data ()) == 0) { if (!Util::send_uninterrupted (fd, const_cast (reinterpret_cast (PONG_REPLY.data ())), 5)) - log_error (LOG_DEFAULT, "Got keepalive request from XS, but could not send response back ({})", strerror (errno)); + log_error (LOG_DEFAULT, "Got keepalive request from XS, but could not send response back (%s)", strerror (errno)); return false; } @@ -490,7 +490,7 @@ Debug::process_cmd (int fd, char *cmd) profiler_fd = fd; profiler_description = Util::monodroid_strdup_printf ("%s,output=#%i", prof, profiler_fd); } else { - log_error (LOG_DEFAULT, "Unknown profiler: '{}'", optional_string (prof)); + log_error (LOG_DEFAULT, "Unknown profiler: '%s'", optional_string (prof)); } /* Notify the main thread (start_profiling ()) */ profiler_configured = true; @@ -499,7 +499,7 @@ Debug::process_cmd (int fd, char *cmd) pthread_mutex_unlock (&process_cmd_mutex); return use_fd; } else { - log_error (LOG_DEFAULT, "Unsupported command: '{}'", optional_string (cmd)); + log_error (LOG_DEFAULT, "Unsupported command: '%s'", optional_string (cmd)); } return false; @@ -529,7 +529,7 @@ Debug::start_debugging (void) // this text is used in unit tests to check the debugger started // do not change it without updating the test. - log_warn (LOG_DEBUGGER, "Trying to initialize the debugger with options: {}", optional_string (debug_arg)); + log_warn (LOG_DEBUGGER, "Trying to initialize the debugger with options: %s", optional_string (debug_arg)); if (enable_soft_breakpoints ()) { constexpr std::string_view soft_breakpoints { "--soft-breakpoints" }; @@ -556,7 +556,7 @@ Debug::start_profiling () if (!profiler_description) return; - log_info (LOG_DEFAULT, "Loading profiler: '{}'", profiler_description); + log_info (LOG_DEFAULT, "Loading profiler: '%s'", profiler_description); monodroid_profiler_load (AndroidSystem::get_runtime_libdir (), profiler_description, nullptr); } @@ -576,7 +576,7 @@ Debug::enable_soft_breakpoints (void) uname (&name); for (const char** ptr = soft_breakpoint_kernel_list; *ptr; ptr++) { if (strcmp (name.release, *ptr) == 0) { - log_info (LOG_DEBUGGER, "soft breakpoints enabled due to kernel version match ({})", name.release); + log_info (LOG_DEBUGGER, "soft breakpoints enabled due to kernel version match (%s)", name.release); return 1; } } @@ -584,17 +584,17 @@ Debug::enable_soft_breakpoints (void) char *value; /* Soft breakpoints are enabled by default */ if (AndroidSystem::monodroid_get_system_property (SharedConstants::DEBUG_MONO_SOFT_BREAKPOINTS, &value) <= 0) { - log_info (LOG_DEBUGGER, "soft breakpoints enabled by default ({} property not defined)", SharedConstants::DEBUG_MONO_SOFT_BREAKPOINTS.data ()); + log_info (LOG_DEBUGGER, "soft breakpoints enabled by default (%s property not defined)", SharedConstants::DEBUG_MONO_SOFT_BREAKPOINTS.data ()); return 1; } bool ret; if (strcmp ("0", value) == 0) { ret = false; - log_info (LOG_DEBUGGER, "soft breakpoints disabled ({} property set to {})", SharedConstants::DEBUG_MONO_SOFT_BREAKPOINTS.data (), optional_string (value)); + log_info (LOG_DEBUGGER, "soft breakpoints disabled (%s property set to %s)", SharedConstants::DEBUG_MONO_SOFT_BREAKPOINTS.data (), optional_string (value)); } else { ret = true; - log_info (LOG_DEBUGGER, "soft breakpoints enabled ({} property set to {})", SharedConstants::DEBUG_MONO_SOFT_BREAKPOINTS.data (), optional_string (value)); + log_info (LOG_DEBUGGER, "soft breakpoints enabled (%s property set to %s)", SharedConstants::DEBUG_MONO_SOFT_BREAKPOINTS.data (), optional_string (value)); } delete[] value; return ret; diff --git a/src/native/mono/monodroid/embedded-assemblies-zip.cc b/src/native/mono/monodroid/embedded-assemblies-zip.cc index 2453f599b08..d3618cc9911 100644 --- a/src/native/mono/monodroid/embedded-assemblies-zip.cc +++ b/src/native/mono/monodroid/embedded-assemblies-zip.cc @@ -22,7 +22,7 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector } auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (assembly_store_map, entry_name.get ()); - log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: {:p}; size: {}", payload_start, payload_size); + log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: %p; size: %zu", payload_start, payload_size); auto header = static_cast(payload_start); if (header->magic != ASSEMBLY_STORE_MAGIC) { @@ -267,7 +267,7 @@ EmbeddedAssemblies::zip_load_assembly_store_entries (std::vector const& dynamic_local_string entry_name; bool assembly_store_found = false; - log_debug (LOG_ASSEMBLY, "Looking for assembly stores in APK ('{}')", assembly_store_file_path.data ()); + log_debug (LOG_ASSEMBLY, "Looking for assembly stores in APK ('%s')", assembly_store_file_path.data ()); for (size_t i = 0uz; i < num_entries; i++) { if (all_required_zip_entries_found ()) { need_to_scan_more_apks = false; @@ -303,11 +303,11 @@ EmbeddedAssemblies::zip_load_assembly_store_entries (std::vector const& log_debug ( LOG_ASSEMBLY, - "Found a shared library entry {} (index: {}; name: {}; hash: {:x}; apk offset: {})", + "Found a shared library entry %s (index: %u; name: %s; hash: %zx; apk offset: %u)", optional_string (entry_name.get ()), number_of_zip_dso_entries, optional_string (name), - apk_entry->name_hash, + static_cast(apk_entry->name_hash), apk_entry->offset ); number_of_zip_dso_entries++; @@ -332,9 +332,9 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus ); } #ifdef DEBUG - log_info (LOG_ASSEMBLY, "Central directory offset: {}", cd_offset); - log_info (LOG_ASSEMBLY, "Central directory size: {}", cd_size); - log_info (LOG_ASSEMBLY, "Central directory entries: {}", cd_entries); + log_info (LOG_ASSEMBLY, "Central directory offset: %u", cd_offset); + log_info (LOG_ASSEMBLY, "Central directory size: %u", cd_size); + log_info (LOG_ASSEMBLY, "Central directory entries: %u", cd_entries); #endif off_t retval = ::lseek (fd, static_cast(cd_offset), SEEK_SET); if (retval < 0) { @@ -412,7 +412,7 @@ EmbeddedAssemblies::set_entry_data (XamarinAndroidBundledAssembly &entry, ZipEnt log_debug ( LOG_ASSEMBLY, - "Set bundled assembly entry data. file name: '{}'; entry name: '{}'; data size: {}", + "Set bundled assembly entry data. file name: '%s'; entry name: '%s'; data size: %u", optional_string (entry.file_name), optional_string (entry.name), entry.data_size @@ -437,14 +437,14 @@ EmbeddedAssemblies::zip_read_cd_info (int fd, uint32_t& cd_offset, uint32_t& cd_ // The simplest case - no file comment off_t ret = ::lseek (fd, -ZIP_EOCD_LEN, SEEK_END); if (ret < 0) { - log_error (LOG_ASSEMBLY, "Unable to seek into the APK to find ECOD: {} (ret: {}; errno: {})", std::strerror (errno), ret, errno); + log_error (LOG_ASSEMBLY, "Unable to seek into the APK to find ECOD: %s (ret: %lld; errno: %d)", std::strerror (errno), static_cast(ret), errno); return false; } std::array eocd; ssize_t nread = ::read (fd, eocd.data (), eocd.size ()); if (nread < 0 || nread != eocd.size ()) { - log_error (LOG_ASSEMBLY, "Failed to read EOCD from the APK: {} (nread: {}; errno: {})", std::strerror (errno), nread, errno); + log_error (LOG_ASSEMBLY, "Failed to read EOCD from the APK: %s (nread: %zd; errno: %d)", std::strerror (errno), nread, errno); return false; } @@ -464,7 +464,7 @@ EmbeddedAssemblies::zip_read_cd_info (int fd, uint32_t& cd_offset, uint32_t& cd_ constexpr size_t alloc_size = 65535uz + ZIP_EOCD_LEN; // 64k is the biggest comment size allowed ret = ::lseek (fd, static_cast(-alloc_size), SEEK_END); if (ret < 0) { - log_error (LOG_ASSEMBLY, "Unable to seek into the file to find ECOD before APK comment: {} (ret: {}; errno: {})", std::strerror (errno), ret, errno); + log_error (LOG_ASSEMBLY, "Unable to seek into the file to find ECOD before APK comment: %s (ret: %lld; errno: %d)", std::strerror (errno), static_cast(ret), errno); return false; } @@ -473,7 +473,7 @@ EmbeddedAssemblies::zip_read_cd_info (int fd, uint32_t& cd_offset, uint32_t& cd_ nread = ::read (fd, buf.data (), buf.size ()); if (nread < 0 || static_cast(nread) != alloc_size) { - log_error (LOG_ASSEMBLY, "Failed to read EOCD and comment from the APK: {} (nread: {}; errno: {})", std::strerror (errno), nread, errno); + log_error (LOG_ASSEMBLY, "Failed to read EOCD and comment from the APK: %s (nread: %zd; errno: %d)", std::strerror (errno), nread, errno); return false; } @@ -507,8 +507,8 @@ EmbeddedAssemblies::zip_adjust_data_offset (int fd, ZipEntryLoadState &state) no if (result < 0) { log_error ( LOG_ASSEMBLY, - "Failed to seek to archive entry local header at offset {}. {} (result: {}; errno: {})", - state.local_header_offset, std::strerror (errno), result, errno + "Failed to seek to archive entry local header at offset %u. %s (result: %lld; errno: %d)", + state.local_header_offset, std::strerror (errno), static_cast(result), errno ); return false; } @@ -518,32 +518,32 @@ EmbeddedAssemblies::zip_adjust_data_offset (int fd, ZipEntryLoadState &state) no ssize_t nread = ::read (fd, local_header.data (), local_header.size ()); if (nread < 0 || nread != ZIP_LOCAL_LEN) { - log_error (LOG_ASSEMBLY, "Failed to read local header at offset {}: {} (nread: {}; errno: {})", state.local_header_offset, std::strerror (errno), nread, errno); + log_error (LOG_ASSEMBLY, "Failed to read local header at offset %u: %s (nread: %zd; errno: %d)", state.local_header_offset, std::strerror (errno), nread, errno); return false; } size_t index = 0; if (!zip_read_field (local_header, index, signature)) { - log_error (LOG_ASSEMBLY, "Failed to read Local Header entry signature at offset {}", state.local_header_offset); + log_error (LOG_ASSEMBLY, "Failed to read Local Header entry signature at offset %u", state.local_header_offset); return false; } if (memcmp (signature.data (), ZIP_LOCAL_MAGIC.data (), signature.size ()) != 0) { - log_error (LOG_ASSEMBLY, "Invalid Local Header entry signature at offset {}", state.local_header_offset); + log_error (LOG_ASSEMBLY, "Invalid Local Header entry signature at offset %u", state.local_header_offset); return false; } uint16_t file_name_length; index = LH_FILE_NAME_LENGTH_OFFSET; if (!zip_read_field (local_header, index, file_name_length)) { - log_error (LOG_ASSEMBLY, "Failed to read Local Header 'file name length' field at offset {}", (state.local_header_offset + index)); + log_error (LOG_ASSEMBLY, "Failed to read Local Header 'file name length' field at offset %zu", (state.local_header_offset + index)); return false; } uint16_t extra_field_length; index = LH_EXTRA_LENGTH_OFFSET; if (!zip_read_field (local_header, index, extra_field_length)) { - log_error (LOG_ASSEMBLY, "Failed to read Local Header 'extra field length' field at offset {}", (state.local_header_offset + index)); + log_error (LOG_ASSEMBLY, "Failed to read Local Header 'extra field length' field at offset %zu", (state.local_header_offset + index)); return false; } @@ -585,7 +585,7 @@ template EmbeddedAssemblies::zip_ensure_valid_params (T const& buf, size_t index, size_t to_read) noexcept { if (index + to_read > buf.size ()) { - log_error (LOG_ASSEMBLY, "Buffer too short to read {} bytes of data", to_read); + log_error (LOG_ASSEMBLY, "Buffer too short to read %zu bytes of data", to_read); return false; } diff --git a/src/native/mono/monodroid/embedded-assemblies.cc b/src/native/mono/monodroid/embedded-assemblies.cc index c3aa3488aa8..dfb4899136b 100644 --- a/src/native/mono/monodroid/embedded-assemblies.cc +++ b/src/native/mono/monodroid/embedded-assemblies.cc @@ -150,7 +150,7 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb ) ); } else { - log_debug (LOG_ASSEMBLY, "Compressed assembly '{}' is smaller than when the application was built. Adjusting accordingly.", optional_string (name)); + log_debug (LOG_ASSEMBLY, "Compressed assembly '%s' is smaller than when the application was built. Adjusting accordingly.", optional_string (name)); } cad.uncompressed_file_size = header->uncompressed_length; } @@ -248,7 +248,7 @@ EmbeddedAssemblies::map_runtime_file (XamarinAndroidBundledAssembly& file) noexc ); if (already_mapped) { - log_debug (LOG_ASSEMBLY, "Assembly {} already mmapped by another thread, unmapping our copy", optional_string (file.name)); + log_debug (LOG_ASSEMBLY, "Assembly %s already mmapped by another thread, unmapping our copy", optional_string (file.name)); munmap (map_info.area, file.data_size); map_info.area = nullptr; } @@ -265,7 +265,7 @@ EmbeddedAssemblies::map_runtime_file (XamarinAndroidBundledAssembly& file) noexc log_info_nocheck_fmt ( LOG_ASSEMBLY, - "file-offset: {:<8x} start: {:<8p} end: {:<8p} len: {:<12} zip-entry: {} name: {} [{}]", + "file-offset: %-8x start: %-8p end: %-8p len: %-12u zip-entry: %s name: %s [%s]", file.data_offset, static_cast(file.data), pointer_add (file.data, file.data_size), @@ -307,7 +307,7 @@ EmbeddedAssemblies::load_bundled_assembly ( if (strcmp (assembly.name, abi_name.get ()) != 0) { return nullptr; } else { - log_debug (LOG_ASSEMBLY, "open_from_bundles: found architecture-specific: '{}'", optional_string (abi_name.get ())); + log_debug (LOG_ASSEMBLY, "open_from_bundles: found architecture-specific: '%s'", optional_string (abi_name.get ())); } } @@ -341,7 +341,7 @@ EmbeddedAssemblies::load_bundled_assembly ( if (debug_file.data != nullptr) { if (debug_file.data_size > std::numeric_limits::max ()) { - log_warn (LOG_ASSEMBLY, "Debug info file '{}' is too big for Mono to consume", optional_string (debug_file.name)); + log_warn (LOG_ASSEMBLY, "Debug info file '%s' is too big for Mono to consume", optional_string (debug_file.name)); } else { mono_debug_open_image_from_memory (image, reinterpret_cast(debug_file.data), static_cast(debug_file.data_size)); } @@ -353,7 +353,7 @@ EmbeddedAssemblies::load_bundled_assembly ( MonoImageOpenStatus status; MonoAssembly *a = mono_assembly_load_from_full (image, name.get (), &status, ref_only); if (a == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { - log_warn (LOG_ASSEMBLY, "Failed to load managed assembly '{}'. {}", optional_string (name.get ()), optional_string (mono_image_strerror (status))); + log_warn (LOG_ASSEMBLY, "Failed to load managed assembly '%s'. %s", optional_string (name.get ()), optional_string (mono_image_strerror (status))); return nullptr; } @@ -368,7 +368,7 @@ EmbeddedAssemblies::individual_assemblies_open_from_bundles (dynamic_local_strin name.append (SharedConstants::DLL_EXTENSION); } - log_debug (LOG_ASSEMBLY, "individual_assemblies_open_from_bundles: looking for bundled name: '{}'", optional_string (name.get ())); + log_debug (LOG_ASSEMBLY, "individual_assemblies_open_from_bundles: looking for bundled name: '%s'", optional_string (name.get ())); dynamic_local_string abi_name; abi_name @@ -415,16 +415,16 @@ template EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string& name, TLoaderData loader_data, bool ref_only) noexcept { hash_t name_hash = xxhash::hash (name.get (), name.length ()); - log_debug (LOG_ASSEMBLY, "assembly_store_open_from_bundles: looking for bundled name: '{}' (hash {:x})", optional_string (name.get ()), name_hash); + log_debug (LOG_ASSEMBLY, "assembly_store_open_from_bundles: looking for bundled name: '%s' (hash %zx)", optional_string (name.get ()), static_cast(name_hash)); const AssemblyStoreIndexEntry *hash_entry = find_assembly_store_entry (name_hash, assembly_store_hashes, assembly_store.index_entry_count); if (hash_entry == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Assembly '{}' (hash {:x}) not found", optional_string (name.get ()), name_hash); + log_warn (LOG_ASSEMBLY, "Assembly '%s' (hash %zx) not found", optional_string (name.get ()), static_cast(name_hash)); return nullptr; } if (hash_entry->ignore != 0) { - log_debug (LOG_ASSEMBLY, "Assembly '{}' ignored"sv, optional_string (name.get ())); + log_debug (LOG_ASSEMBLY, "Assembly '%s' ignored", optional_string (name.get ())); return nullptr; } @@ -453,7 +453,7 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string(assembly_runtime_info.image_data), static_cast(assembly_runtime_info.debug_info_data), static_cast(assembly_runtime_info.config_data), @@ -471,7 +471,7 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string 0) { @@ -645,26 +645,26 @@ EmbeddedAssemblies::typemap_java_to_managed ([[maybe_unused]] hash_t hash, const entry = binary_search (java_type_name.get (), type_map.java_to_managed, type_map.entry_count); if (entry == nullptr) [[unlikely]] { - log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '{}'", java_type_name.get ()); + log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s'", java_type_name.get ()); return nullptr; } const char *managed_type_name = entry->to; if (managed_type_name == nullptr) { - log_debug (LOG_ASSEMBLY, "typemap: Java type '{}' maps either to an open generic type or an interface type.", java_type_name.get ()); + log_debug (LOG_ASSEMBLY, "typemap: Java type '%s' maps either to an open generic type or an interface type.", java_type_name.get ()); return nullptr; } - log_debug (LOG_DEFAULT, "typemap: Java type '{}' corresponds to managed type '{}'", java_type_name.get (), optional_string (managed_type_name)); + log_debug (LOG_DEFAULT, "typemap: Java type '%s' corresponds to managed type '%s'", java_type_name.get (), optional_string (managed_type_name)); MonoType *type = mono_reflection_type_from_name (const_cast(managed_type_name), nullptr); if (type == nullptr) [[unlikely]] { - log_info (LOG_ASSEMBLY, "typemap: managed type '{}' (mapped from Java type '{}') could not be loaded", optional_string (managed_type_name), java_type_name.get ()); + log_info (LOG_ASSEMBLY, "typemap: managed type '%s' (mapped from Java type '%s') could not be loaded", optional_string (managed_type_name), java_type_name.get ()); return nullptr; } MonoReflectionType *ret = mono_type_get_object (Util::get_current_domain (), type); if (ret == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "typemap: unable to instantiate managed type '{}'", optional_string (managed_type_name)); + log_warn (LOG_ASSEMBLY, "typemap: unable to instantiate managed type '%s'", optional_string (managed_type_name)); return nullptr; } @@ -684,16 +684,16 @@ EmbeddedAssemblies::typemap_java_to_managed (hash_t hash, const MonoString *java TypeMapModule *module = java_entry != nullptr && java_entry->module_index < map_module_count ? &map_modules[java_entry->module_index] : nullptr; if (module == nullptr) { if (java_entry == nullptr) { - log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '{}' (hash {:x})", to_utf8 (java_type_name).get (), hash); + log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a managed type from Java type '%s' (hash %zx)", to_utf8 (java_type_name).get (), static_cast(hash)); } else { - log_warn (LOG_ASSEMBLY, "typemap: mapping from Java type '{}' to managed type has invalid module index {}", to_utf8 (java_type_name).get (), java_entry->module_index); + log_warn (LOG_ASSEMBLY, "typemap: mapping from Java type '%s' to managed type has invalid module index %u", to_utf8 (java_type_name).get (), java_entry->module_index); } return nullptr; } const TypeMapModuleEntry *entry = binary_search (java_entry->type_token_id, module->map, module->entry_count); if (entry == nullptr) { - log_info (LOG_ASSEMBLY, "typemap: unable to find mapping from Java type '{}' to managed type with token ID {} in module [{}]", to_utf8 (java_type_name).get (), java_entry->type_token_id, MonoGuidString (module->module_uuid).get ()); + log_info (LOG_ASSEMBLY, "typemap: unable to find mapping from Java type '%s' to managed type with token ID %u in module [%s]", to_utf8 (java_type_name).get (), java_entry->type_token_id, MonoGuidString (module->module_uuid).get ()); return nullptr; } @@ -701,14 +701,14 @@ EmbeddedAssemblies::typemap_java_to_managed (hash_t hash, const MonoString *java module->image = mono_image_loaded (module->assembly_name); if (module->image == nullptr) { - log_debug (LOG_ASSEMBLY, "typemap: assembly '{}' hasn't been loaded yet, attempting a full load", optional_string (module->assembly_name)); + log_debug (LOG_ASSEMBLY, "typemap: assembly '%s' hasn't been loaded yet, attempting a full load", optional_string (module->assembly_name)); // Fake a request from MonoVM to load the assembly. MonoAssemblyName *assembly_name = mono_assembly_name_new (module->assembly_name); MonoAssembly *assm; if (assembly_name == nullptr) { - log_error (LOG_ASSEMBLY, "typemap: failed to create Mono assembly name for '{}'", optional_string (module->assembly_name)); + log_error (LOG_ASSEMBLY, "typemap: failed to create Mono assembly name for '%s'", optional_string (module->assembly_name)); assm = nullptr; } else { MonoAssemblyLoadContextGCHandle alc_gchandle = mono_alc_get_default_gchandle (); @@ -717,24 +717,24 @@ EmbeddedAssemblies::typemap_java_to_managed (hash_t hash, const MonoString *java } if (assm == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: failed to load managed assembly '{}'", optional_string (module->assembly_name)); + log_warn (LOG_ASSEMBLY, "typemap: failed to load managed assembly '%s'", optional_string (module->assembly_name)); } else { module->image = mono_assembly_get_image (assm); } } if (module->image == nullptr) { - log_error (LOG_ASSEMBLY, "typemap: unable to load assembly '{}' when looking up managed type corresponding to Java type '{}'", optional_string (module->assembly_name), to_utf8 (java_type_name).get ()); + log_error (LOG_ASSEMBLY, "typemap: unable to load assembly '%s' when looking up managed type corresponding to Java type '%s'", optional_string (module->assembly_name), to_utf8 (java_type_name).get ()); return nullptr; } } - log_debug (LOG_ASSEMBLY, "typemap: java type '{}' corresponds to managed token id {} ({:x})", to_utf8 (java_type_name).get (), java_entry->type_token_id, java_entry->type_token_id); + log_debug (LOG_ASSEMBLY, "typemap: java type '%s' corresponds to managed token id %u (%x)", to_utf8 (java_type_name).get (), java_entry->type_token_id, java_entry->type_token_id); MonoClass *klass = mono_class_get (module->image, java_entry->type_token_id); if (klass == nullptr) [[unlikely]] { log_error ( LOG_ASSEMBLY, - "typemap: unable to find managed type with token ID {} in assembly '{}', corresponding to Java type '{}'", + "typemap: unable to find managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", java_entry->type_token_id, optional_string (module->assembly_name), to_utf8 (java_type_name).get () @@ -750,7 +750,7 @@ EmbeddedAssemblies::typemap_java_to_managed (hash_t hash, const MonoString *java MonoReflectionType *ret = mono_type_get_object (domain, mono_class_get_type (klass)); if (ret == nullptr) { log_warn (LOG_ASSEMBLY, - "typemap: unable to instantiate managed type with token ID {} in assembly '{}', corresponding to Java type '{}'", + "typemap: unable to instantiate managed type with token ID %u in assembly '%s', corresponding to Java type '%s'", java_entry->type_token_id, optional_string (module->assembly_name), to_utf8 (java_type_name).get () @@ -818,7 +818,7 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo const TypeMapEntry *entry = typemap_managed_to_java (full_name.get ()); if (entry == nullptr) [[unlikely]] { - log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a Java type from managed type '{}'", optional_string (full_name.get ())); + log_info (LOG_ASSEMBLY, "typemap: unable to find mapping to a Java type from managed type '%s'", optional_string (full_name.get ())); return nullptr; } @@ -839,28 +839,28 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo if (mvid == nullptr) { log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"sv); } else { - log_info (LOG_ASSEMBLY, "typemap: module matching MVID [{}] not found.", MonoGuidString (mvid).get ()); + log_info (LOG_ASSEMBLY, "typemap: module matching MVID [%s] not found.", MonoGuidString (mvid).get ()); } return nullptr; } uint32_t token = mono_class_get_type_token (klass); - log_debug (LOG_ASSEMBLY, "typemap: MVID [{}] maps to assembly {}, looking for token {} ({:x}), table index {}", MonoGuidString (mvid).get (), match->assembly_name, token, token, token & 0x00FFFFFF); + log_debug (LOG_ASSEMBLY, "typemap: MVID [%s] maps to assembly %s, looking for token %u (%x), table index %u", MonoGuidString (mvid).get (), optional_string (match->assembly_name), token, token, token & 0x00FFFFFF); // Each map entry is a pair of 32-bit integers: [TypeTokenID][JavaMapArrayIndex] const TypeMapModuleEntry *entry = match->map != nullptr ? binary_search (token, match->map, match->entry_count) : nullptr; if (entry == nullptr) { if (match->map == nullptr) { - log_warn (LOG_ASSEMBLY, "typemap: module with MVID [{}] has no associated type map.", MonoGuidString (mvid).get ()); + log_warn (LOG_ASSEMBLY, "typemap: module with MVID [%s] has no associated type map.", MonoGuidString (mvid).get ()); return nullptr; } if (match->duplicate_count > 0 && match->duplicate_map != nullptr) { - log_debug (LOG_ASSEMBLY, "typemap: searching module [{}] duplicate map for token {} ({:x})", MonoGuidString (mvid).get (), token, token); + log_debug (LOG_ASSEMBLY, "typemap: searching module [%s] duplicate map for token %u (%x)", MonoGuidString (mvid).get (), token, token); entry = binary_search (token, match->duplicate_map, match->duplicate_count); } if (entry == nullptr) { - log_info (LOG_ASSEMBLY, "typemap: type with token {} ({:x}) in module [{}] ({}) not found.", token, token, MonoGuidString (mvid).get (), match->assembly_name); + log_info (LOG_ASSEMBLY, "typemap: type with token %u (%x) in module [%s] (%s) not found.", token, token, MonoGuidString (mvid).get (), optional_string (match->assembly_name)); return nullptr; } } @@ -868,7 +868,7 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo if (entry->java_map_index >= java_type_count) [[unlikely]] { log_warn ( LOG_ASSEMBLY, - "typemap: type with token {} ({:x}) in module [{}] ({}) has invalid Java type index {}", + "typemap: type with token %u (%x) in module [%s] (%s) has invalid Java type index %u", token, token, MonoGuidString (mvid).get (), @@ -882,7 +882,7 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo if (java_entry.java_name_index >= java_type_count) [[unlikely]] { log_warn ( LOG_ASSEMBLY, - "typemap: type with token {} ({:x}) in module [{}] ({}) points to invalid Java type at index {} (invalid type name index {})", + "typemap: type with token %u (%x) in module [%s] (%s) points to invalid Java type at index %u (invalid type name index %u)", token, token, MonoGuidString (mvid).get (), @@ -895,12 +895,12 @@ EmbeddedAssemblies::typemap_managed_to_java ([[maybe_unused]] MonoType *type, Mo const char *ret = java_type_names[java_entry.java_name_index]; if (ret == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index {}", entry->java_map_index); + log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index %u", entry->java_map_index); } log_debug ( LOG_ASSEMBLY, - "typemap: type with token {} ({:x}) in module [{}] ({}) corresponds to Java type '{}'", + "typemap: type with token %u (%x) in module [%s] (%s) corresponds to Java type '%s'", token, token, MonoGuidString (mvid).get (), @@ -966,7 +966,7 @@ EmbeddedAssemblies::md_mmap_apk_file (int fd, uint32_t offset, size_t size, cons log_info ( LOG_ASSEMBLY, - " mmap_start: {:<8p}; mmap_end: {:<8p} mmap_len: {:<12} file_start: {:<8p} file_end: {:<8p} file_len: {:<12} apk descriptor: {} file: {}", + " mmap_start: %-8p; mmap_end: %-8p mmap_len: %-12zu file_start: %-8p file_end: %-8p file_len: %-12zu apk descriptor: %d file: %s", mmap_info.area, pointer_add (mmap_info.area, mmap_info.size), mmap_info.size, @@ -994,7 +994,7 @@ EmbeddedAssemblies::gather_bundled_assemblies_from_apk (const char* apk, monodro ) ); } - log_debug (LOG_ASSEMBLY, "APK {} FD: {}", optional_string (apk), fd); + log_debug (LOG_ASSEMBLY, "APK %s FD: %d", optional_string (apk), fd); zip_load_entries (fd, apk, should_register); } @@ -1023,7 +1023,7 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char if (res < 0) { log_error ( LOG_ASSEMBLY, - "typemap: failed to stat {} file '{}/{}': {}", + "typemap: failed to stat %s file '%s/%s': %s", optional_string (file_type), optional_string (dir_path), optional_string (file_path), @@ -1036,7 +1036,7 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char if (file_size < sizeof (header)) { log_error ( LOG_ASSEMBLY, - "typemap: {} file '{}/{}' is too small (must be at least {} bytes)", + "typemap: %s file '%s/%s' is too small (must be at least %zu bytes)", optional_string (file_type), optional_string (dir_path), optional_string (file_path), @@ -1049,7 +1049,7 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char if (fd < 0) { log_error ( LOG_ASSEMBLY, - "typemap: failed to open {} file {}/{} for reading: {}", + "typemap: failed to open %s file %s/%s for reading: %s", optional_string (file_type), optional_string (dir_path), optional_string (file_path), @@ -1063,7 +1063,7 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char if (nread < 0) { log_error ( LOG_ASSEMBLY, - "typemap: failed to read {} file header from '{}/{}': {}", + "typemap: failed to read %s file header from '%s/%s': %s", optional_string (file_type), optional_string (dir_path), optional_string (file_path), @@ -1072,7 +1072,7 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char } else { log_error ( LOG_ASSEMBLY, - "typemap: end of file while reading {} file header from '{}/{}'", + "typemap: end of file while reading %s file header from '%s/%s'", optional_string (file_type), optional_string (dir_path), optional_string (file_path) @@ -1085,7 +1085,7 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char if (header.magic != expected_magic) { log_error ( LOG_ASSEMBLY, - "typemap: invalid magic value in the {} file header from '{}/{}': expected {:x}, got {:x}", + "typemap: invalid magic value in the %s file header from '%s/%s': expected %x, got %x", optional_string (file_type), optional_string (dir_path), optional_string (file_path), @@ -1098,7 +1098,7 @@ EmbeddedAssemblies::typemap_read_header ([[maybe_unused]] int dir_fd, const char if (header.version != MODULE_FORMAT_VERSION) { log_error ( LOG_ASSEMBLY, - "typemap: incompatible {} format version. This build supports only version {}, file '{}/{}' uses version {}", + "typemap: incompatible %s format version. This build supports only version %u, file '%s/%s' uses version %u", optional_string (file_type), MODULE_FORMAT_VERSION, optional_string (dir_path), @@ -1117,14 +1117,14 @@ EmbeddedAssemblies::typemap_load_index (TypeMapIndexHeader &header, size_t file_ size_t entry_size = header.module_file_name_width; size_t data_size = entry_size * type_map_count; if (sizeof(header) + data_size > file_size) { - log_error (LOG_ASSEMBLY, "typemap: index file is too small, expected {}, found {} bytes", data_size + sizeof(header), file_size); + log_error (LOG_ASSEMBLY, "typemap: index file is too small, expected %zu, found %zu bytes", data_size + sizeof(header), file_size); return nullptr; } auto data = std::make_unique (data_size); ssize_t nread = do_read (index_fd, data.get (), data_size); if (nread != static_cast(data_size)) { - log_error (LOG_ASSEMBLY, "typemap: failed to read {} bytes from index file. {}", data_size, strerror (errno)); + log_error (LOG_ASSEMBLY, "typemap: failed to read %zu bytes from index file. %s", data_size, strerror (errno)); return nullptr; } @@ -1140,7 +1140,7 @@ EmbeddedAssemblies::typemap_load_index (TypeMapIndexHeader &header, size_t file_ std::unique_ptr EmbeddedAssemblies::typemap_load_index (int dir_fd, const char *dir_path, const char *index_path) { - log_debug (LOG_ASSEMBLY, "typemap: loading TypeMap index file '{}/{}'", optional_string (dir_path), optional_string (index_path)); + log_debug (LOG_ASSEMBLY, "typemap: loading TypeMap index file '%s/%s'", optional_string (dir_path), optional_string (index_path)); TypeMapIndexHeader header; size_t file_size; @@ -1169,7 +1169,7 @@ EmbeddedAssemblies::typemap_load_file (BinaryTypeMapHeader &header, const char * if (nread != static_cast(header.assembly_name_length)) { log_error ( LOG_ASSEMBLY, - "typemap: failed to read map assembly name from '{}/{}': {}", + "typemap: failed to read map assembly name from '%s/%s': %s", optional_string (dir_path), optional_string (file_path), strerror (errno) @@ -1182,7 +1182,7 @@ EmbeddedAssemblies::typemap_load_file (BinaryTypeMapHeader &header, const char * log_debug ( LOG_ASSEMBLY, - "typemap: '{}/{}':: entry count == {}; Java name field width == {}; Managed name width == {}; assembly name length == {}; assembly name == {}", + "typemap: '%s/%s':: entry count == %u; Java name field width == %u; Managed name width == %u; assembly name length == %u; assembly name == %s", optional_string (dir_path), optional_string (file_path), header.entry_count, @@ -1203,7 +1203,7 @@ EmbeddedAssemblies::typemap_load_file (BinaryTypeMapHeader &header, const char * module.data = new uint8_t [data_size]; nread = do_read (file_fd, module.data, data_size); if (nread != static_cast(data_size)) { - log_error (LOG_ASSEMBLY, "typemap: failed to read map data from '{}/{}': {}", optional_string (dir_path), optional_string (file_path), strerror (errno)); + log_error (LOG_ASSEMBLY, "typemap: failed to read map data from '%s/%s': %s", optional_string (dir_path), optional_string (file_path), strerror (errno)); return false; } @@ -1247,7 +1247,7 @@ EmbeddedAssemblies::typemap_load_file (BinaryTypeMapHeader &header, const char * bool EmbeddedAssemblies::typemap_load_file (int dir_fd, const char *dir_path, const char *file_path, TypeMap &module) { - log_debug (LOG_ASSEMBLY, "typemap: loading TypeMap file '{}/{}'", optional_string (dir_path), optional_string (file_path)); + log_debug (LOG_ASSEMBLY, "typemap: loading TypeMap file '%s/%s'", optional_string (dir_path), optional_string (file_path)); bool ret = true; BinaryTypeMapHeader header; @@ -1286,7 +1286,7 @@ EmbeddedAssemblies::register_from_apk (const char *apk_file, monodroid_should_re gather_bundled_assemblies_from_apk (apk_file, should_register); - log_info (LOG_ASSEMBLY, "Package '{}' contains {} assemblies", optional_string (apk_file), number_of_found_assemblies - prev); + log_info (LOG_ASSEMBLY, "Package '%s' contains %zu assemblies", optional_string (apk_file), number_of_found_assemblies - prev); return number_of_found_assemblies; } @@ -1390,10 +1390,10 @@ EmbeddedAssemblies::maybe_register_blob_from_filesystem ( [[gnu::always_inline]] size_t EmbeddedAssemblies::register_from_filesystem (const char *lib_dir_path,bool look_for_mangled_names, monodroid_should_register should_register) noexcept { - log_debug (LOG_ASSEMBLY, "Looking for assemblies in '{}'", optional_string (lib_dir_path)); + log_debug (LOG_ASSEMBLY, "Looking for assemblies in '%s'", optional_string (lib_dir_path)); DIR *lib_dir = opendir (lib_dir_path); // TODO: put it in a scope guard at some point if (lib_dir == nullptr) { - log_warn (LOG_ASSEMBLY, "Unable to open app library directory '{}': {}", optional_string (lib_dir_path), std::strerror (errno)); + log_warn (LOG_ASSEMBLY, "Unable to open app library directory '%s': %s", optional_string (lib_dir_path), std::strerror (errno)); return 0; } @@ -1402,14 +1402,14 @@ EmbeddedAssemblies::register_from_filesystem (const char *lib_dir_path,bool look int dir_fd = dirfd (lib_dir); if (dir_fd < 0) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Unable to obtain file descriptor for directory '{}': {}", optional_string (lib_dir_path), std::strerror (errno)); + log_warn (LOG_ASSEMBLY, "Unable to obtain file descriptor for directory '%s': %s", optional_string (lib_dir_path), std::strerror (errno)); closedir (lib_dir); return 0; } state.file_fd = dup (dir_fd); if (state.file_fd < 0) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Unable to duplicate file descriptor {} for directory '{}': {}", dir_fd, optional_string (lib_dir_path), std::strerror (errno)); + log_warn (LOG_ASSEMBLY, "Unable to duplicate file descriptor %d for directory '%s': %s", dir_fd, optional_string (lib_dir_path), std::strerror (errno)); closedir (lib_dir); return 0; } @@ -1426,7 +1426,7 @@ EmbeddedAssemblies::register_from_filesystem (const char *lib_dir_path,bool look dirent *cur = readdir (lib_dir); if (cur == nullptr) { if (errno != 0) { - log_warn (LOG_ASSEMBLY, "Failed to open a directory entry from '{}': {}", optional_string (lib_dir_path), std::strerror (errno)); + log_warn (LOG_ASSEMBLY, "Failed to open a directory entry from '%s': %s", optional_string (lib_dir_path), std::strerror (errno)); continue; // keep going, no harm } break; // No more entries, we're done @@ -1446,7 +1446,7 @@ EmbeddedAssemblies::register_from_filesystem (const char *lib_dir_path,bool look // ...and we can handle the runtime config entry if (!runtime_config_blob_found && std::strncmp (cur->d_name, SharedConstants::RUNTIME_CONFIG_BLOB_NAME.data (), SharedConstants::RUNTIME_CONFIG_BLOB_NAME.size ()) == 0) { - log_debug (LOG_ASSEMBLY, "Mapping runtime config blob from '{}'", optional_string (cur->d_name)); + log_debug (LOG_ASSEMBLY, "Mapping runtime config blob from '%s'", optional_string (cur->d_name)); auto file_size = Util::get_file_size_at (state.file_fd, cur->d_name); if (!file_size) { continue; @@ -1493,6 +1493,6 @@ EmbeddedAssemblies::register_from_filesystem (monodroid_should_register should_r ); #endif - log_debug (LOG_ASSEMBLY, "Found {} assemblies on the filesystem", assembly_count); + log_debug (LOG_ASSEMBLY, "Found %zu assemblies on the filesystem", assembly_count); return assembly_count; } diff --git a/src/native/mono/monodroid/embedded-assemblies.hh b/src/native/mono/monodroid/embedded-assemblies.hh index 5c48f92b400..7bfb28a5d67 100644 --- a/src/native/mono/monodroid/embedded-assemblies.hh +++ b/src/native/mono/monodroid/embedded-assemblies.hh @@ -304,7 +304,7 @@ namespace xamarin::android::internal { elf_header->e_ident[EI_MAG1] != ELFMAG1 || elf_header->e_ident[EI_MAG2] != ELFMAG2 || elf_header->e_ident[EI_MAG3] != ELFMAG3) { - log_debug (LOG_ASSEMBLY, "Not an ELF image: {}", optional_string (file_name)); + log_debug (LOG_ASSEMBLY, "Not an ELF image: %s", optional_string (file_name)); // Not an ELF image, just return what we mmapped before return { map_info.area, map_info.size }; } @@ -323,7 +323,7 @@ namespace xamarin::android::internal { static void store_mapped_runtime_config_data (md_mmap_info const& map_info, const char *file_name) noexcept { auto [payload_start, payload_size] = get_wrapper_dso_payload_pointer_and_size (map_info, file_name); - log_debug (LOG_ASSEMBLY, "Runtime config: payload pointer {:p} ; size {}", payload_start, payload_size); + log_debug (LOG_ASSEMBLY, "Runtime config: payload pointer %p ; size %zu", payload_start, payload_size); runtime_config_data = payload_start; runtime_config_data_size = payload_size; runtime_config_blob_found = true; @@ -430,7 +430,7 @@ namespace xamarin::android::internal { } } } - log_debug (LOG_ASSEMBLY, "Unmangled name to '{}'", optional_string (name.get ())); + log_debug (LOG_ASSEMBLY, "Unmangled name to '%s'", optional_string (name.get ())); }; private: diff --git a/src/native/mono/monodroid/mono-image-loader.hh b/src/native/mono/monodroid/mono-image-loader.hh index ac33fe53588..e997d1196dc 100644 --- a/src/native/mono/monodroid/mono-image-loader.hh +++ b/src/native/mono/monodroid/mono-image-loader.hh @@ -110,7 +110,7 @@ namespace xamarin::android::internal { force_inline static MonoImage* stash_and_return (MonoImage *image, MonoImageOpenStatus status, [[maybe_unused]] hash_t hash) noexcept { if (image == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { - log_warn (LOG_ASSEMBLY, "Failed to open assembly image. {}", optional_string (mono_image_strerror (status))); + log_warn (LOG_ASSEMBLY, "Failed to open assembly image. %s", optional_string (mono_image_strerror (status))); return nullptr; } diff --git a/src/native/mono/monodroid/monodroid-glue-internal.hh b/src/native/mono/monodroid/monodroid-glue-internal.hh index e1d1f446d07..8272a3c73ab 100644 --- a/src/native/mono/monodroid/monodroid-glue-internal.hh +++ b/src/native/mono/monodroid/monodroid-glue-internal.hh @@ -127,7 +127,7 @@ namespace xamarin::android::internal void *symptr = MonodroidDl::monodroid_dlsym (handle, name, &err, nullptr); if (symptr == nullptr) { - log_warn (LOG_DEFAULT, "Failed to load symbol '{}' library with handle {}. {}", name, handle, err == nullptr ? "Unknown error"sv : err); + log_warn (LOG_DEFAULT, "Failed to load symbol '%s' library with handle %p. %s", optional_string (name), handle, err == nullptr ? "Unknown error" : err); fnptr = nullptr; return; } diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc index 99c81025e37..f49551b84c0 100644 --- a/src/native/mono/monodroid/monodroid-glue.cc +++ b/src/native/mono/monodroid/monodroid-glue.cc @@ -186,12 +186,12 @@ MonodroidRuntime::open_from_update_dir (MonoAssemblyName *aname, [[maybe_unused] fullpath.append (SharedConstants::DLL_EXTENSION); } - log_debug (LOG_ASSEMBLY, "open_from_update_dir: trying to open assembly: {}", optional_string (fullpath.get ())); + log_debug (LOG_ASSEMBLY, "open_from_update_dir: trying to open assembly: %s", optional_string (fullpath.get ())); if (Util::file_exists (fullpath.get ())) { MonoImageOpenStatus status{}; result = mono_assembly_open_full (fullpath.get (), &status, 0); if (result == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { - log_warn (LOG_ASSEMBLY, "Failed to load managed assembly '{}'. {}", optional_string (fullpath.get ()), mono_image_strerror (status)); + log_warn (LOG_ASSEMBLY, "Failed to load managed assembly '%s'. %s", optional_string (fullpath.get ()), mono_image_strerror (status)); } } else { log_warn (LOG_ASSEMBLY, "open_from_update_dir: assembly file DOES NOT EXIST"sv); @@ -203,7 +203,7 @@ MonodroidRuntime::open_from_update_dir (MonoAssemblyName *aname, [[maybe_unused] } if (result != nullptr && Util::should_log (LOG_ASSEMBLY)) { - log_info_nocheck_fmt (LOG_ASSEMBLY, "open_from_update_dir: loaded assembly: {:p}", reinterpret_cast(result)); + log_info_nocheck_fmt (LOG_ASSEMBLY, "open_from_update_dir: loaded assembly: %p", reinterpret_cast(result)); } return result; } @@ -390,7 +390,7 @@ MonodroidRuntime::parse_gdb_options () noexcept time_t secs = time (nullptr); if (v + 10 < secs) { - log_warn (LOG_DEFAULT, "Found stale {} property with value '{}', not waiting.", SharedConstants::DEBUG_MONO_GDB_PROPERTY.data (), val.get ()); + log_warn (LOG_DEFAULT, "Found stale %s property with value '%s', not waiting.", SharedConstants::DEBUG_MONO_GDB_PROPERTY.data (), val.get ()); do_wait = false; } } @@ -461,13 +461,13 @@ MonodroidRuntime::parse_runtime_args (dynamic_local_string std::numeric_limits::max ()) { - log_error (LOG_DEFAULT, "Invalid SDB port value {}", sdb_port); + log_error (LOG_DEFAULT, "Invalid SDB port value %u", sdb_port); ret = false; continue; } if (out_port > std::numeric_limits::max ()) { - log_error (LOG_DEFAULT, "Invalid output port value {}", out_port); + log_error (LOG_DEFAULT, "Invalid output port value %u", out_port); ret = false; continue; } @@ -492,7 +492,7 @@ MonodroidRuntime::parse_runtime_args (dynamic_local_string arg (token); - log_error (LOG_DEFAULT, "Unknown runtime argument: '{}'", optional_string (arg.get ())); + log_error (LOG_DEFAULT, "Unknown runtime argument: '%s'", optional_string (arg.get ())); ret = false; } } @@ -521,9 +521,9 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse cur_time = time (nullptr); if (!parse_runtime_args (runtime_args, &options)) { - log_error (LOG_DEFAULT, "Failed to parse runtime args: '{}'", optional_string (runtime_args.get ())); + log_error (LOG_DEFAULT, "Failed to parse runtime args: '%s'", optional_string (runtime_args.get ())); } else if (options.debug && cur_time > options.timeout_time) { - log_warn (LOG_DEBUGGER, "Not starting the debugger as the timeout value has been reached; current-time: {}; timeout: {}", cur_time, options.timeout_time); + log_warn (LOG_DEBUGGER, "Not starting the debugger as the timeout value has been reached; current-time: %lld; timeout: %lld", static_cast(cur_time), static_cast(options.timeout_time)); } else if (options.debug && cur_time <= options.timeout_time) { EmbeddedAssemblies::set_register_debug_symbols (true); @@ -548,7 +548,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse // this text is used in unit tests to check the debugger started // do not change it without updating the test. - log_warn (LOG_DEBUGGER, "Trying to initialize the debugger with options: {}", optional_string (debug_arg)); + log_warn (LOG_DEBUGGER, "Trying to initialize the debugger with options: %s", optional_string (debug_arg)); if (options.out_port > 0) { int sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -581,7 +581,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse if (options.server) { int accepted = monodroid_debug_accept (sock, addr); - log_warn (LOG_DEBUGGER, "Accepted stdout connection: {}", accepted); + log_warn (LOG_DEBUGGER, "Accepted stdout connection: %d", accepted); if (accepted < 0) { Helpers::abort_application ( LOG_DEBUGGER, @@ -673,7 +673,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse if (AndroidSystem::monodroid_get_system_property (SharedConstants::DEBUG_MONO_RUNTIME_ARGS_PROPERTY, prop_val) > 0) { char **ptr; - log_warn (LOG_DEBUGGER, "passing '{}' as extra arguments to the runtime.", optional_string (prop_val.get ())); + log_warn (LOG_DEBUGGER, "passing '%s' as extra arguments to the runtime.", optional_string (prop_val.get ())); char **args = Util::monodroid_strsplit (prop_val.get (), " ", 0); int argc = 0; @@ -746,7 +746,7 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks if (user_assemblies_count == 0 && AndroidSystem::count_override_assemblies () == 0) { #if defined (DEBUG) log_fatal (LOG_DEFAULT, - "No assemblies found in '{}' or '{}'. Assuming this is part of Fast Deployment. Exiting...", + "No assemblies found in '%s' or '%s'. Assuming this is part of Fast Deployment. Exiting...", optional_string (AndroidSystem::override_dirs [0]), (AndroidSystem::override_dirs.size () > 1 && AndroidSystem::override_dirs [1] != nullptr) ? optional_string (AndroidSystem::override_dirs [1]) : "" ); @@ -840,7 +840,7 @@ MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobjec // GC threshold is 90% of the max GREF count init.grefGcThreshold = static_cast(AndroidSystem::get_gref_gc_threshold ()); - log_info (LOG_GC, "GREF GC Threshold: {}", init.grefGcThreshold); + log_info (LOG_GC, "GREF GC Threshold: %d", init.grefGcThreshold); init.grefClass = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "java_lang_Class", true); Class_getName = env->GetMethodID (init.grefClass, "getName", "()Ljava/lang/String;"); @@ -913,7 +913,7 @@ MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobjec auto initialize = reinterpret_cast (mono_method_get_unmanaged_callers_only_ftnptr (method, &error)); if (initialize == nullptr) { - log_fatal (LOG_DEFAULT, "Failed to get pointer to Initialize. Mono error: {}", optional_string (mono_error_get_message (&error))); + log_fatal (LOG_DEFAULT, "Failed to get pointer to Initialize. Mono error: %s", optional_string (mono_error_get_message (&error))); } abort_unless ( @@ -969,7 +969,7 @@ MonodroidRuntime::set_environment_variable_for_directory (const char *name, jstr if (createDirectory) { int rv = Util::create_directory (value.get_cstr (), mode); if (rv < 0 && errno != EEXIST) - log_warn (LOG_DEFAULT, "Failed to create directory for environment variable {}. {}", optional_string (name), strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to create directory for environment variable %s. %s", optional_string (name), strerror (errno)); } setenv (name, value.get_cstr (), 1); } @@ -979,10 +979,10 @@ MonodroidRuntime::create_xdg_directory (jstring_wrapper& home, size_t home_len, { static_local_string dir (home_len + relative_path.length ()); Util::path_combine (dir, home.get_cstr (), home_len, relative_path.data (), relative_path.length ()); - log_debug (LOG_DEFAULT, "Creating XDG directory: {}", optional_string (dir.get ())); + log_debug (LOG_DEFAULT, "Creating XDG directory: %s", optional_string (dir.get ())); int rv = Util::create_directory (dir.get (), DEFAULT_DIRECTORY_MODE); if (rv < 0 && errno != EEXIST) - log_warn (LOG_DEFAULT, "Failed to create XDG directory {}. {}", optional_string (dir.get ()), strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to create XDG directory %s. %s", optional_string (dir.get ()), strerror (errno)); if (!environment_variable_name.empty ()) { setenv (environment_variable_name.data (), dir.get (), 1); } @@ -1011,7 +1011,7 @@ MonodroidRuntime::set_debug_env_vars (void) noexcept return; auto log_envvar = [](const char *name, const char *v) { - log_debug (LOG_DEFAULT, "Env variable '{}' set to '{}'.", optional_string (name), optional_string (v)); + log_debug (LOG_DEFAULT, "Env variable '%s' set to '%s'.", optional_string (name), optional_string (v)); }; string_segment arg_token; @@ -1031,7 +1031,7 @@ MonodroidRuntime::set_debug_env_vars (void) noexcept log_envvar (arg.get (), one.data ()); } else if (index == 0) { // ’=value’ - log_warn (LOG_DEFAULT, "Attempt to set environment variable without specifying name: '{}'", optional_string (arg.get ())); + log_warn (LOG_DEFAULT, "Attempt to set environment variable without specifying name: '%s'", optional_string (arg.get ())); } else { // ’name=value’ arg[index] = '\0'; @@ -1111,10 +1111,10 @@ MonodroidRuntime::set_profile_options () noexcept .append (output_path.get (), output_path.length ()); } if (Util::create_directory (AndroidSystem::override_dirs[0], 0) < 0) { - log_warn (LOG_DEFAULT, "Failed to create directory '{}'. {}", optional_string (AndroidSystem::override_dirs[0]), std::strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to create directory '%s'. %s", optional_string (AndroidSystem::override_dirs[0]), std::strerror (errno)); } - log_warn (LOG_DEFAULT, "Initializing profiler with options: {}", optional_string (value.get ())); + log_warn (LOG_DEFAULT, "Initializing profiler with options: %s", optional_string (value.get ())); debug.monodroid_profiler_load (AndroidSystem::get_runtime_libdir (), value.get (), output_path.get ()); } @@ -1226,7 +1226,7 @@ MonodroidRuntime::preload_jni_libraries () noexcept return; } - log_debug (LOG_ASSEMBLY, "DSO jni preloads index stride == {}", dso_jni_preloads_idx_stride); + log_debug (LOG_ASSEMBLY, "DSO jni preloads index stride == %u", dso_jni_preloads_idx_stride); if ((dso_jni_preloads_idx_count % dso_jni_preloads_idx_stride) != 0) [[unlikely]] { Helpers::abort_application ( @@ -1245,11 +1245,11 @@ MonodroidRuntime::preload_jni_libraries () noexcept log_debug ( LOG_ASSEMBLY, - "Preloading JNI shared library: {} (entry's index: {}; real name hash: {:x}; name hash: {:x})", + "Preloading JNI shared library: %s (entry's index: %zu; real name hash: %zx; name hash: %zx)", optional_string (entry.name), entry_index, - entry.real_name_hash, - entry.hash + static_cast(entry.real_name_hash), + static_cast(entry.hash) ); char *err = nullptr; @@ -1262,7 +1262,7 @@ MonodroidRuntime::preload_jni_libraries () noexcept log_debug ( LOG_ASSEMBLY, - "Putting JNI library handle in alias entry at index {}: {}", + "Putting JNI library handle in alias entry at index %zu: %s", entry_alias_index, entry_alias.name ); @@ -1493,7 +1493,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl if (runtimeNativeLibDir != nullptr) { jstr = runtimeNativeLibDir; AndroidSystem::set_runtime_libdir (strdup (jstr.get_cstr ())); - log_debug (LOG_DEFAULT, "Using runtime path: {}", optional_string (AndroidSystem::get_runtime_libdir ())); + log_debug (LOG_DEFAULT, "Using runtime path: %s", optional_string (AndroidSystem::get_runtime_libdir ())); } @@ -1552,13 +1552,13 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl if (Util::should_log (LOG_DEFAULT)) [[unlikely]] { log_info_nocheck_fmt ( LOG_DEFAULT, - ".NET for Android version: {} ({}; {}); built on {}; NDK version: {}; API level: {}; MonoVM version: {}", - BuildInfo::xa_version.data (), - BuildInfo::architecture.data (), - BuildInfo::kind.data (), - BuildInfo::date.data (), - BuildInfo::ndk_version.data (), - BuildInfo::ndk_api_level.data (), + ".NET for Android version: %.*s (%.*s; %.*s); built on %.*s; NDK version: %.*s; API level: %.*s; MonoVM version: %s", + static_cast(BuildInfo::xa_version.length ()), BuildInfo::xa_version.data (), + static_cast(BuildInfo::architecture.length ()), BuildInfo::architecture.data (), + static_cast(BuildInfo::kind.length ()), BuildInfo::kind.data (), + static_cast(BuildInfo::date.length ()), BuildInfo::date.data (), + static_cast(BuildInfo::ndk_version.length ()), BuildInfo::ndk_version.data (), + static_cast(BuildInfo::ndk_api_level.length ()), BuildInfo::ndk_api_level.data (), mono_get_runtime_build_info () ); } @@ -1679,7 +1679,7 @@ MonodroidRuntime::get_java_class_name_for_TypeManager (jclass klass) noexcept JNIEnv *env = osBridge.ensure_jnienv (); jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName)); if (name == nullptr) { - log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at {:p}", reinterpret_cast(klass)); + log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at %p", reinterpret_cast(klass)); return nullptr; } diff --git a/src/native/mono/monodroid/monodroid-tracing.cc b/src/native/mono/monodroid/monodroid-tracing.cc index adb9f0db1d6..a83288f7450 100644 --- a/src/native/mono/monodroid/monodroid-tracing.cc +++ b/src/native/mono/monodroid/monodroid-tracing.cc @@ -27,7 +27,7 @@ MonodroidRuntime::log_traces (JNIEnv *env, TraceKind kind, const char *first_lin char *err = nullptr; void *handle = MonodroidDl::monodroid_dlopen (SharedConstants::xamarin_native_tracing_name.data (), MONO_DL_EAGER, &err, nullptr); if (handle == nullptr) { - log_warn (LOG_DEFAULT, "Failed to load native tracing library '{}'. {}", SharedConstants::xamarin_native_tracing_name, err == nullptr ? "Unknown error"sv : err); + log_warn (LOG_DEFAULT, "Failed to load native tracing library '%.*s'. %s", static_cast(SharedConstants::xamarin_native_tracing_name.length ()), SharedConstants::xamarin_native_tracing_name.data (), err == nullptr ? "Unknown error" : err); } else { load_symbol (handle, "xa_get_native_backtrace", _xa_get_native_backtrace); load_symbol (handle, "xa_get_managed_backtrace", _xa_get_managed_backtrace); diff --git a/src/native/mono/monodroid/osbridge.cc b/src/native/mono/monodroid/osbridge.cc index d0ea6af8547..f62e13d2fd8 100644 --- a/src/native/mono/monodroid/osbridge.cc +++ b/src/native/mono/monodroid/osbridge.cc @@ -213,7 +213,7 @@ OSBridge::_write_stack_trace (FILE *to, char *from, LogCategories category) *end = '\0'; if ((category == LOG_GREF && gref_to_logcat) || (category == LOG_LREF && lref_to_logcat)) { - log_debug (category, "{}", optional_string (m)); + log_debug (category, "%s", optional_string (m)); } if (to != nullptr) { fprintf (to, "%s\n", optional_string (m)); @@ -227,7 +227,7 @@ void OSBridge::_monodroid_gref_log (const char *message) { if (gref_to_logcat) { - log_debug (LOG_GREF, "{}", optional_string (message)); + log_debug (LOG_GREF, "%s", optional_string (message)); } if (!gref_log) return; @@ -243,7 +243,7 @@ OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject newH return c; log_info (LOG_GREF, - "+g+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})", + "+g+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", c, gc_weak_gref_count, reinterpret_cast(curHandle), @@ -257,7 +257,7 @@ OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject newH if (from_writable) { _write_stack_trace (nullptr, const_cast(from), LOG_GREF); } else { - log_info (LOG_GREF, "{}", optional_string (from)); + log_info (LOG_GREF, "%s", optional_string (from)); } } if (!gref_log) @@ -288,7 +288,7 @@ OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char *thr if ((log_categories & LOG_GREF) == 0) return; log_info (LOG_GREF, - "-g- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})", + "-g- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", c, gc_weak_gref_count, reinterpret_cast(handle), @@ -300,7 +300,7 @@ OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char *thr if (from_writable) { _write_stack_trace (nullptr, const_cast(from), LOG_GREF); } else { - log_info (LOG_GREF, "{}", optional_string (from)); + log_info (LOG_GREF, "%s", optional_string (from)); } } if (!gref_log) @@ -327,7 +327,7 @@ OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobject new if ((log_categories & LOG_GREF) == 0) return; log_info (LOG_GREF, - "+w+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})", + "+w+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)", gc_gref_count, c, reinterpret_cast(curHandle), @@ -341,7 +341,7 @@ OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobject new if (from_writable) { _write_stack_trace (nullptr, const_cast(from), LOG_GREF); } else { - log_info (LOG_GREF, "{}", optional_string (from)); + log_info (LOG_GREF, "%s", optional_string (from)); } } if (!gref_log) @@ -370,7 +370,7 @@ OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const char *th if ((log_categories & LOG_GREF) == 0) return; log_info (LOG_GREF, - "-w- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})", + "-w- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)", gc_gref_count, c, reinterpret_cast(handle), @@ -382,7 +382,7 @@ OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const char *th if (from_writable) { _write_stack_trace (nullptr, const_cast(from), LOG_GREF); } else { - log_info (LOG_GREF, "{}", optional_string (from)); + log_info (LOG_GREF, "%s", optional_string (from)); } } if (!gref_log) @@ -408,7 +408,7 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c if ((log_categories & LOG_LREF) == 0) return; log_info (LOG_LREF, - "+l+ lrefc {} handle {:p}/{} from thread '{}'({})", + "+l+ lrefc %d handle %p/%c from thread '%s'(%d)", lrefc, reinterpret_cast(handle), type, @@ -419,7 +419,7 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c if (from_writable) { _write_stack_trace (nullptr, const_cast(from), LOG_GREF); } else { - log_info (LOG_GREF, "{}", optional_string (from)); + log_info (LOG_GREF, "%s", optional_string (from)); } } if (!lref_log) @@ -444,7 +444,7 @@ OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, cons if ((log_categories & LOG_LREF) == 0) return; log_info (LOG_LREF, - "-l- lrefc {} handle {:p}/{} from thread '{}'({})", + "-l- lrefc %d handle %p/%c from thread '%s'(%d)", lrefc, reinterpret_cast(handle), type, @@ -455,7 +455,7 @@ OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, cons if (from_writable) { _write_stack_trace (nullptr, const_cast(from), LOG_GREF); } else { - log_info (LOG_GREF, "{}", optional_string (from)); + log_info (LOG_GREF, "%s", optional_string (from)); } } if (!lref_log) @@ -584,7 +584,7 @@ OSBridge::gc_bridge_class_kind (MonoClass *klass) i = get_gc_bridge_index (klass); if (i == static_cast (-NUM_GC_BRIDGE_TYPES)) { log_info (LOG_GC, - "asked if a class {}.{} is a bridge before we inited java.lang.Object", + "asked if a class %s.%s is a bridge before we inited java.lang.Object", optional_string (mono_class_get_namespace (klass)), optional_string (mono_class_get_name (klass)) ); @@ -620,7 +620,7 @@ OSBridge::gc_is_bridge_object (MonoObject *object) #if DEBUG MonoClass *mclass = mono_object_get_class (object); log_info (LOG_GC, - "object of class {}.{} with null handle", + "object of class %s.%s with null handle", optional_string (mono_class_get_namespace (mclass)), optional_string (mono_class_get_name (mclass)) ); @@ -718,9 +718,9 @@ OSBridge::add_reference (JNIEnv *env, OSBridge::AddReferenceTarget target, OSBri *reffed_description = describe_target (reffed_target); if (success) - log_warn (LOG_GC, "Added reference for {} to {}", optional_string (description), optional_string (reffed_description)); + log_warn (LOG_GC, "Added reference for %s to %s", optional_string (description), optional_string (reffed_description)); else - log_error (LOG_GC, "Missing monodroidAddReference method for {}", optional_string (description)); + log_error (LOG_GC, "Missing monodroidAddReference method for %s", optional_string (description)); free (description); free (reffed_description); @@ -957,7 +957,7 @@ OSBridge::gc_cleanup_after_java_collection (JNIEnv *env, int num_sccs, MonoGCBri if (Logger::gc_spew_enabled ()) { klass = mono_object_get_class (obj); log_error (LOG_GC, - "Missing monodroidClearReferences method for object of class {}.{}", + "Missing monodroidClearReferences method for object of class %s.%s", optional_string (mono_class_get_namespace (klass)), optional_string (mono_class_get_name (klass)) ); @@ -974,7 +974,7 @@ OSBridge::gc_cleanup_after_java_collection (JNIEnv *env, int num_sccs, MonoGCBri } } #if DEBUG - log_info (LOG_GC, "GC cleanup summary: {} objects tested - resurrecting {}.", total, alive); + log_info (LOG_GC, "GC cleanup summary: %d objects tested - resurrecting %d.", total, alive); #endif } @@ -1004,10 +1004,10 @@ OSBridge::gc_cross_references (int num_sccs, MonoGCBridgeSCC **sccs, int num_xre if (Logger::gc_spew_enabled ()) { int i, j; - log_info (LOG_GC, "cross references callback invoked with {} sccs and {} xrefs.", num_sccs, num_xrefs); + log_info (LOG_GC, "cross references callback invoked with %d sccs and %d xrefs.", num_sccs, num_xrefs); for (i = 0; i < num_sccs; ++i) { - log_info (LOG_GC, "group {} with {} objects", i, sccs [i]->num_objs); + log_info (LOG_GC, "group %d with %d objects", i, sccs [i]->num_objs); for (j = 0; j < sccs [i]->num_objs; ++j) { MonoObject *obj = sccs [i]->objs [j]; @@ -1022,7 +1022,7 @@ OSBridge::gc_cross_references (int num_sccs, MonoGCBridgeSCC **sccs, int num_xre } MonoClass *klass = mono_object_get_class (obj); log_info (LOG_GC, - "\tobj {:p} [{}::{}] handle {:p} key_handle {:p}", + "\tobj %p [%s::%s] handle %p key_handle %p", reinterpret_cast(obj), optional_string (mono_class_get_namespace (klass)), optional_string (mono_class_get_name (klass)), @@ -1034,7 +1034,7 @@ OSBridge::gc_cross_references (int num_sccs, MonoGCBridgeSCC **sccs, int num_xre if (Util::should_log (LOG_GC)) { for (i = 0; i < num_xrefs; ++i) - log_info_nocheck_fmt (LOG_GC, "xref [{}] {} -> {}", i, xrefs [i].src_scc_index, xrefs [i].dst_scc_index); + log_info_nocheck_fmt (LOG_GC, "xref [%d] %d -> %d", i, xrefs [i].src_scc_index, xrefs [i].dst_scc_index); } } diff --git a/src/native/mono/monodroid/xamarin-android-app-context.cc b/src/native/mono/monodroid/xamarin-android-app-context.cc index 33e3a023f37..fb7ce2b260b 100644 --- a/src/native/mono/monodroid/xamarin-android-app-context.cc +++ b/src/native/mono/monodroid/xamarin-android-app-context.cc @@ -15,7 +15,7 @@ MonodroidRuntime::get_method_name (uint32_t mono_image_index, uint32_t method_to { uint64_t id = (static_cast(mono_image_index) << 32) | method_token; - log_debug (LOG_ASSEMBLY, "MM: looking for name of method with id {:x}, in mono image at index {}", id, mono_image_index); + log_debug (LOG_ASSEMBLY, "MM: looking for name of method with id %llx, in mono image at index %u", static_cast(id), mono_image_index); size_t i = 0uz; while (mm_method_names[i].id != 0) { if (mm_method_names[i].id == id) { @@ -43,7 +43,7 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas { log_debug ( LOG_ASSEMBLY, - "MM: Trying to look up pointer to method '{}' (token {:x}) in class '{}' (index {})", + "MM: Trying to look up pointer to method '%s' (token %x) in class '%s' (index %u)", optional_string (get_method_name (mono_image_index, method_token)), method_token, optional_string (get_class_name (class_index)), class_index ); @@ -80,7 +80,7 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas log_debug ( LOG_ASSEMBLY, - "Loaded pointer to method {} ({:p}) (mono_image_index == {}; class_index == {}; method_token == {:x})", + "Loaded pointer to method %s (%p) (mono_image_index == %u; class_index == %u; method_token == %x)", optional_string (mono_method_full_name (method, true)), ret, mono_image_index, @@ -92,14 +92,14 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas log_fatal ( LOG_DEFAULT, - "Failed to obtain function pointer to method '{}' in class '{}'", + "Failed to obtain function pointer to method '%s' in class '%s'", optional_string (get_method_name (mono_image_index, method_token)), optional_string (get_class_name (class_index)) ); log_fatal ( LOG_DEFAULT, - "Looked for image index {}, class index {}, method token {:x}", + "Looked for image index %u, class index %u, method token %x", mono_image_index, class_index, method_token diff --git a/src/native/mono/pinvoke-override/pinvoke-override-api-impl.hh b/src/native/mono/pinvoke-override/pinvoke-override-api-impl.hh index 393101d6017..918c4804c7b 100644 --- a/src/native/mono/pinvoke-override/pinvoke-override-api-impl.hh +++ b/src/native/mono/pinvoke-override/pinvoke-override-api-impl.hh @@ -23,21 +23,21 @@ namespace xamarin::android { constexpr bool PREFER_AOT_CACHE = false; lib_handle = internal::MonodroidDl::monodroid_dlopen (library_name, MONO_DL_LOCAL, nullptr, PREFER_AOT_CACHE); if (lib_handle == nullptr) { - log_warn (LOG_ASSEMBLY, "Shared library '{}' not loaded, p/invoke '{}' may fail", optional_string (library_name), optional_string (symbol_name)); + log_warn (LOG_ASSEMBLY, "Shared library '%s' not loaded, p/invoke '%s' may fail", optional_string (library_name), optional_string (symbol_name)); return nullptr; } if (dso_handle != nullptr) { void *expected_null = nullptr; if (!__atomic_compare_exchange (dso_handle, &expected_null, &lib_handle, false /* weak */, __ATOMIC_ACQUIRE /* success_memorder */, __ATOMIC_RELAXED /* xxxfailure_memorder */)) { - log_debug (LOG_ASSEMBLY, "Library '{}' handle already cached by another thread", optional_string (library_name)); + log_debug (LOG_ASSEMBLY, "Library '%s' handle already cached by another thread", optional_string (library_name)); } } } void *entry_handle = internal::MonodroidDl::monodroid_dlsym (lib_handle, symbol_name, nullptr, nullptr); if (entry_handle == nullptr) { - log_warn (LOG_ASSEMBLY, "Symbol '{}' not found in shared library '{}', p/invoke may fail", optional_string (symbol_name), optional_string (library_name)); + log_warn (LOG_ASSEMBLY, "Symbol '%s' not found in shared library '%s', p/invoke may fail", optional_string (symbol_name), optional_string (library_name)); return nullptr; } @@ -60,7 +60,7 @@ namespace xamarin::android { return nullptr; } - log_debug (LOG_ASSEMBLY, "Caching p/invoke entry {} @ {}", library_name, entrypoint_name); + log_debug (LOG_ASSEMBLY, "Caching p/invoke entry %s @ %s", library_name.c_str (), entrypoint_name.c_str ()); (*api_map)[entrypoint_name] = entry_handle; return entry_handle; } @@ -81,7 +81,7 @@ namespace xamarin::android { ); if (already_loaded) { - log_debug (LOG_ASSEMBLY, "Entry '{}' from library '{}' already loaded by another thread", entrypoint_name, library_name); + log_debug (LOG_ASSEMBLY, "Entry '%s' from library '%s' already loaded by another thread", entrypoint_name, library_name); } } @@ -147,7 +147,7 @@ namespace xamarin::android { handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, lib_map, /* need_lock */ false); } else { if (iter->second == nullptr) [[unlikely]] { - log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '{}'", optional_string (library_name)); + log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '%s'", optional_string (library_name)); return nullptr; // fall back to `monodroid_dlopen` } diff --git a/src/native/mono/pinvoke-override/precompiled.cc b/src/native/mono/pinvoke-override/precompiled.cc index 29ac736e433..a82cdb79669 100644 --- a/src/native/mono/pinvoke-override/precompiled.cc +++ b/src/native/mono/pinvoke-override/precompiled.cc @@ -22,12 +22,12 @@ PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const cha PinvokeEntry *entry = find_pinvoke_address (entrypoint_hash, internal_pinvokes.data (), internal_pinvokes_count); if (entry == nullptr) [[unlikely]] { - log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '{} @ {}' (hash: {:x}) not found in compile-time map.", - optional_string (library_name), optional_string (entrypoint_name), entrypoint_hash); + log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '%s @ %s' (hash: %zx) not found in compile-time map.", + optional_string (library_name), optional_string (entrypoint_name), static_cast(entrypoint_hash)); log_fatal (LOG_ASSEMBLY, "compile-time map contents:"sv); for (size_t i = 0uz; i < internal_pinvokes_count; i++) { PinvokeEntry const& e = internal_pinvokes[i]; - log_fatal (LOG_ASSEMBLY, "\t'{}'={:p} (hash: {:x})", optional_string (e.name), e.func, e.hash); + log_fatal (LOG_ASSEMBLY, "\t'%s'=%p (hash: %zx)", optional_string (e.name), reinterpret_cast(e.func), static_cast(e.hash)); } Helpers::abort_application ( LOG_ASSEMBLY, @@ -68,7 +68,7 @@ PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const cha load_library_entry (library_name, entrypoint_name, *entry, dotnet_dso_handle); if (entry->func == nullptr) { - log_fatal (LOG_ASSEMBLY, "Failed to load symbol '{}' from shared library '{}'", + log_fatal (LOG_ASSEMBLY, "Failed to load symbol '%s' from shared library '%s'", optional_string (entrypoint_name), optional_string (library_name)); return nullptr; // let Mono deal with the fallout } @@ -77,7 +77,7 @@ PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const cha } // It's possible we don't have an entry for some `dotnet` p/invoke, fall back to the slow path below - log_debug (LOG_ASSEMBLY, "Symbol '{}' in library '{}' not found in the generated tables, falling back to slow path", + log_debug (LOG_ASSEMBLY, "Symbol '%s' in library '%s' not found in the generated tables, falling back to slow path", optional_string (entrypoint_name), optional_string (library_name)); } diff --git a/src/native/mono/runtime-base/android-system.cc b/src/native/mono/runtime-base/android-system.cc index 94a5d39a63d..24c88b2c238 100644 --- a/src/native/mono/runtime-base/android-system.cc +++ b/src/native/mono/runtime-base/android-system.cc @@ -66,7 +66,7 @@ AndroidSystem::lookup_system_property (const char *name, size_t &value_len) noex return nullptr; if (application_config.system_property_count % 2 != 0) { - log_warn (LOG_DEFAULT, "Corrupted environment variable array: does not contain an even number of entries ({})", application_config.system_property_count); + log_warn (LOG_DEFAULT, "Corrupted environment variable array: does not contain an even number of entries (%u)", application_config.system_property_count); return nullptr; } @@ -139,7 +139,7 @@ AndroidSystem::_monodroid__system_property_get (const char *name, char *sp_value char *buf = nullptr; if (sp_value_len < PROPERTY_VALUE_BUFFER_LEN) { size_t alloc_size = Helpers::add_with_overflow_check (PROPERTY_VALUE_BUFFER_LEN, 1uz); - log_warn (LOG_DEFAULT, "Buffer to store system property may be too small, will copy only {} bytes", sp_value_len); + log_warn (LOG_DEFAULT, "Buffer to store system property may be too small, will copy only %zu bytes", sp_value_len); buf = new char [alloc_size]; } @@ -251,12 +251,12 @@ AndroidSystem::monodroid_get_system_property_from_overrides ([[maybe_unused]] co } std::unique_ptr override_file {Util::path_combine (od, name)}; - log_info (LOG_DEFAULT, "Trying to get property from {}", override_file.get ()); + log_info (LOG_DEFAULT, "Trying to get property from %s", override_file.get ()); size_t result = _monodroid_get_system_property_from_file (override_file.get (), value); if (result == 0 || value == nullptr || (*value) == nullptr || **value == '\0') { continue; } - log_info (LOG_DEFAULT, "Property '{}' from {} has value '{}'.", name, od, *value); + log_info (LOG_DEFAULT, "Property '%s' from %s has value '%s'.", name, od, *value); return result; } #endif // def DEBUG @@ -282,7 +282,7 @@ AndroidSystem::create_update_dir (char *override_dir) noexcept override_dirs [0] = override_dir; Util::create_public_directory (override_dir); - log_warn (LOG_DEFAULT, "Creating public update directory: `{}`", override_dir); + log_warn (LOG_DEFAULT, "Creating public update directory: `%s`", override_dir); } bool @@ -431,9 +431,9 @@ AndroidSystem::get_max_gref_count_from_system (void) noexcept if (max < 0) max = std::numeric_limits::max (); if (*e) { - log_warn (LOG_GC, "Unsupported '{}' value '{}'.", SharedConstants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); + log_warn (LOG_GC, "Unsupported '%s' value '%s'.", SharedConstants::DEBUG_MONO_MAX_GREFC.data (), override.get ()); } - log_warn (LOG_GC, "Overriding max JNI Global Reference count to {}", max); + log_warn (LOG_GC, "Overriding max JNI Global Reference count to %ld", max); } return max; } @@ -459,7 +459,7 @@ AndroidSystem::setup_environment (const char *name, const char *value) noexcept if (isupper (name [0]) || name [0] == '_') { if (setenv (name, v, 1) < 0) - log_warn (LOG_DEFAULT, "(Debug) Failed to set environment variable: {}", strerror (errno)); + log_warn (LOG_DEFAULT, "(Debug) Failed to set environment variable: %s", strerror (errno)); return; } @@ -473,13 +473,13 @@ AndroidSystem::setup_environment_from_override_file (const char *path) noexcept struct stat sbuf; if (::stat (path, &sbuf) < 0) { - log_warn (LOG_DEFAULT, "Failed to stat the environment override file {}: {}", path, strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to stat the environment override file %s: %s", path, strerror (errno)); return; } int fd = open (path, O_RDONLY); if (fd < 0) { - log_warn (LOG_DEFAULT, "Failed to open the environment override file {}: {}", path, strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to open the environment override file %s: %s", path, strerror (errno)); return; } @@ -496,7 +496,7 @@ AndroidSystem::setup_environment_from_override_file (const char *path) noexcept } while (r < 0 && errno == EINTR); if (nread == 0) { - log_warn (LOG_DEFAULT, "Failed to read the environment override file {}: {}", path, strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to read the environment override file %s: %s", path, strerror (errno)); return; } @@ -517,26 +517,26 @@ AndroidSystem::setup_environment_from_override_file (const char *path) noexcept // # Variable value, terminated with NUL and padded to [value width] with NUL characters // value\0 if (nread < OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE) { - log_warn (LOG_DEFAULT, "Invalid format of the environment override file {}: malformatted header", path); + log_warn (LOG_DEFAULT, "Invalid format of the environment override file %s: malformatted header", path); return; } char *endptr; unsigned long name_width = strtoul (buf.get (), &endptr, 16); if ((name_width == std::numeric_limits::max () && errno == ERANGE) || (buf[0] != '\0' && *endptr != '\0')) { - log_warn (LOG_DEFAULT, "Malformed header of the environment override file {}: name width has invalid format", path); + log_warn (LOG_DEFAULT, "Malformed header of the environment override file %s: name width has invalid format", path); return; } unsigned long value_width = strtoul (buf.get () + 11, &endptr, 16); if ((value_width == std::numeric_limits::max () && errno == ERANGE) || (buf[0] != '\0' && *endptr != '\0')) { - log_warn (LOG_DEFAULT, "Malformed header of the environment override file {}: value width has invalid format", path); + log_warn (LOG_DEFAULT, "Malformed header of the environment override file %s: value width has invalid format", path); return; } uint64_t data_width = name_width + value_width; if (data_width > file_size - OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE || (file_size - OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE) % data_width != 0) { - log_warn (LOG_DEFAULT, "Malformed environment override file {}: invalid data size", path); + log_warn (LOG_DEFAULT, "Malformed environment override file %s: invalid data size", path); return; } @@ -544,11 +544,11 @@ AndroidSystem::setup_environment_from_override_file (const char *path) noexcept char *name = buf.get () + OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE; while (data_size > 0 && data_size >= data_width) { if (*name == '\0') { - log_warn (LOG_DEFAULT, "Malformed environment override file {}: name at offset {} is empty", path, name - buf.get ()); + log_warn (LOG_DEFAULT, "Malformed environment override file %s: name at offset %zd is empty", path, name - buf.get ()); return; } - log_debug (LOG_DEFAULT, "Setting environment variable from the override file {}: '{}' = '{}'", path, name, name + name_width); + log_debug (LOG_DEFAULT, "Setting environment variable from the override file %s: '%s' = '%s'", path, name, name + name_width); setup_environment (name, name + name_width); name += data_width; data_size -= data_width; @@ -583,10 +583,10 @@ AndroidSystem::setup_environment () noexcept } if (aotMode != MonoAotMode::MONO_AOT_MODE_LAST) { - log_debug (LOG_DEFAULT, "Mono AOT mode: {}", mono_aot_mode_name); + log_debug (LOG_DEFAULT, "Mono AOT mode: %s", mono_aot_mode_name); } else { if (!is_interpreter_enabled ()) { - log_warn (LOG_DEFAULT, "Unknown Mono AOT mode: {}", mono_aot_mode_name); + log_warn (LOG_DEFAULT, "Unknown Mono AOT mode: %s", mono_aot_mode_name); } else { log_warn (LOG_DEFAULT, "Mono AOT mode: interpreter"sv); } @@ -594,7 +594,7 @@ AndroidSystem::setup_environment () noexcept } if (application_config.environment_variable_count % 2 != 0) { - log_warn (LOG_DEFAULT, "Corrupted environment variable array: does not contain an even number of entries ({})", application_config.environment_variable_count); + log_warn (LOG_DEFAULT, "Corrupted environment variable array: does not contain an even number of entries (%u)", application_config.environment_variable_count); return; } @@ -610,10 +610,10 @@ AndroidSystem::setup_environment () noexcept var_value = ""; #if defined (DEBUG) - log_info (LOG_DEFAULT, "Setting environment variable '{}' to '{}'", var_name, var_value); + log_info (LOG_DEFAULT, "Setting environment variable '%s' to '%s'", var_name, var_value); #endif // def DEBUG if (setenv (var_name, var_value, 1) < 0) - log_warn (LOG_DEFAULT, "Failed to set environment variable: {}", strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to set environment variable: %s", strerror (errno)); } #if defined (DEBUG) log_debug (LOG_DEFAULT, "Loading environment from override directories."sv); @@ -622,9 +622,9 @@ AndroidSystem::setup_environment () noexcept continue; } std::unique_ptr env_override_file {Util::path_combine (od, OVERRIDE_ENVIRONMENT_FILE_NAME.data ())}; - log_debug (LOG_DEFAULT, "{}", env_override_file.get ()); + log_debug (LOG_DEFAULT, "%s", env_override_file.get ()); if (Util::file_exists (env_override_file.get ())) { - log_debug (LOG_DEFAULT, "Loading {}", env_override_file.get ()); + log_debug (LOG_DEFAULT, "Loading %s", env_override_file.get ()); setup_environment_from_override_file (env_override_file.get ()); } } @@ -652,12 +652,12 @@ AndroidSystem::detect_embedded_dso_mode (jstring_array_wrapper& appDirs) noexcep { // appDirs[SharedConstants::APP_DIRS_DATA_DIR_INDEX] points to the native library directory std::unique_ptr libmonodroid_path {Util::path_combine (appDirs[SharedConstants::APP_DIRS_DATA_DIR_INDEX].get_cstr (), "libmonodroid.so")}; - log_debug (LOG_ASSEMBLY, "Checking if libmonodroid was unpacked to {}", libmonodroid_path.get ()); + log_debug (LOG_ASSEMBLY, "Checking if libmonodroid was unpacked to %s", libmonodroid_path.get ()); if (!Util::file_exists (libmonodroid_path.get ())) { - log_debug (LOG_ASSEMBLY, "{} not found, assuming application/android:extractNativeLibs == false", libmonodroid_path.get ()); + log_debug (LOG_ASSEMBLY, "%s not found, assuming application/android:extractNativeLibs == false", libmonodroid_path.get ()); set_embedded_dso_mode_enabled (true); } else { - log_debug (LOG_ASSEMBLY, "Native libs extracted to {}, assuming application/android:extractNativeLibs == true", appDirs[SharedConstants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); + log_debug (LOG_ASSEMBLY, "Native libs extracted to %s, assuming application/android:extractNativeLibs == true", appDirs[SharedConstants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); set_embedded_dso_mode_enabled (false); } } @@ -670,7 +670,7 @@ AndroidSystem::setup_app_library_directories (jstring_array_wrapper& runtimeApks AndroidSystem::app_lib_directories = std::span (single_app_lib_directory); AndroidSystem::app_lib_directories [0] = Util::strdup_new (appDirs[SharedConstants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); - log_debug (LOG_ASSEMBLY, "Added filesystem DSO lookup location: {}", appDirs[SharedConstants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); + log_debug (LOG_ASSEMBLY, "Added filesystem DSO lookup location: %s", appDirs[SharedConstants::APP_DIRS_DATA_DIR_INDEX].get_cstr ()); } else { log_debug (LOG_DEFAULT, "Setting up for DSO lookup directly in the APK"sv); @@ -706,7 +706,7 @@ AndroidSystem::add_apk_libdir (const char *apk, size_t &index, const char *abi) { abort_unless (index < app_lib_directories.size (), "Index out of range"); app_lib_directories [index] = Util::string_concat (apk, "!/lib/", abi); - log_debug (LOG_ASSEMBLY, "Added APK DSO lookup location: {}", app_lib_directories[index]); + log_debug (LOG_ASSEMBLY, "Added APK DSO lookup location: %s", app_lib_directories[index]); index++; } diff --git a/src/native/mono/runtime-base/logger.cc b/src/native/mono/runtime-base/logger.cc index 256f6f2b030..8cb89af3386 100644 --- a/src/native/mono/runtime-base/logger.cc +++ b/src/native/mono/runtime-base/logger.cc @@ -29,7 +29,7 @@ namespace { if (path && access (path, W_OK) < 0) { log_warn (category, - "Could not open path '{}' for logging (\"{}\"). Using '{}/{}' instead.", + "Could not open path '%s' for logging (\"%s\"). Using '%s/%s' instead.", optional_string (path), strerror (errno), optional_string (override_dir), @@ -51,7 +51,7 @@ namespace { if (f) { Util::set_world_accessable (path); } else { - log_warn (category, "Could not open path '{}' for logging: {}", optional_string (path), strerror (errno)); + log_warn (category, "Could not open path '%s' for logging: %s", optional_string (path), strerror (errno)); } free (p); @@ -77,12 +77,12 @@ Logger::set_debugger_log_level (const char *level) noexcept unsigned long v = strtoul (level, nullptr, 0); if (v == std::numeric_limits::max () && errno == ERANGE) { - log_error (LOG_DEFAULT, "Invalid debugger log level value '{}', expecting a positive integer or zero", level); + log_error (LOG_DEFAULT, "Invalid debugger log level value '%s', expecting a positive integer or zero", level); return; } if (v > std::numeric_limits::max ()) { - log_warn (LOG_DEFAULT, "Debugger log level value is higher than the maximum of {}, resetting to the maximum value.", std::numeric_limits::max ()); + log_warn (LOG_DEFAULT, "Debugger log level value is higher than the maximum of %d, resetting to the maximum value.", std::numeric_limits::max ()); v = std::numeric_limits::max (); } diff --git a/src/native/mono/runtime-base/monodroid-dl.hh b/src/native/mono/runtime-base/monodroid-dl.hh index 3e2cfe62402..5a9511ba96f 100644 --- a/src/native/mono/runtime-base/monodroid-dl.hh +++ b/src/native/mono/runtime-base/monodroid-dl.hh @@ -46,11 +46,11 @@ namespace xamarin::android::internal size_t arr_size; if constexpr (WhichCache == CacheKind::AOT) { - log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in AOT cache", hash); + log_debug (LOG_ASSEMBLY, "Looking for hash %zx in AOT cache", static_cast(hash)); arr = aot_dso_cache; arr_size = application_config.number_of_aot_cache_entries; } else if constexpr (WhichCache == CacheKind::DSO) { - log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in DSO cache", hash); + log_debug (LOG_ASSEMBLY, "Looking for hash %zx in DSO cache", static_cast(hash)); arr = dso_cache; arr_size = application_config.number_of_dso_cache_entries; } @@ -60,7 +60,7 @@ namespace xamarin::android::internal ssize_t idx = Search::binary_search (hash, arr, arr_size); if (idx >= 0) { - log_debug (LOG_ASSEMBLY, "Found hash 0x{:x} entry at index {} of the cache", hash, idx); + log_debug (LOG_ASSEMBLY, "Found hash 0x%zx entry at index %zd of the cache", static_cast(hash), idx); return &arr[idx]; } @@ -101,7 +101,7 @@ namespace xamarin::android::internal if (MonodroidState::is_startup_in_progress ()) { auto ignore_component = [&](const char *label, MonoComponent component) -> bool { if ((application_config.mono_components_mask & component) != component) { - log_info (LOG_ASSEMBLY, "Mono '{}' component requested but not packaged, ignoring", label); + log_info (LOG_ASSEMBLY, "Mono '%s' component requested but not packaged, ignoring", label); return true; } @@ -146,7 +146,7 @@ namespace xamarin::android::internal [[gnu::flatten]] static void* monodroid_dlopen (DSOCacheEntry *dso, hash_t name_hash, const char *name, int flags, char **err) noexcept { - log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash match {}found, DSO name is '{}'", dso == nullptr ? "not "sv : ""sv, dso == nullptr ? ""sv : dso->name); + log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash match %sfound, DSO name is '%s'", dso == nullptr ? "not " : "", dso == nullptr ? "" : dso->name); if (dso == nullptr) { // DSO not known at build time, try to load it @@ -154,11 +154,11 @@ namespace xamarin::android::internal } else if (dso->handle != nullptr) { return monodroid_dlopen_log_and_return (dso->handle, err, dso->name, false /* name_needs_free */); } - log_debug (LOG_ASSEMBLY, "monodroid_dlopen: cache entry's real name hash == 0x{:x}; name hash == 0x{:x}", - dso->real_name_hash, dso->hash); + log_debug (LOG_ASSEMBLY, "monodroid_dlopen: cache entry's real name hash == 0x%zx; name hash == 0x%zx", + static_cast(dso->real_name_hash), static_cast(dso->hash)); if (dso->ignore) { - log_info (LOG_ASSEMBLY, "Request to load '{}' ignored, it is known not to exist", dso->name); + log_info (LOG_ASSEMBLY, "Request to load '%s' ignored, it is known not to exist", optional_string (dso->name)); return nullptr; } @@ -200,7 +200,7 @@ namespace xamarin::android::internal } hash_t name_hash = xxhash::hash (name, strlen (name)); - log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash for name '{}' is 0x{:x}", name, name_hash); + log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash for name '%s' is 0x%zx", name, static_cast(name_hash)); DSOCacheEntry *dso = nullptr; if (prefer_aot_cache) { diff --git a/src/native/mono/runtime-base/util.cc b/src/native/mono/runtime-base/util.cc index fda41ecfd88..3b2f8712592 100644 --- a/src/native/mono/runtime-base/util.cc +++ b/src/native/mono/runtime-base/util.cc @@ -149,7 +149,7 @@ Util::create_public_directory (const char *dir) { int ret = create_directory (dir, 0777); if (ret < 0) { - log_warn (LOG_DEFAULT, "Failed to create directory '{}'. {}", dir, std::strerror (errno)); + log_warn (LOG_DEFAULT, "Failed to create directory '%s'. %s", dir, std::strerror (errno)); } } @@ -196,7 +196,7 @@ Util::set_world_accessable ([[maybe_unused]] const char *path) } while (r == -1 && errno == EINTR); if (r == -1) { - log_error (LOG_DEFAULT, "chmod(\"{}\", 0664) failed: {}", path, strerror (errno)); + log_error (LOG_DEFAULT, "chmod(\"%s\", 0664) failed: %s", path, strerror (errno)); } } @@ -209,7 +209,7 @@ Util::set_world_accessible (int fd) noexcept -> bool } while (r == -1 && errno == EINTR); if (r == -1) { - log_error (LOG_DEFAULT, "fchmod() failed: {}", strerror (errno)); + log_error (LOG_DEFAULT, "fchmod() failed: %s", strerror (errno)); return false; } @@ -225,7 +225,7 @@ Util::set_user_executable ([[maybe_unused]] const char *path) } while (r == -1 && errno == EINTR); if (r == -1) { - log_error (LOG_DEFAULT, "chmod(\"{}\") failed: {}", path, strerror (errno)); + log_error (LOG_DEFAULT, "chmod(\"%s\") failed: %s", path, strerror (errno)); } } @@ -312,7 +312,7 @@ Util::monodroid_fopen (const char *filename, const char *mode) */ ret = fopen (filename, mode); if (ret == nullptr) { - log_error (LOG_DEFAULT, "fopen failed for file {}: {}", filename, strerror (errno)); + log_error (LOG_DEFAULT, "fopen failed for file %s: %s", filename, strerror (errno)); return nullptr; } diff --git a/src/native/mono/runtime-base/util.hh b/src/native/mono/runtime-base/util.hh index 8f4e6882bfe..700dd9c89ca 100644 --- a/src/native/mono/runtime-base/util.hh +++ b/src/native/mono/runtime-base/util.hh @@ -89,7 +89,7 @@ namespace xamarin::android { struct stat sbuf; if (fstatat (dirfd, file_name, &sbuf, 0) == -1) { - log_warn (LOG_ASSEMBLY, "Failed to stat file '{}': {}", file_name, std::strerror (errno)); + log_warn (LOG_ASSEMBLY, "Failed to stat file '%s': %s", optional_string (file_name), std::strerror (errno)); return std::nullopt; } @@ -100,7 +100,7 @@ namespace xamarin::android { int fd = openat (dirfd, file_name, O_RDONLY); if (fd < 0) { - log_error (LOG_ASSEMBLY, "Failed to open file '{}' for reading: {}", file_name, std::strerror (errno)); + log_error (LOG_ASSEMBLY, "Failed to open file '%s' for reading: %s", optional_string (file_name), std::strerror (errno)); return std::nullopt; } diff --git a/src/native/mono/shared/cpp-util.hh b/src/native/mono/shared/cpp-util.hh index cf4cb16bf5a..a3ba5688f54 100644 --- a/src/native/mono/shared/cpp-util.hh +++ b/src/native/mono/shared/cpp-util.hh @@ -142,7 +142,7 @@ abort_if_negative_integer_argument (int arg, const char *arg_name, std::source_l // of the calls present. force_inline inline void pd_log_location (std::source_location sloc = std::source_location::current ()) noexcept { - log_warn (LOG_DEFAULT, "loc: {}:{} ('{}')", sloc.file_name (), sloc.line (), sloc.function_name ()); + log_warn (LOG_DEFAULT, "loc: %s:%u ('%s')", sloc.file_name (), sloc.line (), sloc.function_name ()); } namespace xamarin::android diff --git a/src/native/mono/shared/helpers.cc b/src/native/mono/shared/helpers.cc index 510665cbaa3..d997f7ff4d8 100644 --- a/src/native/mono/shared/helpers.cc +++ b/src/native/mono/shared/helpers.cc @@ -11,7 +11,7 @@ using namespace xamarin::android; 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_fatal (category, "%s", message); // ...let android include it in the tombstone, debuggerd output, stack trace etc android_set_abort_message (message); @@ -35,7 +35,7 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo log_fatal ( category, - "Abort at {}:{}:{} ('{}')", + "Abort at %s:%u:%u ('%s')", file_name, sloc.line (), sloc.column (), diff --git a/src/native/mono/shared/log_functions.cc b/src/native/mono/shared/log_functions.cc index 394ba2a1927..ea3d4b2609d 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 @@ -117,4 +118,74 @@ namespace xamarin::android { __android_log_write (priority, CATEGORY_NAME (category), message); } + + void + log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept + { + size_t map_index = static_cast(level); + android_LogPriority priority; + + if (map_index > loglevel_map_max_index) { + priority = DEFAULT_PRIORITY; + } else { + priority = loglevel_map[map_index]; + } + + const char *safe_format = format == nullptr ? "" : format; + __android_log_vprint (priority, CATEGORY_NAME (category), safe_format, args); + } + + void + log_write_fmt (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_debug_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Debug, format, args); + va_end (args); + } + + void + log_info_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Info, format, args); + va_end (args); + } + + void + log_warn_fmt (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_error_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Error, format, args); + va_end (args); + } + + void + log_fatal_fmt (LogCategories category, const char *format, ...) noexcept + { + va_list args; + va_start (args, format); + log_writev (category, LogLevel::Fatal, 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..265d5625e6e 100644 --- a/src/native/mono/shared/log_types.hh +++ b/src/native/mono/shared/log_types.hh @@ -1,7 +1,7 @@ #pragma once +#include #include -#include #include #include @@ -20,99 +20,132 @@ #define DO_LOG_FMT(_level, _category_, _fmt_, ...) \ do { \ if ((log_categories & ((_category_))) != 0) { \ - ::log_ ## _level ## _nocheck_fmt ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \ + ::xamarin::android::log_ ## _level ## _fmt ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \ } \ } while (0) -// -// For std::format spec, see https://en.cppreference.com/w/cpp/utility/format/spec -// - -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style +// NOTE: _fmt_ takes arguments in POSIX printf style. #define log_debug(_category_, _fmt_, ...) DO_LOG_FMT (debug, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style +// NOTE: _fmt_ takes arguments in POSIX printf style. #define log_info(_category_, _fmt_, ...) DO_LOG_FMT (info, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_warn(_category_, _fmt_, ...) log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +// NOTE: _fmt_ takes arguments in POSIX printf style. +#define log_warn(_category_, _fmt_, ...) ::xamarin::android::log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_error(_category_, _fmt_, ...) log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +// NOTE: _fmt_ takes arguments in POSIX printf style. +#define log_error(_category_, _fmt_, ...) ::xamarin::android::log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) -// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style -#define log_fatal(_category_, _fmt_, ...) log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) +// NOTE: _fmt_ takes arguments in POSIX printf style. +#define log_fatal(_category_, _fmt_, ...) ::xamarin::android::log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__) 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_write_fmt (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4))); + void log_debug_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_info_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_warn_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_error_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); + void log_fatal_fmt (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 { log_write (category, level, message.data ()); } -} -template [[gnu::always_inline]] -static inline constexpr void log_debug_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args) -{ - log_write (category, xamarin::android::LogLevel::Debug, std::format (fmt, std::forward(args)...).c_str ()); -} + [[gnu::always_inline]] + static inline void log_debug_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Debug, message); + } -[[gnu::always_inline]] -static inline constexpr void log_debug_nocheck (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Debug, message.data ()); -} + [[gnu::always_inline]] + static inline void log_info_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Info, message); + } -template [[gnu::always_inline]] -static inline constexpr void log_info_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args) -{ - log_write (category, xamarin::android::LogLevel::Info, std::format (fmt, std::forward(args)...).c_str ()); -} + [[gnu::always_inline]] + static inline void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Warn, message); + } -[[gnu::always_inline]] -static inline constexpr void log_info_nocheck (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Info, message.data ()); -} + [[gnu::always_inline]] + static inline void log_error_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Error, message); + } -template [[gnu::always_inline]] -static inline constexpr void log_warn_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept -{ - log_write (category, xamarin::android::LogLevel::Warn, std::format (fmt, std::forward(args)...).c_str ()); -} + [[gnu::always_inline]] + static inline void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept + { + log_write (category, LogLevel::Fatal, message); + } -[[gnu::always_inline]] -static inline constexpr void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept -{ - log_write (category, xamarin::android::LogLevel::Warn, message.data ()); + [[gnu::always_inline]] + static inline void log_debug_fmt (LogCategories category, std::string const& message) noexcept + { + log_debug_fmt (category, std::string_view { message }); + } + + [[gnu::always_inline]] + static inline void log_info_fmt (LogCategories category, std::string const& message) noexcept + { + log_info_fmt (category, std::string_view { message }); + } + + [[gnu::always_inline]] + static inline void log_warn_fmt (LogCategories category, std::string const& message) noexcept + { + log_warn_fmt (category, std::string_view { message }); + } + + [[gnu::always_inline]] + static inline void log_error_fmt (LogCategories category, std::string const& message) noexcept + { + log_error_fmt (category, std::string_view { message }); + } + + [[gnu::always_inline]] + static inline void log_fatal_fmt (LogCategories category, std::string const& message) noexcept + { + log_fatal_fmt (category, std::string_view { message }); + } } -template [[gnu::always_inline]] -static inline constexpr void log_error_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept +[[gnu::always_inline]] +static inline constexpr void log_debug_nocheck (LogCategories category, std::string_view const& message) noexcept { - log_write (category, xamarin::android::LogLevel::Error, std::format (fmt, std::forward(args)...).c_str ()); + xamarin::android::log_write (category, xamarin::android::LogLevel::Debug, message.data ()); } [[gnu::always_inline]] -static inline constexpr void log_error_fmt (LogCategories category, std::string_view const& message) noexcept +static inline constexpr void log_info_nocheck (LogCategories category, std::string_view const& message) noexcept { - log_write (category, xamarin::android::LogLevel::Error, message.data ()); + xamarin::android::log_write (category, xamarin::android::LogLevel::Info, message.data ()); } -template [[gnu::always_inline]] -static inline constexpr void log_fatal_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept +static inline void log_debug_nocheck_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); +static inline void log_debug_nocheck_fmt (LogCategories category, const char *format, ...) noexcept { - log_write (category, xamarin::android::LogLevel::Fatal, std::format (fmt, std::forward(args)...).c_str ()); + va_list args; + va_start (args, format); + xamarin::android::log_writev (category, xamarin::android::LogLevel::Debug, format, args); + va_end (args); } -[[gnu::always_inline]] -static inline constexpr void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept +static inline void log_info_nocheck_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3))); +static inline void log_info_nocheck_fmt (LogCategories category, const char *format, ...) noexcept { - log_write (category, xamarin::android::LogLevel::Fatal, message.data ()); + va_list args; + va_start (args, format); + xamarin::android::log_writev (category, xamarin::android::LogLevel::Info, format, args); + va_end (args); } extern unsigned int log_categories; diff --git a/src/native/mono/xamarin-debug-app-helper/debug-app-helper.cc b/src/native/mono/xamarin-debug-app-helper/debug-app-helper.cc index 85de1c000ed..b7fce6c2e5d 100644 --- a/src/native/mono/xamarin-debug-app-helper/debug-app-helper.cc +++ b/src/native/mono/xamarin-debug-app-helper/debug-app-helper.cc @@ -48,7 +48,7 @@ Java_mono_android_DebugRuntime_init (JNIEnv *env, [[maybe_unused]] jclass klass, if (runtimeNativeLibDir != nullptr) { jstr = runtimeNativeLibDir; AndroidSystem::set_runtime_libdir (Util::strdup_new (jstr.get_cstr ())); - log_warn (LOG_DEFAULT, "Using runtime path: {}", optional_string (AndroidSystem::get_runtime_libdir ())); + log_warn (LOG_DEFAULT, "Using runtime path: %s", optional_string (AndroidSystem::get_runtime_libdir ())); } const char *monosgen_path = get_libmonosgen_path (); @@ -74,7 +74,7 @@ copy_file_to_internal_location (char *to_dir, char *from_dir, char *file) break; log_warn (LOG_DEFAULT, - "Copying file `{}` from external location `{}` to internal location `{}`", + "Copying file `%s` from external location `%s` to internal location `%s`", optional_string (file), optional_string (from_dir), optional_string (to_dir) @@ -86,12 +86,12 @@ copy_file_to_internal_location (char *to_dir, char *from_dir, char *file) int r = unlink (to_file); if (r < 0 && errno != ENOENT) { - log_warn (LOG_DEFAULT, "Unable to delete file `{}`: {}", optional_string (to_file), strerror (errno)); + log_warn (LOG_DEFAULT, "Unable to delete file `%s`: %s", optional_string (to_file), strerror (errno)); break; } if (!Util::file_copy (to_file, from_file)) { - log_warn (LOG_DEFAULT, "Copy failed from `{}` to `{}`: {}", optional_string (from_file), optional_string (to_file), strerror (errno)); + log_warn (LOG_DEFAULT, "Copy failed from `%s` to `%s`: %s", optional_string (from_file), optional_string (to_file), strerror (errno)); break; } @@ -110,22 +110,22 @@ copy_native_libraries_to_internal_location () dirent *e; char *dir_path = Util::path_combine (od, "lib"); - log_warn (LOG_DEFAULT, "checking directory: `{}`", optional_string (dir_path)); + log_warn (LOG_DEFAULT, "checking directory: `%s`", optional_string (dir_path)); if (dir_path == nullptr || !Util::directory_exists (dir_path)) { - log_warn (LOG_DEFAULT, "directory does not exist: `{}`", optional_string (dir_path)); + log_warn (LOG_DEFAULT, "directory does not exist: `%s`", optional_string (dir_path)); delete[] dir_path; continue; } if ((dir = ::opendir (dir_path)) == nullptr) { - log_warn (LOG_DEFAULT, "could not open directory: `{}`", optional_string (dir_path)); + log_warn (LOG_DEFAULT, "could not open directory: `%s`", optional_string (dir_path)); delete[] dir_path; continue; } while ((e = readdir (dir)) != nullptr) { - log_warn (LOG_DEFAULT, "checking file: `{}`", optional_string (e->d_name)); + log_warn (LOG_DEFAULT, "checking file: `%s`", optional_string (e->d_name)); if (Util::monodroid_dirent_hasextension (e, ".so")) { copy_file_to_internal_location (AndroidSystem::get_primary_override_dir (), dir_path, e->d_name); } @@ -142,9 +142,9 @@ runtime_exists (const char *dir, char*& libmonoso) return false; libmonoso = Util::path_combine (dir, SharedConstants::MONO_SGEN_SO); - log_warn (LOG_DEFAULT, "Checking whether Mono runtime exists at: {}", optional_string (libmonoso)); + log_warn (LOG_DEFAULT, "Checking whether Mono runtime exists at: %s", optional_string (libmonoso)); if (Util::file_exists (libmonoso)) { - log_info (LOG_DEFAULT, "Mono runtime found at: {}", optional_string (libmonoso)); + log_info (LOG_DEFAULT, "Mono runtime found at: %s", optional_string (libmonoso)); return true; } delete[] libmonoso; @@ -196,34 +196,34 @@ get_libmonosgen_path () if (!Util::file_exists (link)) { int result = symlink (libmonoso, link); if (result != 0 && errno == EEXIST) { - log_warn (LOG_DEFAULT, "symlink exists, recreating: {} -> {}", optional_string (link), optional_string (libmonoso)); + log_warn (LOG_DEFAULT, "symlink exists, recreating: %s -> %s", optional_string (link), optional_string (libmonoso)); unlink (link); result = symlink (libmonoso, link); } if (result != 0) - log_warn (LOG_DEFAULT, "symlink failed with errno={} {}", errno, strerror (errno)); + log_warn (LOG_DEFAULT, "symlink failed with errno=%d %s", errno, strerror (errno)); } delete[] libmonoso; libmonoso = link; } - log_warn (LOG_DEFAULT, "Trying to load sgen from: {}", optional_string (libmonoso)); + log_warn (LOG_DEFAULT, "Trying to load sgen from: %s", optional_string (libmonoso)); if (libmonoso != nullptr && Util::file_exists (libmonoso)) return libmonoso; delete[] libmonoso; if (runtime_exists (AndroidSystem::SYSTEM_LIB_PATH.data (), libmonoso)) return libmonoso; - log_fatal (LOG_DEFAULT, "Cannot find '{}'. Looked in the following locations:", SharedConstants::MONO_SGEN_SO); + log_fatal (LOG_DEFAULT, "Cannot find '%.*s'. Looked in the following locations:", static_cast(SharedConstants::MONO_SGEN_SO.length ()), SharedConstants::MONO_SGEN_SO.data ()); for (const char *od : AndroidSystem::override_dirs) { if (od == nullptr) continue; - log_fatal (LOG_DEFAULT, " {}", optional_string (od)); + log_fatal (LOG_DEFAULT, " %s", optional_string (od)); } for (const char *app_lib_dir : AndroidSystem::app_lib_directories) { - log_fatal (LOG_DEFAULT, " {}", optional_string (app_lib_dir)); + log_fatal (LOG_DEFAULT, " %s", optional_string (app_lib_dir)); } Helpers::abort_application ( diff --git a/src/native/nativeaot/host/bridge-processing.cc b/src/native/nativeaot/host/bridge-processing.cc index b59dae7c163..dafb38dc403 100644 --- a/src/native/nativeaot/host/bridge-processing.cc +++ b/src/native/nativeaot/host/bridge-processing.cc @@ -5,7 +5,6 @@ using namespace xamarin::android; const BridgeProcessingCallbacks BridgeProcessing::bridge_processing_callbacks { - .context = nullptr, .maybe_call_gc_user_peerable_add_managed_reference = &BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference, .maybe_call_gc_user_peerable_clear_managed_references = &BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references, }; @@ -41,7 +40,7 @@ void BridgeProcessing::naot_initialize_on_runtime_init (JNIEnv *env) noexcept } } -bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference ([[maybe_unused]] void *context, JNIEnv *env, jobject from, jobject to) noexcept +bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept { if (!env->IsInstanceOf (from, GCUserPeerable_class)) { return false; @@ -51,7 +50,7 @@ bool BridgeProcessing::maybe_call_gc_user_peerable_add_managed_reference ([[mayb return true; } -bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references ([[maybe_unused]] void *context, JNIEnv *env, jobject handle) noexcept +bool BridgeProcessing::maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept { if (!env->IsInstanceOf (handle, GCUserPeerable_class)) { return false; diff --git a/src/native/nativeaot/include/host/bridge-processing.hh b/src/native/nativeaot/include/host/bridge-processing.hh index 9e473107520..80aed8dad3a 100644 --- a/src/native/nativeaot/include/host/bridge-processing.hh +++ b/src/native/nativeaot/include/host/bridge-processing.hh @@ -12,8 +12,8 @@ public: static void naot_initialize_on_runtime_init (JNIEnv *env) noexcept; private: - static bool maybe_call_gc_user_peerable_add_managed_reference ([[maybe_unused]] void *context, JNIEnv *env, jobject from, jobject to) noexcept; - static bool maybe_call_gc_user_peerable_clear_managed_references ([[maybe_unused]] void *context, JNIEnv *env, jobject handle) noexcept; + static bool maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept; + static bool maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept; private: static const BridgeProcessingCallbacks bridge_processing_callbacks; From ee6ec0c5242de267428bb0334c27ed1f60089756 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 11:03:23 +0200 Subject: [PATCH 14/16] Check GCUserPeer before constructor lookup Avoid calling GetMethodID with a null jclass when initializing the temporary peer map. This preserves the intended abort message when the runtime field lookup fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/clr/host/bridge-processing.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc index 21d85dfaf7d..0d06315c7bb 100644 --- a/src/native/clr/host/bridge-processing.cc +++ b/src/native/clr/host/bridge-processing.cc @@ -63,9 +63,10 @@ void TemporaryPeerMap::initialize_on_runtime_init (JNIEnv *env, jclass runtimeCl abort_if_invalid_pointer_argument (runtimeClass, "runtimeClass"); peer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true); - peer_ctor = env->GetMethodID (peer_class, "", "()V"); + abort_unless (peer_class != nullptr, "Failed to load mono.android.GCUserPeer!"); - abort_unless (peer_class != nullptr && peer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!"); + peer_ctor = env->GetMethodID (peer_class, "", "()V"); + abort_unless (peer_ctor != nullptr, "Failed to load mono.android.GCUserPeer constructor!"); } void TemporaryPeerMap::add (StronglyConnectedComponent &scc) noexcept From b9b3c2a460f6579aaeef3d6f30d8b8f62ce981af Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 28 May 2026 11:17:15 +0200 Subject: [PATCH 15/16] Document NativeAOT C++ shims Explain that these shims intentionally cover only the no-libc++ allocation symbols needed by the NativeAOT runtime pack while native code is built without C++ exceptions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/native/nativeaot/host/cxx-shims.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/native/nativeaot/host/cxx-shims.cc b/src/native/nativeaot/host/cxx-shims.cc index 79868018251..19e1e62ec49 100644 --- a/src/native/nativeaot/host/cxx-shims.cc +++ b/src/native/nativeaot/host/cxx-shims.cc @@ -4,6 +4,9 @@ #include namespace std { + // NativeAOT is built without libc++ and with -fno-cxx-exceptions. Provide + // only the allocation/nothrow symbols needed by the runtime pack, and abort + // instead of throwing on allocation failure. const nothrow_t nothrow {}; } From 3b3f3040f406db19a6a12015b27da2fe3bbaa297 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 29 May 2026 15:37:12 +0200 Subject: [PATCH 16/16] Update apkdesc reference files from CI build 14220162 Drop libc++ from NativeAOT linking reduces libUnnamedProject.so by ~625KB. Refresh the BuildReleaseArm64 size-regression reference descriptions from the latest CI build. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...ldReleaseArm64SimpleDotNet.CoreCLR.apkdesc | 27 +++---- ...ildReleaseArm64SimpleDotNet.MonoVM.apkdesc | 22 +++--- ...ReleaseArm64SimpleDotNet.NativeAOT.apkdesc | 15 +--- ...ldReleaseArm64XFormsDotNet.CoreCLR.apkdesc | 73 ++++-------------- ...ildReleaseArm64XFormsDotNet.MonoVM.apkdesc | 76 +++++++++---------- 5 files changed, 75 insertions(+), 138 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc index 15e3aae8ee9..135a71476c5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc @@ -5,19 +5,19 @@ "Size": 3036 }, "classes.dex": { - "Size": 400044 + "Size": 405624 }, "lib/arm64-v8a/libassembly-store.so": { - "Size": 3098192 + "Size": 3124888 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 3223752 + "Size": 3222704 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 5771784 + "Size": 5757272 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1366648 + "Size": 1279264 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72344 @@ -26,22 +26,13 @@ "Size": 1280336 }, "lib/arm64-v8a/libSystem.Native.so": { - "Size": 100552 + "Size": 101872 }, "lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": { "Size": 162000 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 20424 - }, - "META-INF/BNDLTOOL.RSA": { - "Size": 1223 - }, - "META-INF/BNDLTOOL.SF": { - "Size": 2091 - }, - "META-INF/MANIFEST.MF": { - "Size": 1964 + "Size": 20776 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -68,5 +59,5 @@ "Size": 1904 } }, - "PackageSize": 7632514 -} + "PackageSize": 7620027 +} \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc index 42c7d46617e..20aeaa019a6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc @@ -8,31 +8,31 @@ "Size": 22384 }, "lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": { - "Size": 18232 + "Size": 18224 }, "lib/arm64-v8a/lib_Java.Interop.dll.so": { - "Size": 87720 + "Size": 88144 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 118392 + "Size": 118312 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 26848 + "Size": 26864 }, "lib/arm64-v8a/lib_System.Console.dll.so": { "Size": 24360 }, "lib/arm64-v8a/lib_System.Linq.dll.so": { - "Size": 25432 + "Size": 25448 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 638568 + "Size": 638000 }, "lib/arm64-v8a/lib_System.Runtime.dll.so": { "Size": 20224 }, "lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": { - "Size": 19752 + "Size": 19760 }, "lib/arm64-v8a/lib_UnnamedProject.dll.so": { "Size": 19968 @@ -44,10 +44,10 @@ "Size": 36416 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1385800 + "Size": 1294440 }, "lib/arm64-v8a/libmonosgen-2.0.so": { - "Size": 3111840 + "Size": 3111632 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72344 @@ -62,7 +62,7 @@ "Size": 162000 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 19832 + "Size": 19680 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -89,5 +89,5 @@ "Size": 1904 } }, - "PackageSize": 3254606 + "PackageSize": 3221838 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc index 75e2332c446..305a7566aa0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc @@ -5,19 +5,10 @@ "Size": 3124 }, "classes.dex": { - "Size": 25400 + "Size": 25304 }, "lib/arm64-v8a/libUnnamedProject.so": { - "Size": 5072296 - }, - "META-INF/BNDLTOOL.RSA": { - "Size": 1221 - }, - "META-INF/BNDLTOOL.SF": { - "Size": 1211 - }, - "META-INF/MANIFEST.MF": { - "Size": 1084 + "Size": 4446712 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -44,5 +35,5 @@ "Size": 1904 } }, - "PackageSize": 2126818 + "PackageSize": 1897243 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc index 3e9a925186f..a43a6641838 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc @@ -5,10 +5,10 @@ "Size": 6652 }, "classes.dex": { - "Size": 9444112 + "Size": 9411640 }, "classes2.dex": { - "Size": 156448 + "Size": 157320 }, "kotlin/annotation/annotation.kotlin_builtins": { "Size": 928 @@ -32,16 +32,16 @@ "Size": 2396 }, "lib/arm64-v8a/libassembly-store.so": { - "Size": 14076232 + "Size": 14100448 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 3223752 + "Size": 3222704 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 5771784 + "Size": 5757272 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1366648 + "Size": 1279264 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72344 @@ -50,13 +50,13 @@ "Size": 1280336 }, "lib/arm64-v8a/libSystem.Native.so": { - "Size": 100552 + "Size": 101872 }, "lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": { "Size": 162000 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 147264 + "Size": 147608 }, "META-INF/androidx.activity_activity.version": { "Size": 6 @@ -208,12 +208,6 @@ "META-INF/androidx.viewpager2_viewpager2.version": { "Size": 6 }, - "META-INF/BNDLTOOL.RSA": { - "Size": 1221 - }, - "META-INF/BNDLTOOL.SF": { - "Size": 90142 - }, "META-INF/com.android.tools/proguard/coroutines.pro": { "Size": 1345 }, @@ -238,9 +232,6 @@ "META-INF/kotlinx_coroutines_core.version": { "Size": 5 }, - "META-INF/MANIFEST.MF": { - "Size": 90015 - }, "META-INF/maven/com.google.guava/listenablefuture/pom.properties": { "Size": 96 }, @@ -478,9 +469,6 @@ "res/color-night-v8/material_timepicker_modebutton_tint.xml": { "Size": 340 }, - "res/color-v21/abc_btn_colored_borderless_text_material.xml": { - "Size": 464 - }, "res/color-v23/abc_btn_colored_borderless_text_material.xml": { "Size": 500 }, @@ -514,9 +502,6 @@ "res/color/abc_background_cache_hint_selector_material_light.xml": { "Size": 468 }, - "res/color/abc_btn_colored_text_material.xml": { - "Size": 604 - }, "res/color/abc_hint_foreground_material_dark.xml": { "Size": 564 }, @@ -544,24 +529,6 @@ "res/color/abc_secondary_text_material_light.xml": { "Size": 464 }, - "res/color/abc_tint_btn_checkable.xml": { - "Size": 728 - }, - "res/color/abc_tint_default.xml": { - "Size": 1224 - }, - "res/color/abc_tint_edittext.xml": { - "Size": 772 - }, - "res/color/abc_tint_seek_thumb.xml": { - "Size": 604 - }, - "res/color/abc_tint_spinner.xml": { - "Size": 772 - }, - "res/color/abc_tint_switch_track.xml": { - "Size": 768 - }, "res/color/checkbox_themeable_attribute_color.xml": { "Size": 464 }, @@ -1699,9 +1666,6 @@ "res/drawable/mtrl_ic_error.xml": { "Size": 644 }, - "res/drawable/mtrl_popupmenu_background_dark.xml": { - "Size": 740 - }, "res/drawable/mtrl_popupmenu_background.xml": { "Size": 740 }, @@ -1798,15 +1762,6 @@ "res/layout-v21/notification_template_icon_group.xml": { "Size": 988 }, - "res/layout-v22/abc_alert_dialog_button_bar_material.xml": { - "Size": 1584 - }, - "res/layout-v22/material_timepicker_dialog.xml": { - "Size": 3184 - }, - "res/layout-v22/mtrl_alert_dialog_actions.xml": { - "Size": 1764 - }, "res/layout-v26/abc_screen_toolbar.xml": { "Size": 1560 }, @@ -1844,7 +1799,7 @@ "Size": 1684 }, "res/layout/abc_alert_dialog_button_bar_material.xml": { - "Size": 1536 + "Size": 1584 }, "res/layout/abc_alert_dialog_material.xml": { "Size": 2648 @@ -2009,7 +1964,7 @@ "Size": 1208 }, "res/layout/material_timepicker_dialog.xml": { - "Size": 3132 + "Size": 3184 }, "res/layout/material_timepicker_textinput_display.xml": { "Size": 684 @@ -2018,7 +1973,7 @@ "Size": 1136 }, "res/layout/mtrl_alert_dialog_actions.xml": { - "Size": 1620 + "Size": 1764 }, "res/layout/mtrl_alert_dialog_title.xml": { "Size": 956 @@ -2276,8 +2231,8 @@ "Size": 268 }, "resources.arsc": { - "Size": 812848 + "Size": 794696 } }, - "PackageSize": 21263139 -} + "PackageSize": 21151309 +} \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc index 043500537a1..5a802f51b13 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc @@ -35,91 +35,91 @@ "Size": 25360 }, "lib/arm64-v8a/lib_Java.Interop.dll.so": { - "Size": 96344 + "Size": 96784 }, "lib/arm64-v8a/lib_Mono.Android.dll.so": { - "Size": 542848 + "Size": 542448 }, "lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": { - "Size": 26848 + "Size": 26864 }, "lib/arm64-v8a/lib_mscorlib.dll.so": { "Size": 21408 }, "lib/arm64-v8a/lib_netstandard.dll.so": { - "Size": 23048 + "Size": 23056 }, "lib/arm64-v8a/lib_System.Collections.dll.so": { - "Size": 33944 + "Size": 33952 }, "lib/arm64-v8a/lib_System.Collections.NonGeneric.dll.so": { - "Size": 25608 + "Size": 25624 }, "lib/arm64-v8a/lib_System.Collections.Specialized.dll.so": { - "Size": 23800 + "Size": 23816 }, "lib/arm64-v8a/lib_System.ComponentModel.dll.so": { - "Size": 19560 + "Size": 19568 }, "lib/arm64-v8a/lib_System.ComponentModel.Primitives.dll.so": { "Size": 21296 }, "lib/arm64-v8a/lib_System.ComponentModel.TypeConverter.dll.so": { - "Size": 43600 + "Size": 43608 }, "lib/arm64-v8a/lib_System.Console.dll.so": { - "Size": 24392 + "Size": 24400 }, "lib/arm64-v8a/lib_System.Core.dll.so": { - "Size": 19424 + "Size": 19432 }, "lib/arm64-v8a/lib_System.Diagnostics.TraceSource.dll.so": { - "Size": 24584 + "Size": 24592 }, "lib/arm64-v8a/lib_System.dll.so": { - "Size": 19784 + "Size": 19792 }, "lib/arm64-v8a/lib_System.Drawing.dll.so": { "Size": 19400 }, "lib/arm64-v8a/lib_System.Drawing.Primitives.dll.so": { - "Size": 30016 + "Size": 30032 }, "lib/arm64-v8a/lib_System.Formats.Asn1.dll.so": { - "Size": 50968 + "Size": 50976 }, "lib/arm64-v8a/lib_System.IO.Compression.Brotli.dll.so": { - "Size": 29536 + "Size": 29552 }, "lib/arm64-v8a/lib_System.IO.Compression.dll.so": { - "Size": 34600 + "Size": 34616 }, "lib/arm64-v8a/lib_System.IO.IsolatedStorage.dll.so": { - "Size": 28232 + "Size": 28240 }, "lib/arm64-v8a/lib_System.Linq.dll.so": { - "Size": 47912 + "Size": 47920 }, "lib/arm64-v8a/lib_System.Linq.Expressions.dll.so": { - "Size": 185968 + "Size": 185976 }, "lib/arm64-v8a/lib_System.Net.Http.dll.so": { - "Size": 86680 + "Size": 86688 }, "lib/arm64-v8a/lib_System.Net.Primitives.dll.so": { - "Size": 42280 + "Size": 42288 }, "lib/arm64-v8a/lib_System.Net.Requests.dll.so": { - "Size": 21520 + "Size": 21528 }, "lib/arm64-v8a/lib_System.ObjectModel.dll.so": { - "Size": 26968 + "Size": 26984 }, "lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": { - "Size": 999864 + "Size": 1000432 }, "lib/arm64-v8a/lib_System.Private.DataContractSerialization.dll.so": { - "Size": 217808 + "Size": 217816 }, "lib/arm64-v8a/lib_System.Private.Uri.dll.so": { "Size": 62216 @@ -128,25 +128,25 @@ "Size": 236968 }, "lib/arm64-v8a/lib_System.Private.Xml.Linq.dll.so": { - "Size": 35464 + "Size": 35480 }, "lib/arm64-v8a/lib_System.Runtime.dll.so": { - "Size": 20352 + "Size": 20360 }, "lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": { - "Size": 19752 + "Size": 19760 }, "lib/arm64-v8a/lib_System.Runtime.Numerics.dll.so": { "Size": 63312 }, "lib/arm64-v8a/lib_System.Runtime.Serialization.dll.so": { - "Size": 19328 + "Size": 19336 }, "lib/arm64-v8a/lib_System.Runtime.Serialization.Formatters.dll.so": { - "Size": 20296 + "Size": 20304 }, "lib/arm64-v8a/lib_System.Runtime.Serialization.Primitives.dll.so": { - "Size": 21424 + "Size": 21432 }, "lib/arm64-v8a/lib_System.Security.Cryptography.dll.so": { "Size": 82024 @@ -155,10 +155,10 @@ "Size": 194176 }, "lib/arm64-v8a/lib_System.Xml.dll.so": { - "Size": 19216 + "Size": 19224 }, "lib/arm64-v8a/lib_System.Xml.Linq.dll.so": { - "Size": 19240 + "Size": 19248 }, "lib/arm64-v8a/lib_UnnamedProject.dll.so": { "Size": 22040 @@ -239,10 +239,10 @@ "Size": 36416 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1385800 + "Size": 1294440 }, "lib/arm64-v8a/libmonosgen-2.0.so": { - "Size": 3111840 + "Size": 3111632 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72344 @@ -257,7 +257,7 @@ "Size": 162000 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 350616 + "Size": 350464 }, "META-INF/androidx.activity_activity.version": { "Size": 6 @@ -2435,5 +2435,5 @@ "Size": 794696 } }, - "PackageSize": 11024231 + "PackageSize": 10991463 } \ No newline at end of file