From 594d0905db5b57f996c5fec0fbcef2a4723ea4ec Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Mon, 14 Oct 2024 23:27:39 +0200 Subject: [PATCH] [native] Let Android know what is our reason to abort() (#9314) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Android NDK r10c (2014)][0]: > Added the following functions to all architectures: > `android_set_abort_message`, `posix_fadvise`, `posix_fadvise64`, > `pthread_gettid_np`. There doesn't appear to be any "proper" documentation for [`android_set_abort_message()`][1] (404), but it is *mentioned* in the [Logging][2] documentation, e.g. [`__android_log_call_aborter()`][3]: > **abort_message**: an additional message supplied when aborting, > for example this is used to call [android_set_abort_message()][1] > in [__android_log_default_aborter()][3]. `android_set_abort_message()` allows the caller to set a text message indicating the reason why the application is aborting its execution using the **abort**(3) function, since the call doesn't accept any parameters. The message is then used in the native stack trace as well as in the application tombstone, which may be helpful for us when reading crash reports which contain just the stack trace and no context information. Given the following code: Helpers::abort_application (LOG_ASSEMBLY, "Testing abort stuff"); We will see the following logged in logcat: F monodroid-assembly: Testing abort stuff F monodroid-assembly: Abort at monodroid-glue.cc:825:2 ('void xamarin::android::internal::MonodroidRuntime::init_android_runtime(JNIEnv *, jclass, jobject)') F libc : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 8803 (XAPerfTest.net9), pid 8803 (XAPerfTest.net9) I crash_dump64: obtaining output fd from tombstoned, type: kDebuggerdTombstoneProto I tombstoned: received crash request for pid 8803 I crash_dump64: performing dump of process 8803 (target tid = 8803) F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** F DEBUG : Build fingerprint: 'google/raven/raven:14/AP2A.240805.005.F1/12043167:user/release-keys' F DEBUG : Revision: 'MP1.0' F DEBUG : ABI: 'arm64' F DEBUG : Timestamp: 2024-09-24 12:00:57.245408791+0200 F DEBUG : Process uptime: 1s F DEBUG : Cmdline: com.xamarin.XAPerfTest.net9 F DEBUG : pid: 8803, tid: 8803, name: XAPerfTest.net9 >>> com.xamarin.XAPerfTest.net9 <<< F DEBUG : uid: 10456 F DEBUG : tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE) F DEBUG : signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- F DEBUG 👉: Abort message: 'Testing abort stuff' F DEBUG : x0 0000000000000000 x1 0000000000002263 x2 0000000000000006 x3 0000007fc7ed5530 F DEBUG : x4 3139343137396262 x5 3139343137396262 x6 3139343137396262 x7 7f7f7f7f7f7f7f7f F DEBUG : x8 00000000000000f0 x9 000000721c528350 x10 0000000000000001 x11 000000721c579170 F DEBUG : x12 0000007fc7ed3e50 x13 0000000000000088 x14 0000007fc7ed5068 x15 000000057ae2d47e F DEBUG : x16 000000721c5dffd0 x17 000000721c5cb560 x18 0000007230490000 x19 0000000000002263 F DEBUG : x20 0000000000002263 x21 00000000ffffffff x22 0000006f69e1d7d6 x23 0000007fc7ed58a4 F DEBUG : x24 0000007fc7ed58c8 x25 0000000000000000 x26 0000000000000000 x27 b400007218a0a060 F DEBUG : x28 0000000000000000 x29 0000007fc7ed55b0 F DEBUG : lr 000000721c5628b8 sp 0000007fc7ed5510 pc 000000721c5628e4 pst 0000000000001000 The rest of stack trace is omitted for brevity. We log the indicated message before calling `abort()`, but it also appears on the `Abort message:` line in the stack trace; see 👉. Messages provided to `android_set_abort_message()` appear to have an undocumented length limit of 120 characters, so messages should "front-load" important information. Additionally, don't log the full path to the source file containing the abort call, a file name is just enough. [0]: https://developer.android.com/ndk/downloads/revision_history [1]: https://developer.android.com/ndk/reference/set/abort-message-8h#set__abort__message_8h_1ac483b2fcb74191566f18476ce3280c9c [2]: https://developer.android.com/ndk/reference/group/logging [3]: https://developer.android.com/ndk/reference/group/logging#__android_log_call_aborter --- src/native/monodroid/debug.cc | 12 ++- .../monodroid/embedded-assemblies-zip.cc | 97 +++++++++++++++---- src/native/monodroid/embedded-assemblies.cc | 88 +++++++++++++---- src/native/monodroid/mono-image-loader.hh | 10 +- src/native/monodroid/mono-log-adapter.cc | 2 +- src/native/monodroid/monodroid-glue.cc | 75 +++++++++----- src/native/monodroid/timezones.cc | 3 +- .../monodroid/xamarin-android-app-context.cc | 21 ++-- src/native/pinvoke-override/precompiled.cc | 9 +- src/native/runtime-base/strings.hh | 24 +++-- src/native/runtime-base/util.cc | 16 ++- src/native/shared/cpp-util.hh | 5 +- src/native/shared/cxx-abi/terminate.cc | 3 +- src/native/shared/helpers.cc | 33 ++++++- src/native/shared/helpers.hh | 19 +++- src/native/shared/new_delete.cc | 5 +- src/native/tracing/native-tracing.cc | 4 +- .../debug-app-helper.cc | 15 ++- 18 files changed, 316 insertions(+), 125 deletions(-) diff --git a/src/native/monodroid/debug.cc b/src/native/monodroid/debug.cc index 7397fece427..139e8a24aae 100644 --- a/src/native/monodroid/debug.cc +++ b/src/native/monodroid/debug.cc @@ -216,8 +216,13 @@ Debug::start_debugging_and_profiling () if (AndroidSystem::monodroid_get_system_property (SharedConstants::DEBUG_MONO_CONNECT_PROPERTY, &connect_args) > 0) { DebuggerConnectionStatus res = start_connection (connect_args); if (res == DebuggerConnectionStatus::Error) { - log_fatal (LOG_DEBUGGER, "Could not start a connection to the debugger with connection args '%s'.", connect_args); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_DEBUGGER, + Util::monodroid_strdup_printf ( + "Connection to debugger failed. Args: %s", + connect_args + ) + ); } else if (res == DebuggerConnectionStatus::Connected) { /* Wait for XS to configure debugging/profiling */ gettimeofday(&wait_tv, nullptr); @@ -604,8 +609,7 @@ xamarin::android::conn_thread (void *arg) Debug *instance = static_cast (arg); res = instance->handle_server_connection (); if (res && res != 3) { - log_fatal (LOG_DEBUGGER, "Error communicating with the IDE, exiting..."); - Helpers::abort_application (); + Helpers::abort_application (LOG_DEBUGGER, "Error communicating with the IDE, exiting..."); } return nullptr; diff --git a/src/native/monodroid/embedded-assemblies-zip.cc b/src/native/monodroid/embedded-assemblies-zip.cc index 317b6cb9011..c64d6d33702 100644 --- a/src/native/monodroid/embedded-assemblies-zip.cc +++ b/src/native/monodroid/embedded-assemblies-zip.cc @@ -25,13 +25,25 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector const& entry_name, ZipEntryLoadState &state) noexcept { if (number_of_mapped_assembly_stores > number_of_assembly_store_files) { - log_fatal (LOG_ASSEMBLY, "Too many assembly stores. Expected at most %u", number_of_assembly_store_files); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Too many assembly stores. Expected at most %u", + number_of_assembly_store_files + ) + ); } int fd; @@ -192,13 +215,25 @@ EmbeddedAssemblies::map_assembly_store (dynamic_local_string auto header = static_cast(payload_start); if (header->magic != ASSEMBLY_STORE_MAGIC) { - log_fatal (LOG_ASSEMBLY, "Assembly store '%s' is not a valid .NET for Android assembly store file", entry_name.get ()); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Assembly store '%s' is not a valid .NET for Android assembly store file", + entry_name.get () + ) + ); } if (header->version != ASSEMBLY_STORE_FORMAT_VERSION) { - log_fatal (LOG_ASSEMBLY, "Assembly store '%s' uses format version 0x%x, instead of the expected 0x%x", entry_name.get (), header->version, ASSEMBLY_STORE_FORMAT_VERSION); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Assembly store '%s' uses format version 0x%x, instead of the expected 0x%x", + entry_name.get (), + header->version, + ASSEMBLY_STORE_FORMAT_VERSION + ) + ); } constexpr size_t header_size = sizeof(AssemblyStoreHeader); @@ -272,8 +307,13 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus uint16_t cd_entries; if (!zip_read_cd_info (fd, cd_offset, cd_size, cd_entries)) { - log_fatal (LOG_ASSEMBLY, "Failed to read the EOCD record from APK file %s", apk_name); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Failed to read the EOCD record from APK file %s", + apk_name + ) + ); } #ifdef DEBUG log_info (LOG_ASSEMBLY, "Central directory offset: %u", cd_offset); @@ -282,8 +322,16 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus #endif off_t retval = ::lseek (fd, static_cast(cd_offset), SEEK_SET); if (retval < 0) { - log_fatal (LOG_ASSEMBLY, "Failed to seek to central directory position in the APK file %s. %s (result: %d; errno: %d)", apk_name, std::strerror (errno), retval, errno); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Failed to seek to central directory position in APK: %s. retval=%d errno=%d, File=%s", + std::strerror (errno), + retval, + errno, + apk_name + ) + ); } std::vector buf (cd_size); @@ -298,12 +346,23 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus .local_header_offset = 0, .data_offset = 0, .file_size = 0, + .bundled_assemblies_slow_path = false, + .max_assembly_name_size = 0, + .max_assembly_file_name_size = 0, }; ssize_t nread = read (fd, buf.data (), static_cast(buf.size ())); if (static_cast(nread) != cd_size) { - log_fatal (LOG_ASSEMBLY, "Failed to read Central Directory from the APK archive %s. %s (nread: %d; errno: %d)", apk_name, std::strerror (errno), nread, errno); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Failed to read Central Directory from APK: %s. nread=%d errno=%d File=%s", + std::strerror (errno), + nread, + errno, + apk_name + ) + ); } if (application_config.have_assembly_store) { diff --git a/src/native/monodroid/embedded-assemblies.cc b/src/native/monodroid/embedded-assemblies.cc index d68174b2d8c..7f66b345d2a 100644 --- a/src/native/monodroid/embedded-assemblies.cc +++ b/src/native/monodroid/embedded-assemblies.cc @@ -86,12 +86,16 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb auto header = reinterpret_cast(data); if (header->magic == COMPRESSED_DATA_MAGIC) { if (compressed_assemblies.descriptors == nullptr) [[unlikely]] { - log_fatal (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined"); - Helpers::abort_application (); + Helpers::abort_application (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined"); } if (header->descriptor_index >= compressed_assemblies.count) [[unlikely]] { - log_fatal (LOG_ASSEMBLY, "Invalid compressed assembly descriptor index %u", header->descriptor_index); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Invalid compressed assembly descriptor index %u", + header->descriptor_index + ) + ); } CompressedAssemblyDescriptor &cad = compressed_assemblies.descriptors[header->descriptor_index]; @@ -105,14 +109,26 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb } if (cad.data == nullptr) [[unlikely]] { - log_fatal (LOG_ASSEMBLY, "Invalid compressed assembly descriptor at %u: no data", header->descriptor_index); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Invalid compressed assembly descriptor at %u: no data", + header->descriptor_index + ) + ); } if (header->uncompressed_length != cad.uncompressed_file_size) { if (header->uncompressed_length > cad.uncompressed_file_size) { - log_fatal (LOG_ASSEMBLY, "Compressed assembly '%s' is larger than when the application was built (expected at most %u, got %u). Assemblies don't grow just like that!", name, cad.uncompressed_file_size, header->uncompressed_length); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Compressed assembly '%s' is larger than when the application was built (expected at most %u, got %u). Assemblies don't grow just like that!", + name, + cad.uncompressed_file_size, + header->uncompressed_length + ) + ); } else { log_debug (LOG_ASSEMBLY, "Compressed assembly '%s' is smaller than when the application was built. Adjusting accordingly.", name); } @@ -123,13 +139,26 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb int ret = LZ4_decompress_safe (data_start, reinterpret_cast(cad.data), static_cast(assembly_data_size), static_cast(cad.uncompressed_file_size)); if (ret < 0) { - log_fatal (LOG_ASSEMBLY, "Decompression of assembly %s failed with code %d", name, ret); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Decompression of assembly %s failed with code %d", + name, + ret + ) + ); } if (static_cast(ret) != cad.uncompressed_file_size) { - log_debug (LOG_ASSEMBLY, "Decompression of assembly %s yielded a different size (expected %lu, got %u)", name, cad.uncompressed_file_size, static_cast(ret)); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Decompression of assembly %s yielded a different size (expected %lu, got %u)", + name, + cad.uncompressed_file_size, + static_cast(ret) + ) + ); } cad.loaded = true; } @@ -366,8 +395,14 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_stringdescriptor_index >= assembly_store.assembly_count) { - log_fatal (LOG_ASSEMBLY, "Invalid assembly descriptor index %u, exceeds the maximum value of %u", hash_entry->descriptor_index, assembly_store.assembly_count - 1); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Invalid assembly descriptor index %u, exceeds the maximum value of %u", + hash_entry->descriptor_index, + assembly_store.assembly_count - 1 + ) + ); } AssemblyStoreEntryDescriptor &store_entry = assembly_store.assemblies[hash_entry->descriptor_index]; @@ -499,8 +534,7 @@ EmbeddedAssemblies::binary_search (const Key *key, const Entry *base, size_t nme // This is a coding error on our part, crash! if (base == nullptr) { - log_fatal (LOG_ASSEMBLY, "Map address not passed to binary_search"); - Helpers::abort_application (); + Helpers::abort_application (LOG_ASSEMBLY, "Map address not passed to binary_search"); } [[maybe_unused]] @@ -855,8 +889,15 @@ EmbeddedAssemblies::md_mmap_apk_file (int fd, uint32_t offset, size_t size, cons mmap_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast(offsetPage)); if (mmap_info.area == MAP_FAILED) { - log_fatal (LOG_DEFAULT, "Could not `mmap` apk fd %d entry `%s`: %s", fd, filename, strerror (errno)); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Could not mmap APK fd %d: %s; File=%s", + fd, + strerror (errno), + filename + ) + ); } mmap_info.size = offsetSize; @@ -876,10 +917,15 @@ EmbeddedAssemblies::gather_bundled_assemblies_from_apk (const char* apk, monodro int fd; if ((fd = open (apk, O_RDONLY)) < 0) { - log_error (LOG_DEFAULT, "ERROR: Unable to load application package %s.", apk); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "ERROR: Unable to load application package %s.", + apk + ) + ); } - log_info (LOG_ASSEMBLY, "APK %s FD: %d", apk, fd); + log_debug (LOG_ASSEMBLY, "APK %s FD: %d", apk, fd); zip_load_entries (fd, apk, should_register); } diff --git a/src/native/monodroid/mono-image-loader.hh b/src/native/monodroid/mono-image-loader.hh index c1dbb913878..8e1c765128f 100644 --- a/src/native/monodroid/mono-image-loader.hh +++ b/src/native/monodroid/mono-image-loader.hh @@ -14,6 +14,7 @@ #include "xxhash.hh" #include "search.hh" #include "strings.hh" +#include "util.hh" #if defined (RELEASE) #define USE_CACHE 1 @@ -116,8 +117,13 @@ namespace xamarin::android::internal { #if defined (USE_CACHE) ssize_t index = find_index (hash); if (index < 0) { - log_fatal (LOG_ASSEMBLY, "Failed to look up image index for hash 0x%zx", hash); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Failed to look up image index for hash 0x%zx", + hash + ) + ); } // We don't need to worry about locking here. Even if we're overwriting an entry just set by another diff --git a/src/native/monodroid/mono-log-adapter.cc b/src/native/monodroid/mono-log-adapter.cc index da8e4ee3a52..ecdd1f21f5f 100644 --- a/src/native/monodroid/mono-log-adapter.cc +++ b/src/native/monodroid/mono-log-adapter.cc @@ -43,7 +43,7 @@ MonodroidRuntime::mono_log_handler (const char *log_domain, const char *log_leve __android_log_write (prio, log_domain, message); if (fatal) { - Helpers::abort_application (); + Helpers::abort_application (message); } } diff --git a/src/native/monodroid/monodroid-glue.cc b/src/native/monodroid/monodroid-glue.cc index 9fa816e80ab..88ff6ada6ba 100644 --- a/src/native/monodroid/monodroid-glue.cc +++ b/src/native/monodroid/monodroid-glue.cc @@ -83,8 +83,7 @@ MonodroidRuntime::thread_start ([[maybe_unused]] MonoProfiler *prof, [[maybe_unu if (r != JNI_OK) { #if DEBUG - log_fatal (LOG_DEFAULT, "ERROR: Unable to attach current thread to the Java VM!"); - Helpers::abort_application (); + Helpers::abort_application ("ERROR: Unable to attach current thread to the Java VM!"); #endif } } @@ -545,8 +544,13 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse if (options.out_port > 0) { int sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) { - log_fatal (LOG_DEBUGGER, "Could not construct a socket for stdout and stderr; does your app have the android.permission.INTERNET permission? %s", strerror (errno)); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_DEBUGGER, + Util::monodroid_strdup_printf ( + "Could not construct a socket for stdout and stderr; does your app have the android.permission.INTERNET permission? %s", + strerror (errno) + ) + ); } sockaddr_in addr; @@ -557,27 +561,43 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse int r; if ((r = inet_pton (AF_INET, options.host, &addr.sin_addr)) != 1) { - log_error (LOG_DEBUGGER, "Could not setup a socket for stdout and stderr: %s", - r == -1 ? strerror (errno) : "address not parseable in the specified address family"); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_DEBUGGER, + Util::monodroid_strdup_printf ( + "Could not setup a socket for stdout and stderr: %s", + r == -1 ? strerror (errno) : "address not parseable in the specified address family" + ) + ); } if (options.server) { int accepted = monodroid_debug_accept (sock, addr); log_warn (LOG_DEBUGGER, "Accepted stdout connection: %d", accepted); if (accepted < 0) { - log_fatal (LOG_DEBUGGER, "Error accepting stdout and stderr (%s:%d): %s", - options.host, options.out_port, strerror (errno)); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_DEBUGGER, + Util::monodroid_strdup_printf ( + "Error accepting stdout and stderr (%s:%d): %s", + options.host, + options.out_port, + strerror (errno) + ) + ); } dup2 (accepted, 1); dup2 (accepted, 2); } else { if (monodroid_debug_connect (sock, addr) != 1) { - log_fatal (LOG_DEBUGGER, "Error connecting stdout and stderr (%s:%d): %s", - options.host, options.out_port, strerror (errno)); - Helpers::abort_application (); + Helpers::abort_application ( + LOG_DEBUGGER, + Util::monodroid_strdup_printf ( + "Error connecting stdout and stderr (%s:%d): %s", + options.host, + options.out_port, + strerror (errno) + ) + ); } dup2 (sock, 1); @@ -682,7 +702,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse } void -MonodroidRuntime::cleanup_runtime_config (MonovmRuntimeConfigArguments *args, [[maybe_unused]] void *user_data) +MonodroidRuntime::cleanup_runtime_config ([[maybe_unused]] MonovmRuntimeConfigArguments *args, [[maybe_unused]] void *user_data) { embeddedAssemblies.unmap_runtime_config_blob (); } @@ -718,10 +738,12 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks log_fatal (LOG_DEFAULT, "No assemblies (or assembly blobs) were found in the application APK file(s) or on the filesystem"); #endif constexpr const char *assemblies_prefix = EmbeddedAssemblies::get_assemblies_prefix ().data (); - log_fatal (LOG_DEFAULT, "Make sure that all entries in the APK directory named `%s` are STORED (not compressed)", assemblies_prefix); - log_fatal (LOG_DEFAULT, "If Android Gradle Plugin's minification feature is enabled, it is likely all the entries in `%s` are compressed", assemblies_prefix); - - Helpers::abort_application (); + Helpers::abort_application ( + Util::monodroid_strdup_printf ( + "ALL entries in APK named `%s` MUST be STORED. Gradle's minification may COMPRESS such entries.", + assemblies_prefix + ) + ); } MonoDomain *domain = mono_jit_init_version (const_cast ("RootDomain"), const_cast ("mobile")); @@ -760,15 +782,18 @@ MonodroidRuntime::lookup_bridge_info (MonoClass *klass, const OSBridge::MonoJava info->handle_type = mono_class_get_field_from_name (info->klass, const_cast ("handle_type")); info->refs_added = mono_class_get_field_from_name (info->klass, const_cast ("refs_added")); info->weak_handle = mono_class_get_field_from_name (info->klass, const_cast ("weak_handle")); - if (info->klass == nullptr || info->handle == nullptr || info->handle_type == nullptr || - info->refs_added == nullptr || info->weak_handle == nullptr) { - log_fatal (LOG_DEFAULT, "The type `%s.%s` is missing required instance fields! handle=%p handle_type=%p refs_added=%p weak_handle=%p", - type->_namespace, type->_typename, + if (info->klass == nullptr || info->handle == nullptr || info->handle_type == nullptr || info->refs_added == nullptr || info->weak_handle == nullptr) { + Helpers::abort_application ( + Util::monodroid_strdup_printf ( + "The type `%s.%s` is missing required instance fields! handle=%p handle_type=%p refs_added=%p weak_handle=%p", + type->_namespace, + type->_typename, info->handle, info->handle_type, info->refs_added, - info->weak_handle); - Helpers::abort_application (); + info->weak_handle + ) + ); } } @@ -1530,7 +1555,7 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, [[maybe_unused]] jobjectArray externalStorageDirs, jobjectArray assembliesJava, [[maybe_unused]] jstring packageName, - jint apiLevel, [[maybe_unused]] jobjectArray environmentVariables) + [[maybe_unused]] jint apiLevel, [[maybe_unused]] jobjectArray environmentVariables) { monodroidRuntime.Java_mono_android_Runtime_initInternal ( env, diff --git a/src/native/monodroid/timezones.cc b/src/native/monodroid/timezones.cc index 2680af72a2f..5816dd4ddce 100644 --- a/src/native/monodroid/timezones.cc +++ b/src/native/monodroid/timezones.cc @@ -39,8 +39,7 @@ init () AndroidEnvironment_NotifyTimeZoneChanged = mono_class_get_method_from_name (AndroidEnvironment, "NotifyTimeZoneChanged", 0); if (AndroidEnvironment_NotifyTimeZoneChanged == nullptr) { - log_fatal (LOG_DEFAULT, "Unable to find Android.Runtime.AndroidEnvironment.NotifyTimeZoneChanged()!"); - Helpers::abort_application (); + Helpers::abort_application ("Unable to find Android.Runtime.AndroidEnvironment.NotifyTimeZoneChanged()!"); } } diff --git a/src/native/monodroid/xamarin-android-app-context.cc b/src/native/monodroid/xamarin-android-app-context.cc index 6539be1f7a2..8fdffd7cbe7 100644 --- a/src/native/monodroid/xamarin-android-app-context.cc +++ b/src/native/monodroid/xamarin-android-app-context.cc @@ -49,12 +49,13 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas ); if (class_index >= marshal_methods_number_of_classes) [[unlikely]] { - log_fatal (LOG_DEFAULT, - "Internal error: invalid index for class cache (expected at most %u, got %u)", - marshal_methods_number_of_classes - 1, - class_index + Helpers::abort_application ( + Util::monodroid_strdup_printf ( + "Internal error: invalid index for class cache (expected at most %u, got %u)", + marshal_methods_number_of_classes - 1, + class_index + ) ); - Helpers::abort_application (); } // We need to do that, as Mono APIs cannot be invoked from threads that aren't attached to the runtime. @@ -102,14 +103,14 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas log_fatal (LOG_DEFAULT, "Failed to load class from the assembly"); } + const char *message = nullptr; if (error.error_code != MONO_ERROR_NONE) { - const char *msg = mono_error_get_message (&error); - if (msg != nullptr) { - log_fatal (LOG_DEFAULT, msg); - } + message = mono_error_get_message (&error); } - Helpers::abort_application (); + Helpers::abort_application ( + message == nullptr ? "Failure to obtain marshal methods function pointer" : message + ); } void diff --git a/src/native/pinvoke-override/precompiled.cc b/src/native/pinvoke-override/precompiled.cc index d82d7bd4341..c7496ee2723 100644 --- a/src/native/pinvoke-override/precompiled.cc +++ b/src/native/pinvoke-override/precompiled.cc @@ -28,7 +28,14 @@ PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const cha PinvokeEntry const& e = internal_pinvokes[i]; log_fatal (LOG_ASSEMBLY, "\t'%s'=%p (hash: 0x%zx)", e.name, e.func, e.hash); } - Helpers::abort_application (); + Helpers::abort_application ( + LOG_ASSEMBLY, + Util::monodroid_strdup_printf ( + "Failure handling a p/invoke request for '%s'@'%s'", + entrypoint_name, + library_name + ) + ); } return entry->func; diff --git a/src/native/runtime-base/strings.hh b/src/native/runtime-base/strings.hh index a552abd345c..77cf335eb9a 100644 --- a/src/native/runtime-base/strings.hh +++ b/src/native/runtime-base/strings.hh @@ -560,8 +560,7 @@ namespace xamarin::android::internal if constexpr (BoundsCheck) { size_t slen = strlen (s); if (offset + count > slen) { - log_fatal (LOG_DEFAULT, "Attempt to assign data from a string exceeds the source string length"); - Helpers::abort_application (); + Helpers::abort_application ("Attempt to assign data from a string exceeds the source string length"); } } @@ -766,24 +765,23 @@ namespace xamarin::android::internal return; } - log_fatal ( - LOG_DEFAULT, - "Index %u is out of range (0 - %u)", - access_index, idx - ); - Helpers::abort_application (); + char *message = nullptr; + int n = asprintf (&message, "Index %zu is out of range (0 - %zu)", access_index, idx); + Helpers::abort_application (n == -1 ? "Index out of range" : message); } force_inline void ensure_have_extra (size_t length) noexcept { size_t needed_space = Helpers::add_with_overflow_check (length, idx + 1); if (needed_space > buffer.size ()) { - log_fatal ( - LOG_DEFAULT, - "Attempt to store too much data in a buffer (capacity: %u; exceeded by: %u)", - buffer.size (), needed_space - buffer.size () + char *message = nullptr; + int n = asprintf ( + &message, + "Attempt to store too much data in a buffer (capacity: %zu; exceeded by: %zu)", + buffer.size (), + needed_space - buffer.size () ); - Helpers::abort_application (); + Helpers::abort_application (n == -1 ? "Attempt to store too much data in a buffer" : message); } } diff --git a/src/native/runtime-base/util.cc b/src/native/runtime-base/util.cc index 892d7fe5343..934ef789da8 100644 --- a/src/native/runtime-base/util.cc +++ b/src/native/runtime-base/util.cc @@ -64,8 +64,12 @@ Util::monodroid_load_assembly (MonoAssemblyLoadContextGCHandle alc_handle, const mono_assembly_name_free (aname); if (assm == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) { - log_fatal (LOG_DEFAULT, "Unable to find assembly '%s'.", basename); - Helpers::abort_application (); + Helpers::abort_application ( + monodroid_strdup_printf ( + "Unable to find assembly '%s'.", + basename + ) + ); } return assm; } @@ -91,8 +95,12 @@ Util::monodroid_load_assembly (MonoDomain *domain, const char *basename) mono_assembly_name_free (aname); if (!assm) { - log_fatal (LOG_DEFAULT, "Unable to find assembly '%s'.", basename); - Helpers::abort_application (); + Helpers::abort_application ( + monodroid_strdup_printf ( + "Unable to find assembly '%s'.", + basename + ) + ); } return assm; } diff --git a/src/native/shared/cpp-util.hh b/src/native/shared/cpp-util.hh index 3ac87f9de82..9b0922ff927 100644 --- a/src/native/shared/cpp-util.hh +++ b/src/native/shared/cpp-util.hh @@ -20,10 +20,11 @@ do_abort_unless (const char* fmt, ...) va_list ap; va_start (ap, fmt); - __android_log_vprint (ANDROID_LOG_FATAL, "monodroid", fmt, ap); + char *message = nullptr; + int n = vasprintf (&message, fmt, ap); va_end (ap); - xamarin::android::Helpers::abort_application (); + xamarin::android::Helpers::abort_application (n == -1 ? "Unable to allocate memory for abort message" : message); } #define abort_unless(_condition_, _fmt_, ...) \ diff --git a/src/native/shared/cxx-abi/terminate.cc b/src/native/shared/cxx-abi/terminate.cc index e38da64b034..04dc890fb61 100644 --- a/src/native/shared/cxx-abi/terminate.cc +++ b/src/native/shared/cxx-abi/terminate.cc @@ -12,7 +12,6 @@ namespace std { [[noreturn]] void terminate () noexcept { - __android_log_write (ANDROID_LOG_FATAL, "monodroid", "std::terminate() called. Aborting."); - xamarin::android::Helpers::abort_application (); + xamarin::android::Helpers::abort_application ("std::terminate() called. Aborting."); } } diff --git a/src/native/shared/helpers.cc b/src/native/shared/helpers.cc index 5ccd7bd4e3f..bf8a8bd5339 100644 --- a/src/native/shared/helpers.cc +++ b/src/native/shared/helpers.cc @@ -1,15 +1,42 @@ +#include +#include +#include + #include "helpers.hh" +#include "java-interop-logger.h" using namespace xamarin::android; [[noreturn]] void -Helpers::abort_application (bool log_location, std::source_location sloc) noexcept +Helpers::abort_application (LogCategories category, const char *message, bool log_location, std::source_location sloc) noexcept { + // Log it, but also... + log_fatal (category, message); + + // ...let android include it in the tombstone, debuggerd output, stack trace etc + android_set_abort_message (message); + if (log_location) { + // We don't want to log the full path, just the file name. libc++ uses full file path here. + const char *file_name = sloc.file_name (); + const char *last_path_sep = strrchr (file_name, '/'); + + if (last_path_sep == nullptr) [[unlikely]] { + // In case we were built on Windows + last_path_sep = strrchr (file_name, '\\'); + } + + if (last_path_sep != nullptr) [[likely]] { + last_path_sep++; + if (*last_path_sep != '\0') [[unlikely]] { + file_name = last_path_sep; + } + } + log_fatal ( - LOG_DEFAULT, + category, "Abort at %s:%u:%u ('%s')", - sloc.file_name (), + file_name, sloc.line (), sloc.column (), sloc.function_name () diff --git a/src/native/shared/helpers.hh b/src/native/shared/helpers.hh index ba576d0fef2..3c6c16ca6a3 100644 --- a/src/native/shared/helpers.hh +++ b/src/native/shared/helpers.hh @@ -19,8 +19,10 @@ namespace xamarin::android Ret ret; if (__builtin_add_overflow (a, b, &ret)) [[unlikely]] { - log_fatal (LOG_DEFAULT, "Integer overflow on addition at %s:%u", sloc.file_name (), sloc.line ()); - abort_application (DoNotLogLocation, sloc); + // It will leak memory, but it's fine, we're exiting the app anyway + char *message = nullptr; + int n = asprintf (&message, "Integer overflow on addition at %s:%u", sloc.file_name (), sloc.line ()); + abort_application (n == -1 ? "Integer overflow on addition" : message, DoNotLogLocation, sloc); } return ret; @@ -33,14 +35,21 @@ namespace xamarin::android Ret ret; if (__builtin_mul_overflow (a, b, &ret)) [[unlikely]] { - log_fatal (LOG_DEFAULT, "Integer overflow on multiplication at %s:%u", sloc.file_name (), sloc.line ()); - abort_application (DoNotLogLocation, sloc); + // It will leak memory, but it's fine, we're exiting the app anyway + char *message = nullptr; + int n = asprintf (&message, "Integer overflow on multiplication at %s:%u", sloc.file_name (), sloc.line ()); + abort_application (n == -1 ? "Integer overflow on multiplication" : message, DoNotLogLocation, sloc); } return ret; } - [[noreturn]] static void abort_application (bool log_location = true, std::source_location sloc = std::source_location::current ()) noexcept; + [[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_application (const char *message, bool log_location = true, std::source_location sloc = std::source_location::current ()) noexcept + { + abort_application (LOG_DEFAULT, message, log_location, sloc); + } }; } #endif // __HELPERS_HH diff --git a/src/native/shared/new_delete.cc b/src/native/shared/new_delete.cc index dcd3eb1d20d..2293dddca10 100644 --- a/src/native/shared/new_delete.cc +++ b/src/native/shared/new_delete.cc @@ -22,10 +22,7 @@ operator new (size_t size) { void* p = do_alloc (size); if (p == nullptr) { -#if !defined (XAMARIN_TRACING) - log_fatal (LOG_DEFAULT, "Out of memory in the `new` operator"); -#endif - xamarin::android::Helpers::abort_application (); + xamarin::android::Helpers::abort_application ("Out of memory in the `new` operator"); } return p; diff --git a/src/native/tracing/native-tracing.cc b/src/native/tracing/native-tracing.cc index 5a54a342332..d1ec536dba3 100644 --- a/src/native/tracing/native-tracing.cc +++ b/src/native/tracing/native-tracing.cc @@ -304,7 +304,7 @@ void init_jni (JNIEnv *env) noexcept if (env->ExceptionOccurred ()) { env->ExceptionDescribe (); env->ExceptionClear (); - xamarin::android::Helpers::abort_application (); + xamarin::android::Helpers::abort_application ("Java exception occurred"); } bool all_found = assert_valid_jni_pointer (java_lang_Thread, "class", "java.lang.Thread"); @@ -314,7 +314,7 @@ void init_jni (JNIEnv *env) noexcept all_found &= assert_valid_jni_pointer (java_lang_Thread_currentThread, "method", "java.lang.StackTraceElement.toString ()"); if (!all_found) { - xamarin::android::Helpers::abort_application (); + xamarin::android::Helpers::abort_application ("JNI failure to look up type or method pointers"); } } diff --git a/src/native/xamarin-debug-app-helper/debug-app-helper.cc b/src/native/xamarin-debug-app-helper/debug-app-helper.cc index 9bdffa46b76..e083f905ab4 100644 --- a/src/native/xamarin-debug-app-helper/debug-app-helper.cc +++ b/src/native/xamarin-debug-app-helper/debug-app-helper.cc @@ -70,8 +70,12 @@ Java_mono_android_DebugRuntime_init (JNIEnv *env, [[maybe_unused]] jclass klass, const char *monosgen_path = get_libmonosgen_path (); void *monosgen = dlopen (monosgen_path, RTLD_LAZY | RTLD_GLOBAL); if (monosgen == nullptr) { - log_fatal (LOG_DEFAULT, "Failed to dlopen Mono runtime from %s: %s", monosgen_path, dlerror ()); - Helpers::abort_application (); + char *message = Util::monodroid_strdup_printf ( + "Failed to dlopen MonoVM: %s (from %s)", + dlerror (), + monosgen_path + ); + Helpers::abort_application (message); } } @@ -234,9 +238,10 @@ get_libmonosgen_path () log_fatal (LOG_DEFAULT, " %s", app_lib_dir); } - log_fatal (LOG_DEFAULT, "Do you have a shared runtime build of your app with AndroidManifest.xml android:minSdkVersion < 10 while running on a 64-bit Android 5.0 target? This combination is not supported."); - log_fatal (LOG_DEFAULT, "Please either set android:minSdkVersion >= 10 or use a build without the shared runtime (like default Release configuration)."); - Helpers::abort_application (); + Helpers::abort_application ( + "Do you have a shared runtime build of your app with AndroidManifest.xml android:minSdkVersion < 10 while running on a 64-bit Android 5.0 target? This combination is not supported. " + "Please either set android:minSdkVersion >= 10 or use a build without the shared runtime (like default Release configuration)." + ); return libmonoso; }