Skip to content

Commit 594d090

Browse files
authored
[native] Let Android know what is our reason to abort() (#9314)
[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
1 parent c2a9ac6 commit 594d090

File tree

18 files changed

+316
-125
lines changed

18 files changed

+316
-125
lines changed

src/native/monodroid/debug.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,13 @@ Debug::start_debugging_and_profiling ()
216216
if (AndroidSystem::monodroid_get_system_property (SharedConstants::DEBUG_MONO_CONNECT_PROPERTY, &connect_args) > 0) {
217217
DebuggerConnectionStatus res = start_connection (connect_args);
218218
if (res == DebuggerConnectionStatus::Error) {
219-
log_fatal (LOG_DEBUGGER, "Could not start a connection to the debugger with connection args '%s'.", connect_args);
220-
Helpers::abort_application ();
219+
Helpers::abort_application (
220+
LOG_DEBUGGER,
221+
Util::monodroid_strdup_printf (
222+
"Connection to debugger failed. Args: %s",
223+
connect_args
224+
)
225+
);
221226
} else if (res == DebuggerConnectionStatus::Connected) {
222227
/* Wait for XS to configure debugging/profiling */
223228
gettimeofday(&wait_tv, nullptr);
@@ -604,8 +609,7 @@ xamarin::android::conn_thread (void *arg)
604609
Debug *instance = static_cast<Debug*> (arg);
605610
res = instance->handle_server_connection ();
606611
if (res && res != 3) {
607-
log_fatal (LOG_DEBUGGER, "Error communicating with the IDE, exiting...");
608-
Helpers::abort_application ();
612+
Helpers::abort_application (LOG_DEBUGGER, "Error communicating with the IDE, exiting...");
609613
}
610614

611615
return nullptr;

src/native/monodroid/embedded-assemblies-zip.cc

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,25 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector<uint8
2525

2626
log_debug (LOG_ASSEMBLY, "%s entry: %s", state.file_name, entry_name.get () == nullptr ? "unknown" : entry_name.get ());
2727
if (!result || entry_name.empty ()) {
28-
log_fatal (LOG_ASSEMBLY, "Failed to read Central Directory info for entry %u in APK file %s", entry_index, state.file_name);
29-
Helpers::abort_application ();
28+
Helpers::abort_application (
29+
LOG_ASSEMBLY,
30+
Util::monodroid_strdup_printf (
31+
"Failed to read Central Directory info for entry %u in APK %s",
32+
entry_index,
33+
state.file_name
34+
)
35+
);
3036
}
3137

3238
if (!zip_adjust_data_offset (state.file_fd, state)) {
33-
log_fatal (LOG_ASSEMBLY, "Failed to adjust data start offset for entry %u in APK file %s", entry_index, state.file_name);
34-
Helpers::abort_application ();
39+
Helpers::abort_application (
40+
LOG_ASSEMBLY,
41+
Util::monodroid_strdup_printf (
42+
"Failed to adjust data start offset for entry %u in APK %s",
43+
entry_index,
44+
state.file_name
45+
)
46+
);
3547
}
3648

3749
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);
@@ -61,9 +73,15 @@ EmbeddedAssemblies::zip_load_entry_common (size_t entry_index, std::vector<uint8
6173

6274
// assemblies must be 16-byte or 4-byte aligned, or Bad Things happen
6375
if (((state.data_offset & 0xf) != 0) || ((state.data_offset & 0x3) != 0)) {
64-
log_fatal (LOG_ASSEMBLY, "Assembly '%s' is located at bad offset %lu within the .apk", entry_name.get (), state.data_offset);
65-
log_fatal (LOG_ASSEMBLY, "You MUST run `zipalign` on %s to align it on 4 or 16 bytes ", strrchr (state.file_name, '/') + 1);
66-
Helpers::abort_application ();
76+
Helpers::abort_application (
77+
LOG_ASSEMBLY,
78+
Util::monodroid_strdup_printf (
79+
"Assembly '%s' is at bad offset %lu in the APK (not aligned to 4 or 16 bytes). 'zipalign' MUST be used on %s to align it properly",
80+
entry_name.get (),
81+
state.data_offset,
82+
strrchr (state.file_name, '/') + 1
83+
)
84+
);
6785
}
6886

6987
return true;
@@ -161,8 +179,13 @@ inline void
161179
EmbeddedAssemblies::map_assembly_store (dynamic_local_string<SENSIBLE_PATH_MAX> const& entry_name, ZipEntryLoadState &state) noexcept
162180
{
163181
if (number_of_mapped_assembly_stores > number_of_assembly_store_files) {
164-
log_fatal (LOG_ASSEMBLY, "Too many assembly stores. Expected at most %u", number_of_assembly_store_files);
165-
Helpers::abort_application ();
182+
Helpers::abort_application (
183+
LOG_ASSEMBLY,
184+
Util::monodroid_strdup_printf (
185+
"Too many assembly stores. Expected at most %u",
186+
number_of_assembly_store_files
187+
)
188+
);
166189
}
167190

168191
int fd;
@@ -192,13 +215,25 @@ EmbeddedAssemblies::map_assembly_store (dynamic_local_string<SENSIBLE_PATH_MAX>
192215
auto header = static_cast<AssemblyStoreHeader*>(payload_start);
193216

194217
if (header->magic != ASSEMBLY_STORE_MAGIC) {
195-
log_fatal (LOG_ASSEMBLY, "Assembly store '%s' is not a valid .NET for Android assembly store file", entry_name.get ());
196-
Helpers::abort_application ();
218+
Helpers::abort_application (
219+
LOG_ASSEMBLY,
220+
Util::monodroid_strdup_printf (
221+
"Assembly store '%s' is not a valid .NET for Android assembly store file",
222+
entry_name.get ()
223+
)
224+
);
197225
}
198226

199227
if (header->version != ASSEMBLY_STORE_FORMAT_VERSION) {
200-
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);
201-
Helpers::abort_application ();
228+
Helpers::abort_application (
229+
LOG_ASSEMBLY,
230+
Util::monodroid_strdup_printf (
231+
"Assembly store '%s' uses format version 0x%x, instead of the expected 0x%x",
232+
entry_name.get (),
233+
header->version,
234+
ASSEMBLY_STORE_FORMAT_VERSION
235+
)
236+
);
202237
}
203238

204239
constexpr size_t header_size = sizeof(AssemblyStoreHeader);
@@ -272,8 +307,13 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
272307
uint16_t cd_entries;
273308

274309
if (!zip_read_cd_info (fd, cd_offset, cd_size, cd_entries)) {
275-
log_fatal (LOG_ASSEMBLY, "Failed to read the EOCD record from APK file %s", apk_name);
276-
Helpers::abort_application ();
310+
Helpers::abort_application (
311+
LOG_ASSEMBLY,
312+
Util::monodroid_strdup_printf (
313+
"Failed to read the EOCD record from APK file %s",
314+
apk_name
315+
)
316+
);
277317
}
278318
#ifdef DEBUG
279319
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
282322
#endif
283323
off_t retval = ::lseek (fd, static_cast<off_t>(cd_offset), SEEK_SET);
284324
if (retval < 0) {
285-
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);
286-
Helpers::abort_application ();
325+
Helpers::abort_application (
326+
LOG_ASSEMBLY,
327+
Util::monodroid_strdup_printf (
328+
"Failed to seek to central directory position in APK: %s. retval=%d errno=%d, File=%s",
329+
std::strerror (errno),
330+
retval,
331+
errno,
332+
apk_name
333+
)
334+
);
287335
}
288336

289337
std::vector<uint8_t> buf (cd_size);
@@ -298,12 +346,23 @@ EmbeddedAssemblies::zip_load_entries (int fd, const char *apk_name, [[maybe_unus
298346
.local_header_offset = 0,
299347
.data_offset = 0,
300348
.file_size = 0,
349+
.bundled_assemblies_slow_path = false,
350+
.max_assembly_name_size = 0,
351+
.max_assembly_file_name_size = 0,
301352
};
302353

303354
ssize_t nread = read (fd, buf.data (), static_cast<read_count_type>(buf.size ()));
304355
if (static_cast<size_t>(nread) != cd_size) {
305-
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);
306-
Helpers::abort_application ();
356+
Helpers::abort_application (
357+
LOG_ASSEMBLY,
358+
Util::monodroid_strdup_printf (
359+
"Failed to read Central Directory from APK: %s. nread=%d errno=%d File=%s",
360+
std::strerror (errno),
361+
nread,
362+
errno,
363+
apk_name
364+
)
365+
);
307366
}
308367

309368
if (application_config.have_assembly_store) {

src/native/monodroid/embedded-assemblies.cc

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,16 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb
8686
auto header = reinterpret_cast<const CompressedAssemblyHeader*>(data);
8787
if (header->magic == COMPRESSED_DATA_MAGIC) {
8888
if (compressed_assemblies.descriptors == nullptr) [[unlikely]] {
89-
log_fatal (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined");
90-
Helpers::abort_application ();
89+
Helpers::abort_application (LOG_ASSEMBLY, "Compressed assembly found but no descriptor defined");
9190
}
9291
if (header->descriptor_index >= compressed_assemblies.count) [[unlikely]] {
93-
log_fatal (LOG_ASSEMBLY, "Invalid compressed assembly descriptor index %u", header->descriptor_index);
94-
Helpers::abort_application ();
92+
Helpers::abort_application (
93+
LOG_ASSEMBLY,
94+
Util::monodroid_strdup_printf (
95+
"Invalid compressed assembly descriptor index %u",
96+
header->descriptor_index
97+
)
98+
);
9599
}
96100

97101
CompressedAssemblyDescriptor &cad = compressed_assemblies.descriptors[header->descriptor_index];
@@ -105,14 +109,26 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb
105109
}
106110

107111
if (cad.data == nullptr) [[unlikely]] {
108-
log_fatal (LOG_ASSEMBLY, "Invalid compressed assembly descriptor at %u: no data", header->descriptor_index);
109-
Helpers::abort_application ();
112+
Helpers::abort_application (
113+
LOG_ASSEMBLY,
114+
Util::monodroid_strdup_printf (
115+
"Invalid compressed assembly descriptor at %u: no data",
116+
header->descriptor_index
117+
)
118+
);
110119
}
111120

112121
if (header->uncompressed_length != cad.uncompressed_file_size) {
113122
if (header->uncompressed_length > cad.uncompressed_file_size) {
114-
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);
115-
Helpers::abort_application ();
123+
Helpers::abort_application (
124+
LOG_ASSEMBLY,
125+
Util::monodroid_strdup_printf (
126+
"Compressed assembly '%s' is larger than when the application was built (expected at most %u, got %u). Assemblies don't grow just like that!",
127+
name,
128+
cad.uncompressed_file_size,
129+
header->uncompressed_length
130+
)
131+
);
116132
} else {
117133
log_debug (LOG_ASSEMBLY, "Compressed assembly '%s' is smaller than when the application was built. Adjusting accordingly.", name);
118134
}
@@ -123,13 +139,26 @@ EmbeddedAssemblies::get_assembly_data (uint8_t *data, uint32_t data_size, [[mayb
123139
int ret = LZ4_decompress_safe (data_start, reinterpret_cast<char*>(cad.data), static_cast<int>(assembly_data_size), static_cast<int>(cad.uncompressed_file_size));
124140

125141
if (ret < 0) {
126-
log_fatal (LOG_ASSEMBLY, "Decompression of assembly %s failed with code %d", name, ret);
127-
Helpers::abort_application ();
142+
Helpers::abort_application (
143+
LOG_ASSEMBLY,
144+
Util::monodroid_strdup_printf (
145+
"Decompression of assembly %s failed with code %d",
146+
name,
147+
ret
148+
)
149+
);
128150
}
129151

130152
if (static_cast<uint64_t>(ret) != cad.uncompressed_file_size) {
131-
log_debug (LOG_ASSEMBLY, "Decompression of assembly %s yielded a different size (expected %lu, got %u)", name, cad.uncompressed_file_size, static_cast<uint32_t>(ret));
132-
Helpers::abort_application ();
153+
Helpers::abort_application (
154+
LOG_ASSEMBLY,
155+
Util::monodroid_strdup_printf (
156+
"Decompression of assembly %s yielded a different size (expected %lu, got %u)",
157+
name,
158+
cad.uncompressed_file_size,
159+
static_cast<uint32_t>(ret)
160+
)
161+
);
133162
}
134163
cad.loaded = true;
135164
}
@@ -366,8 +395,14 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string<SENSI
366395
}
367396

368397
if (hash_entry->descriptor_index >= assembly_store.assembly_count) {
369-
log_fatal (LOG_ASSEMBLY, "Invalid assembly descriptor index %u, exceeds the maximum value of %u", hash_entry->descriptor_index, assembly_store.assembly_count - 1);
370-
Helpers::abort_application ();
398+
Helpers::abort_application (
399+
LOG_ASSEMBLY,
400+
Util::monodroid_strdup_printf (
401+
"Invalid assembly descriptor index %u, exceeds the maximum value of %u",
402+
hash_entry->descriptor_index,
403+
assembly_store.assembly_count - 1
404+
)
405+
);
371406
}
372407

373408
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
499534

500535
// This is a coding error on our part, crash!
501536
if (base == nullptr) {
502-
log_fatal (LOG_ASSEMBLY, "Map address not passed to binary_search");
503-
Helpers::abort_application ();
537+
Helpers::abort_application (LOG_ASSEMBLY, "Map address not passed to binary_search");
504538
}
505539

506540
[[maybe_unused]]
@@ -855,8 +889,15 @@ EmbeddedAssemblies::md_mmap_apk_file (int fd, uint32_t offset, size_t size, cons
855889
mmap_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast<off_t>(offsetPage));
856890

857891
if (mmap_info.area == MAP_FAILED) {
858-
log_fatal (LOG_DEFAULT, "Could not `mmap` apk fd %d entry `%s`: %s", fd, filename, strerror (errno));
859-
Helpers::abort_application ();
892+
Helpers::abort_application (
893+
LOG_ASSEMBLY,
894+
Util::monodroid_strdup_printf (
895+
"Could not mmap APK fd %d: %s; File=%s",
896+
fd,
897+
strerror (errno),
898+
filename
899+
)
900+
);
860901
}
861902

862903
mmap_info.size = offsetSize;
@@ -876,10 +917,15 @@ EmbeddedAssemblies::gather_bundled_assemblies_from_apk (const char* apk, monodro
876917
int fd;
877918

878919
if ((fd = open (apk, O_RDONLY)) < 0) {
879-
log_error (LOG_DEFAULT, "ERROR: Unable to load application package %s.", apk);
880-
Helpers::abort_application ();
920+
Helpers::abort_application (
921+
LOG_ASSEMBLY,
922+
Util::monodroid_strdup_printf (
923+
"ERROR: Unable to load application package %s.",
924+
apk
925+
)
926+
);
881927
}
882-
log_info (LOG_ASSEMBLY, "APK %s FD: %d", apk, fd);
928+
log_debug (LOG_ASSEMBLY, "APK %s FD: %d", apk, fd);
883929

884930
zip_load_entries (fd, apk, should_register);
885931
}

src/native/monodroid/mono-image-loader.hh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "xxhash.hh"
1515
#include "search.hh"
1616
#include "strings.hh"
17+
#include "util.hh"
1718

1819
#if defined (RELEASE)
1920
#define USE_CACHE 1
@@ -116,8 +117,13 @@ namespace xamarin::android::internal {
116117
#if defined (USE_CACHE)
117118
ssize_t index = find_index (hash);
118119
if (index < 0) {
119-
log_fatal (LOG_ASSEMBLY, "Failed to look up image index for hash 0x%zx", hash);
120-
Helpers::abort_application ();
120+
Helpers::abort_application (
121+
LOG_ASSEMBLY,
122+
Util::monodroid_strdup_printf (
123+
"Failed to look up image index for hash 0x%zx",
124+
hash
125+
)
126+
);
121127
}
122128

123129
// We don't need to worry about locking here. Even if we're overwriting an entry just set by another

src/native/monodroid/mono-log-adapter.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ MonodroidRuntime::mono_log_handler (const char *log_domain, const char *log_leve
4343

4444
__android_log_write (prio, log_domain, message);
4545
if (fatal) {
46-
Helpers::abort_application ();
46+
Helpers::abort_application (message);
4747
}
4848
}
4949

0 commit comments

Comments
 (0)