Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 0 additions & 86 deletions build_test/CMakeCache.txt

This file was deleted.

1 change: 0 additions & 1 deletion build_test/CMakeFiles/cmake.check_cache

This file was deleted.

Binary file removed build_test/archive.zip
Binary file not shown.
8,959 changes: 0 additions & 8,959 deletions build_test/native_crash.log

This file was deleted.

21 changes: 15 additions & 6 deletions osu.Android/Native/crash_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,17 @@ static void logcatWrite(const char* msg) {
// To enable the perfmap output (step 1) for a build:
// - Add an `AndroidEnvironment` text file to the project containing:
// MONO_ENV_OPTIONS=--jitmap
// TMPDIR=/storage/emulated/0/Android/data/<pkg>/files
// so Mono writes the perfmap into the same dir as `native_crash.log`.
// `crash_handler.cpp` searches that dir, plus `/tmp` and `/data/local/tmp`,
// plus the directory containing `g_logPath`.
// TMPDIR=/data/user/0/<pkg>/cache
// TMPDIR MUST point at the app's internal storage (not the external
// /storage/emulated/... path): Realm calls mkfifo() under TMPDIR for its
// cross-process notifier, and FUSE-backed external storage rejects
// mkfifo() with EACCES, which crashes Realm.GetInstance() at startup.
// Internal storage (ext4/f2fs) supports FIFOs. The crash handler then
// reads `getenv("TMPDIR")` at signal time to locate the perfmap and
// emits the symbolicated frames into native_crash.log (which lives in
// the external files dir and IS user-retrievable).
// `crash_handler.cpp` also searches `/tmp`, `/data/local/tmp`, and the
// directory containing `g_logPath` as fallbacks.
//
// Async-signal safety:
// - All file I/O uses open/read/close (signal-safe).
Expand Down Expand Up @@ -255,8 +262,10 @@ static bool ensurePerfmapLoaded() {
nameBuf[np] = '\0';

// Candidate directories, in priority order. The dir containing g_logPath
// is checked first so a build that sets `TMPDIR=<external-files-dir>`
// (the recommended config) finds its perfmap immediately.
// (external files dir) is checked first as a historical fallback, but
// current builds set TMPDIR to the app's internal cache dir (FUSE-backed
// external storage cannot host the FIFOs Realm needs — see mono.env), so
// the perfmap normally lives at $TMPDIR/perf-<pid>.map.
Comment on lines 264 to +268

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With TMPDIR now recommended to point to internal cache, ensurePerfmapLoaded() will usually fail the first lookup attempt in the external-files log directory before trying $TMPDIR. Since this runs in the crash handler, consider reordering the candidate directories to check getenv("TMPDIR") first (and only fall back to the log dir), to reduce crash-time syscalls and simplify the “priority order” comment.

Copilot uses AI. Check for mistakes.
const char* tmpEnv = getenv("TMPDIR");
char logDir[kMaxLogPathLen] = {};
if (g_logPath[0] != '\0') {
Expand Down
29 changes: 22 additions & 7 deletions osu.Android/mono.env
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,26 @@
# crash backtraces. Has negligible runtime overhead (a single fprintf per
# JIT compile, which already happens infrequently after warm-up).
#
# TMPDIR=/storage/emulated/0/Android/data/sh.ppy.osulazer/files
# Redirects Mono's perfmap output into the same external-files directory
# that we already write native_crash.log to. This is the *only* path that
# is (a) writable by the app, (b) readable post-mortem by the user via
# the Files app on an unrooted device, and (c) survives app uninstall on
# most devices' "external files" semantics.
# TMPDIR=/data/user/0/sh.ppy.osulazer/cache
# Redirects Mono's temp-file output (and, transitively, anything that goes
# through .NET's Path.GetTempPath()) into the app's *internal* cache dir.
#
# IMPORTANT: this MUST point at internal storage, not at the external
# /storage/emulated/... path. External storage on modern Android is a
# FUSE/sdcardfs mount, and the Linux kernel disallows mkfifo() on FUSE
# mounts (returns EACCES/13). RealmAccess.getConfiguration() sets
# FallbackPipePath = Path.GetTempPath() + "/lazer", and Realm calls
# mkfifo() there to create its cross-process notifier (.cv / .note files);
# pointing TMPDIR at external storage causes Realm.GetInstance() to throw
# "Failed to create fifo … Permission denied" on every startup, before the
# managed file logger is up. An internal path (ext4/f2fs-backed) supports
# FIFOs and is also writable by the app without runtime permissions.
#
# Side-effect: perf-<pid>.map is now in internal cache and is not directly
# retrievable by the user on an unrooted device. That's fine — it is only
# consumed by our in-process native crash handler at crash time (which
# reads getenv("TMPDIR") to find it), and the symbolicated output it
# produces is written into native_crash.log in the external files dir,
# which IS user-retrievable.
MONO_ENV_OPTIONS=--jitmap
TMPDIR=/storage/emulated/0/Android/data/sh.ppy.osulazer/files
TMPDIR=/data/user/0/sh.ppy.osulazer/cache
7 changes: 4 additions & 3 deletions osu.Android/osu.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@
</ItemGroup>
<!-- Mono runtime environment variables baked into the APK. Enables Mono's
jitmap output (read by Native/crash_handler.cpp's resolveViaPerfmap()
to symbolicate JIT frames in native_crash.log) and redirects TMPDIR so
the perfmap lands in the app's external-files dir (same place as the
crash log). See mono.env for details. -->
to symbolicate JIT frames in native_crash.log) and redirects TMPDIR
to the app's internal cache dir. TMPDIR must NOT point at external
(FUSE-backed) storage — that breaks Realm's mkfifo() notifier.
See mono.env for the full rationale. -->
<ItemGroup>
<AndroidEnvironment Include="mono.env" />
</ItemGroup>
Expand Down
Loading