diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.R8.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.R8.apkdesc index b5158333e24..bf347bf119d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.R8.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.R8.apkdesc @@ -17,7 +17,7 @@ "Size": 4843864 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1184800 + "Size": 1094848 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72432 @@ -59,5 +59,5 @@ "Size": 1904 } }, - "PackageSize": 7058875 + "PackageSize": 7034299 } \ No newline at end of file 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 6eca790dae4..0edc6c57567 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 @@ -17,7 +17,7 @@ "Size": 4843864 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1184800 + "Size": 1094848 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72432 @@ -59,5 +59,5 @@ "Size": 1904 } }, - "PackageSize": 7058875 + "PackageSize": 7034299 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc index 029e52cbf6b..f525e93b692 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.R8.apkdesc @@ -38,7 +38,7 @@ "Size": 4843864 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1184800 + "Size": 1094848 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72432 @@ -2231,5 +2231,5 @@ "Size": 794696 } }, - "PackageSize": 16346643 + "PackageSize": 16317971 } \ 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 70922c56fcd..7281e64086b 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 @@ -41,7 +41,7 @@ "Size": 4843864 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1184800 + "Size": 1094848 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72432 @@ -2234,5 +2234,5 @@ "Size": 794696 } }, - "PackageSize": 18730573 + "PackageSize": 18468429 } \ No newline at end of file diff --git a/src/native/clr/host/assembly-store.cc b/src/native/clr/host/assembly-store.cc index 3b34cf87092..12ef3884d57 100644 --- a/src/native/clr/host/assembly-store.cc +++ b/src/native/clr/host/assembly-store.cc @@ -589,11 +589,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses_more_info */); - - dynamic_local_string msg; - msg.append (name); - msg.append (" (decompressed in another thread)"sv); - internal_timing.add_more_info (msg); + internal_timing.add_more_info (name, " (decompressed in another thread)"sv); } return {assembly_data, assembly_data_size}; } @@ -660,13 +656,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co __atomic_store_n (&cad.loaded, true, __ATOMIC_RELEASE); if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses_more_info */); - - dynamic_local_string msg; - msg.append (name); - if (loaded_from_cache) { - msg.append (" (decompressed cache hit)"sv); - } - internal_timing.add_more_info (msg); + internal_timing.add_more_info (name, loaded_from_cache ? " (decompressed cache hit)"sv : ""sv); } } @@ -690,11 +680,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses more info */); - - dynamic_local_string msg; - msg.append (name); - msg.append (" (memcpy to r/w area, part of assembly load time)"sv); - internal_timing.add_more_info (msg); + internal_timing.add_more_info (name, " (memcpy to r/w area, part of assembly load time)"sv); } set_assembly_data_and_size (rw_pointer, e.descriptor->data_size, assembly_data, assembly_data_size); diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc index acb571c38b4..1b06742d790 100644 --- a/src/native/clr/host/host.cc +++ b/src/native/clr/host/host.cc @@ -524,10 +524,8 @@ void Host::Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, int methods_len = env->GetStringLength (methods); const jchar *methods_ptr = env->GetStringChars (methods, nullptr); - 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: '{}'"sv, mt_ptr); env->ReleaseStringUTFChars (managedType, mt_ptr); // TODO: must attach thread to the runtime here @@ -541,12 +539,9 @@ void Host::Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses_more_info */); - dynamic_local_string type; mt_ptr = env->GetStringUTFChars (managedType, nullptr); - type.assign (mt_ptr, strlen (mt_ptr)); + internal_timing.add_more_info (mt_ptr); env->ReleaseStringUTFChars (managedType, mt_ptr); - - internal_timing.add_more_info (type); } } diff --git a/src/native/clr/include/host/assembly-store.hh b/src/native/clr/include/host/assembly-store.hh index 6a529a11376..bf77b58981f 100644 --- a/src/native/clr/include/host/assembly-store.hh +++ b/src/native/clr/include/host/assembly-store.hh @@ -9,7 +9,6 @@ #include #include -#include namespace xamarin::android { class AssemblyStore diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh index 380407eac1a..8a13669e9cc 100644 --- a/src/native/clr/include/runtime-base/android-system.hh +++ b/src/native/clr/include/runtime-base/android-system.hh @@ -128,6 +128,25 @@ namespace xamarin::android { } static auto monodroid_get_system_property (std::string_view const& name, dynamic_local_property_string &value) noexcept -> int; + + template + static auto monodroid_get_system_property (std::string_view const& name, char (&value)[Size]) noexcept -> int + { + dynamic_local_property_string property_value; + int result = monodroid_get_system_property (name, property_value); + if (result > 0) { + if (property_value.length () >= Size) { + value [0] = '\0'; + return -1; + } + memcpy (value, property_value.get (), property_value.length ()); + value [property_value.length ()] = '\0'; + } else { + value [0] = '\0'; + } + return result; + } + static void detect_embedded_dso_mode (jstring_array_wrapper& appDirs) noexcept; static void setup_environment () noexcept; static void setup_app_library_directories (jstring_array_wrapper& runtimeApks, jstring_array_wrapper& appDirs, bool have_split_apks) noexcept; diff --git a/src/native/common/include/runtime-base/timing-internal.hh b/src/native/common/include/runtime-base/timing-internal.hh index ae986be6ea8..310f8cb6fb4 100644 --- a/src/native/common/include/runtime-base/timing-internal.hh +++ b/src/native/common/include/runtime-base/timing-internal.hh @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -24,6 +26,7 @@ using namespace xamarin::android::internal; #include #include #include +#include #include namespace xamarin::android { @@ -183,53 +186,78 @@ namespace xamarin::android { // The [STAGE/EVENT] format is meant to help the test runner application, so that it can parse logcat without // having to be kept in sync with the actual wording used for the event message. // - template [[gnu::always_inline]] - static auto format_message (TimingEvent const& event, dynamic_local_string& message, bool indent = false) noexcept -> uint64_t + [[gnu::always_inline]] + static auto event_duration_ns (TimingEvent const& event) noexcept -> uint64_t { - using namespace std::literals; + return static_cast((event.end - event.start).count ()); + } - constexpr auto INDENT = " "sv; - constexpr auto NATIVE_INIT_TAG = "[0/"sv; - constexpr auto MANAGED_TAG = "[1/"sv; + // Returns the message length excluding NUL, or the negative required capacity including NUL. + static auto format_message (TimingEvent const& event, char *buffer, size_t buffer_size, bool indent) noexcept -> ssize_t + { + using namespace std::literals; - message.clear (); - if (indent) { - message.append (INDENT); + auto interval = event.end - event.start; // nanoseconds + int length = snprintf ( + buffer, + buffer_size, + "%s%s%u] %s%s; elapsed: %llu:%llu::%llu", + indent ? " " : "", + event.before_managed ? "[0/" : "[1/", + static_cast(event.kind), + event_kind_description (event.kind), + event.more_info == nullptr ? "" : event.more_info->c_str (), + static_cast(chrono::duration_cast(interval).count ()), + static_cast(chrono::duration_cast(interval).count ()), + static_cast((interval % 1ms).count ()) + ); + if (length < 0) { + if (buffer != nullptr && buffer_size > 0uz) { + buffer [0] = '\0'; + } + return 0; } - if (event.before_managed) { - message.append (NATIVE_INIT_TAG); - } else { - message.append (MANAGED_TAG); + size_t required_capacity = static_cast(length) + 1uz; + if (buffer == nullptr || buffer_size < required_capacity) { + return -static_cast(required_capacity); } - message.append (static_cast(event.kind)); - message.append ("] "sv); + return static_cast(length); + } - append_event_kind_description (event.kind, message); - if (event.more_info != nullptr && !event.more_info->empty ()) { - message.append (event.more_info->c_str (), event.more_info->length ()); + // Formats the event message into `stack_buffer`, falling back to a heap buffer when the message + // doesn't fit. The returned pointer must be passed to `std::free` if it differs from `stack_buffer`. + static auto build_message (TimingEvent const& event, char *stack_buffer, size_t stack_buffer_size, size_t *message_length, bool indent) noexcept -> char* + { + ssize_t result = format_message (event, stack_buffer, stack_buffer_size, indent); + if (result < 0) { + size_t required_capacity = static_cast(-result); + char *heap_buffer = static_cast (std::malloc (required_capacity)); + abort_unless (heap_buffer != nullptr, "Failed to allocate the timing event message"); + result = format_message (event, heap_buffer, required_capacity, indent); + abort_unless (result >= 0, "Failed to format the timing event message using the required capacity"); + if (message_length != nullptr) { + *message_length = static_cast(result); + } + return heap_buffer; } - auto interval = event.end - event.start; // nanoseconds - message.append ("; elapsed: "sv); - message.append (static_cast((chrono::duration_cast(interval).count ()))); - message.append (":"sv); - message.append (static_cast((chrono::duration_cast(interval)).count ())); - message.append ("::"sv); - message.append (static_cast((interval % 1ms).count ())); - - return static_cast(interval.count ()); + if (message_length != nullptr) { + *message_length = static_cast(result); + } + return stack_buffer; } [[gnu::always_inline]] static void format_and_log (TimingEvent const& event, bool indent = false) noexcept { - // `message` isn't used here, it is passed to `format_and_log` so that the `dump()` function can - // be slightly more efficient when dumping the event buffer. - dynamic_local_string message; - format_message (event, message, indent); - log_write (LOG_TIMING, LogLevel::Info, message.get ()); + char stack_buffer [Constants::MAX_LOGCAT_MESSAGE_LENGTH]; + char *message = build_message (event, stack_buffer, sizeof (stack_buffer), nullptr, indent); + log_write (LOG_TIMING, LogLevel::Info, message); + if (message != stack_buffer) { + std::free (message); + } } [[gnu::always_inline]] @@ -277,47 +305,32 @@ namespace xamarin::android { } } - template [[gnu::always_inline]] - void add_more_info (string_base const& str) noexcept + void add_more_info (const char *str, size_t length) noexcept { - TimingEvent *event = pop_sequence_event (); - if (event == nullptr) [[unlikely]] { - log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"sv); - return; - } + store_more_info (new std::string (str, length)); + } - event->more_info = new std::string (str.get (), str.length ()); - __atomic_store_n (&event->complete, true, __ATOMIC_RELEASE); - log (*event, false /* skip_log_if_more_info_missing */); + // Builds the message from two parts, so that its exact length is known up front and the + // caller doesn't need a temporary buffer that the message might not fit into. + [[gnu::always_inline]] + void add_more_info (std::string_view const& first, std::string_view const& second) noexcept + { + auto *more_info = new std::string (first.data (), first.length ()); + more_info->append (second); + store_more_info (more_info); } [[gnu::always_inline]] void add_more_info (const char* str) noexcept { - TimingEvent *event = pop_sequence_event (); - if (event == nullptr) [[unlikely]] { - log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"sv); - return; - } - - event->more_info = new std::string (str); - __atomic_store_n (&event->complete, true, __ATOMIC_RELEASE); - log (*event, false /* skip_log_if_more_info_missing */); + add_more_info (str, strlen (str)); } [[gnu::always_inline]] void add_more_info (std::string_view const& str) noexcept { - TimingEvent *event = pop_sequence_event (); - if (event == nullptr) [[unlikely]] { - log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"sv); - return; - } - - event->more_info = new std::string (str); - __atomic_store_n (&event->complete, true, __ATOMIC_RELEASE); - log (*event, false /* skip_log_if_more_info_missing */); + add_more_info (str.data (), str.length ()); } void dump () noexcept; @@ -375,6 +388,22 @@ namespace xamarin::android { void dump_to_file (size_t entries) noexcept; void dump (size_t entries, bool indent, std::function line_writer) noexcept; + // Takes ownership of `more_info`. + [[gnu::always_inline]] + void store_more_info (std::string *more_info) noexcept + { + TimingEvent *event = pop_sequence_event (); + if (event == nullptr) [[unlikely]] { + delete more_info; + log_warn (LOG_TIMING, "FastTiming::add_more_info called without prior FastTiming::start_event called"sv); + return; + } + + event->more_info = more_info; + __atomic_store_n (&event->complete, true, __ATOMIC_RELEASE); + log (*event, false /* skip_log_if_more_info_missing */); + } + [[gnu::always_inline]] auto get_sequence_event () noexcept -> TimingEvent* { @@ -396,77 +425,57 @@ namespace xamarin::android { return event; } - template [[gnu::always_inline]] - static void append_event_kind_description (TimingEventKind kind, dynamic_local_string& message) noexcept + [[gnu::always_inline]] + static auto event_kind_description (TimingEventKind kind) noexcept -> const char* { - auto append_desc = [&message] (std::string_view const& desc) { - message.append (desc); - }; - switch (kind) { case TimingEventKind::AssemblyDecompression: - append_desc ("Zstd decompression time for "sv); - return; + return "Zstd decompression time for "; case TimingEventKind::AssemblyLoad: - append_desc ("Assembly load for "sv); - return; + return "Assembly load for "; case TimingEventKind::AssemblyPreload: - append_desc ("Finished preloading, number of loaded assemblies: "sv); - return; + return "Finished preloading, number of loaded assemblies: "; case TimingEventKind::DebugStart: - append_desc ("Debug::start_debugging_and_profiling: end"sv); - return; + return "Debug::start_debugging_and_profiling: end"; case TimingEventKind::Init: - append_desc ("XATiming: init time"sv); - return; + return "XATiming: init time"; case TimingEventKind::JavaToManaged: - append_desc ("Typemap.java_to_managed: end, total time"sv); - return; + return "Typemap.java_to_managed: end, total time"; case TimingEventKind::ManagedToJava: - append_desc ("Typemap.managed_to_java: end, total time"sv); - return; + return "Typemap.managed_to_java: end, total time"; case TimingEventKind::ManagedRuntimeInit: - append_desc ("Runtime.init: Managed runtime init"sv); - return; + return "Runtime.init: Managed runtime init"; case TimingEventKind::NativeToManagedTransition: - append_desc ("Runtime.init: end native-to-managed transition"sv); - return; + return "Runtime.init: end native-to-managed transition"; case TimingEventKind::RuntimeConfigBlob: - append_desc ("Register runtimeconfig binary blob"sv); - return; + return "Register runtimeconfig binary blob"; case TimingEventKind::RuntimeRegister: - append_desc ("Runtime.register: end time. Registered type: "sv); - return; + return "Runtime.register: end time. Registered type: "; case TimingEventKind::TotalRuntimeInit: - append_desc ("Runtime.init: end, total time"sv); - return; + return "Runtime.init: end, total time"; case TimingEventKind::GetTimeOverhead: - append_desc ("clock_gettime overhead"sv); - return; + return "clock_gettime overhead"; case TimingEventKind::StartEndOverhead: - append_desc ("start+end event overhead"sv); - return; + return "start+end event overhead"; case TimingEventKind::FunctionCall: - append_desc ("function call: "sv); - return; + return "function call: "; case TimingEventKind::Unspecified: - append_desc ("unspecified event type: "sv); - return; + return "unspecified event type: "; } log_warnf ( @@ -474,11 +483,11 @@ namespace xamarin::android { "Unknown event kind '%u' logged", static_cast(kind) ); - append_desc ("unknown event kind"sv); + return "unknown event kind"; } private: - void parse_options (dynamic_local_property_string const& value) noexcept; + void parse_options (char *value) noexcept; static void really_initialize (bool log_immediately) noexcept; [[gnu::always_inline]] diff --git a/src/native/common/runtime-base/timing-internal.cc b/src/native/common/runtime-base/timing-internal.cc index 480bf582381..e399e1ed17a 100644 --- a/src/native/common/runtime-base/timing-internal.cc +++ b/src/native/common/runtime-base/timing-internal.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -30,8 +29,9 @@ void FastTiming::really_initialize (bool log_immediately) noexcept return; } - dynamic_local_property_string value; - if (AndroidSystem::monodroid_get_system_property (Constants::DEBUG_MONO_TIMING, value) != 0) { + char value [Constants::PROPERTY_VALUE_BUFFER_LEN]; + int value_length = AndroidSystem::monodroid_get_system_property (Constants::DEBUG_MONO_TIMING, value); + if (value_length > 0) { internal_timing.parse_options (value); } @@ -42,31 +42,33 @@ void FastTiming::really_initialize (bool log_immediately) noexcept ); } -void FastTiming::parse_options (dynamic_local_property_string const& value) noexcept +void FastTiming::parse_options (char *value) noexcept { - if (value.length () == 0) { - return; - } - - string_segment param; - while (value.next_token (',', param)) { - if (param.equal (OPT_TO_FILE)) { - log_to_file = true; - continue; - } - - if (param.starts_with (OPT_FILE_NAME)) { - output_file_name = std::make_unique (param.start () + OPT_FILE_NAME.length (), param.length () - OPT_FILE_NAME.length ()); - continue; + char *param = value; + while (param != nullptr && *param != '\0') { + char *separator = strchr (param, ','); + if (separator != nullptr) { + *separator = '\0'; } - 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 ()); + if (strcmp (param, OPT_TO_FILE.data ()) == 0) { + log_to_file = true; + } else if (strncmp (param, OPT_FILE_NAME.data (), OPT_FILE_NAME.length ()) == 0) { + output_file_name = std::make_unique (param + OPT_FILE_NAME.length ()); + } else if (strncmp (param, OPT_DURATION.data (), OPT_DURATION.length ()) == 0) { + const char *duration = param + OPT_DURATION.length (); + char *end; + errno = 0; + unsigned long long parsed_duration = strtoull (duration, &end, 10); + if (end == duration || *end != '\0' || errno == ERANGE || parsed_duration > std::numeric_limits::max ()) { + log_warn (LOG_TIMING, "Failed to parse duration in milliseconds from '%s'"sv, param); duration_ms = default_duration_milliseconds; + } else { + duration_ms = static_cast(parsed_duration); } - continue; } + + param = separator == nullptr ? nullptr : separator + 1; } if (output_file_name) { @@ -91,13 +93,17 @@ bool FastTiming::no_events_logged (size_t entries) noexcept void FastTiming::dump (size_t entries, bool indent, std::function line_writer) noexcept { - dynamic_local_string message; + char stack_buffer [Constants::MAX_LOGCAT_MESSAGE_LENGTH]; line_writer ("Startup costs:"sv); auto log = [&] (TimingEvent const& event) -> uint64_t { - uint64_t ret = format_message (event, message, indent); - line_writer (message.as_string_view ()); - return ret; + size_t message_length; + char *message = build_message (event, stack_buffer, sizeof (stack_buffer), &message_length, indent); + line_writer (std::string_view { message, message_length }); + if (message != stack_buffer) { + std::free (message); + } + return event_duration_ns (event); }; log (start_end_event_time); log (get_time_overhead); @@ -149,7 +155,6 @@ void FastTiming::dump (size_t entries, bool indent, std::function #include #include +#include #include #include @@ -1111,6 +1112,43 @@ MonodroidRuntime::set_profile_options () noexcept debug.monodroid_profiler_load (AndroidSystem::get_runtime_libdir (), value.get (), output_path.get ()); } +inline auto +format_assembly_load_timing_info (std::string_view prefix, const char *assembly_name, char *buffer, size_t buffer_size) noexcept -> ssize_t +{ + size_t assembly_name_length = strlen (assembly_name); + size_t more_info_length = Helpers::add_with_overflow_check (prefix.length (), assembly_name_length); + size_t required_capacity = Helpers::add_with_overflow_check (more_info_length, 1uz); + abort_unless (required_capacity <= static_cast(std::numeric_limits::max ()), "Assembly timing information is too long"); + if (buffer == nullptr || buffer_size < required_capacity) { + return -static_cast(required_capacity); + } + + memcpy (buffer, prefix.data (), prefix.length ()); + memcpy (buffer + prefix.length (), assembly_name, assembly_name_length); + buffer [more_info_length] = '\0'; + return static_cast(more_info_length); +} + +inline void +add_assembly_load_timing_info (std::string_view prefix, const char *assembly_name) noexcept +{ + char stack_buffer [SENSIBLE_PATH_MAX]; + char *more_info = stack_buffer; + ssize_t result = format_assembly_load_timing_info (prefix, assembly_name, more_info, sizeof (stack_buffer)); + if (result < 0) { + size_t required_capacity = static_cast(-result); + more_info = static_cast (std::malloc (required_capacity)); + abort_unless (more_info != nullptr, "Failed to allocate assembly load timing information"); + result = format_assembly_load_timing_info (prefix, assembly_name, more_info, required_capacity); + } + + abort_unless (result >= 0, "Failed to format assembly load timing information using the required capacity"); + internal_timing.add_more_info (more_info, static_cast(result)); + if (more_info != stack_buffer) { + std::free (more_info); + } +} + inline void MonodroidRuntime::load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jstring_wrapper &assembly) noexcept { @@ -1133,12 +1171,7 @@ MonodroidRuntime::load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, jst if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses_more_info */); - - constexpr std::string_view PREFIX { " (ALC): " }; - - dynamic_local_string more_info { PREFIX }; - more_info.append_c (assm_name); - internal_timing.add_more_info (more_info); + add_assembly_load_timing_info (" (ALC): "sv, assm_name); } } @@ -1169,13 +1202,7 @@ MonodroidRuntime::load_assembly (MonoDomain *domain, jstring_wrapper &assembly) if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses_more_info */); - - constexpr std::string_view PREFIX { " (domain): " }; - constexpr size_t PREFIX_SIZE = sizeof(PREFIX) - 1uz; - - dynamic_local_string more_info { PREFIX }; - more_info.append_c (assm_name); - internal_timing.add_more_info (more_info); + add_assembly_load_timing_info (" (domain): "sv, assm_name); } } @@ -1198,9 +1225,13 @@ MonodroidRuntime::load_assemblies (load_assemblies_context_type ctx, bool preloa if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses-more_info */); - static_local_string more_info; - more_info.append (static_cast(i + 1u)); - internal_timing.add_more_info (more_info); + char more_info [SharedConstants::INTEGER_BASE10_BUFFER_SIZE]; + int more_info_length = snprintf (more_info, sizeof (more_info), "%zu", i + 1uz); + abort_unless ( + more_info_length >= 0 && static_cast(more_info_length) < sizeof (more_info), + "Failed to format the assembly count" + ); + internal_timing.add_more_info (more_info, static_cast(more_info_length)); } } @@ -1639,12 +1670,9 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag if (FastTiming::enabled ()) [[unlikely]] { internal_timing.end_event (true /* uses_more_info */); - dynamic_local_string type; const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr); - type.assign (mt_ptr, strlen (mt_ptr)); + internal_timing.add_more_info (mt_ptr); env->ReleaseStringUTFChars (managedType, mt_ptr); - - internal_timing.add_more_info (type); } } diff --git a/src/native/mono/runtime-base/android-system.hh b/src/native/mono/runtime-base/android-system.hh index 0cb6b081589..1a39e3c7cf0 100644 --- a/src/native/mono/runtime-base/android-system.hh +++ b/src/native/mono/runtime-base/android-system.hh @@ -106,6 +106,24 @@ namespace xamarin::android::internal { return monodroid_get_system_property (name.data (), value); } + template + static int monodroid_get_system_property (std::string_view const& name, char (&value)[Size]) noexcept + { + dynamic_local_string property_value; + int result = monodroid_get_system_property (name.data (), property_value); + if (result > 0) { + if (property_value.length () >= Size) { + value [0] = '\0'; + return -1; + } + memcpy (value, property_value.get (), property_value.length ()); + value [property_value.length ()] = '\0'; + } else { + value [0] = '\0'; + } + return result; + } + static void set_override_dir (uint32_t index, const char* dir) noexcept { if (index >= override_dirs.size ())