Skip to content

Make Android startup crashes diagnosable: keep native symbols + in-process crash dumper to user-accessible file - #229

Merged
winnerspiros merged 2 commits into
masterfrom
copilot/fix-crash-issues
Apr 22, 2026
Merged

Make Android startup crashes diagnosable: keep native symbols + in-process crash dumper to user-accessible file#229
winnerspiros merged 2 commits into
masterfrom
copilot/fix-crash-issues

Conversation

Copilot AI commented Apr 22, 2026

Copy link
Copy Markdown

v148 still crashes ~5s into startup on SDLThread with signal 11 / pc=0x0, and the only artifact available is the unsymbolicated 2-frame summary from a Play Store crash viewer. Five prior PRs guessed at fixes against this same opaque tombstone; none stuck. This PR fixes the diagnostics gap so the next occurrence pinpoints the actual call site.

Tombstone re-read (registers, no symbols)

  • pc=0 + x21=0xb400… (Scudo heap tag) ⇒ indirect call through a NULL function-pointer slot on a heap object.
  • lr=0x74b7012738 lies in a system-library mapping range (libvulkan / libsdl3 / libaaudio) — not libosu_native.so.
  • 5 s on SDLThread aligns with Veldrid Vulkan instance/device/swapchain creation (Vulkan is the Android default on this fork).

Most likely cause: vkGet{Instance,Device}ProcAddr returning NULL for an extension entry point that gets called blindly. That's framework/Veldrid/driver territory — confirming it requires a real backtrace.

Changes

  • Stop stripping libosu_native.so — drop -s from CMAKE_SHARED_LINKER_FLAGS_RELEASE. --gc-sections still removes unreferenced code; only the symbol table is retained, so dladdr (and any third-party tombstone viewer) can resolve PCs to function names. Cost: a few hundred KB on-device; no impact on APK download size with LZ4 assembly compression.

  • New osu.Android/Native/crash_handler.{cpp,h} — async-signal-safe handler for SIGSEGV/SIGBUS/SIGILL/SIGFPE/SIGABRT:

    • Runs on a 64 KB sigaltstack (survives stack overflow).
    • Walks the stack with _Unwind_Backtrace, resolves frames via dladdr (lib + sym + offset).
    • Dumps signal info, all 31 GPRs + sp/pc/pstate, and the backtrace to both logcat (tag osu!crash) and <external-files-dir>/native_crash.log — reachable from Android's Files app on unrooted devices.
    • Uses only write/open/close/_Unwind_Backtrace/dladdr/__android_log_write; no malloc, no stdio, no locale. Re-entrancy guarded.
    • Restores and chains to the previous handler (debuggerd) so the system tombstone is still produced.
  • WiringCMakeLists.txt builds crash_handler.cpp and explicitly links libdl. OboeAudioBridge.cs exposes nInstallCrashHandler (UTF-8 string P/Invoke). OsuGameActivity.OnCreate calls it as the first action after base.OnCreate, fully wrapped so a failure here cannot itself contribute to a startup crash:

try
{
    string? crashLogPath = null;
    try
    {
        var dir = GetExternalFilesDir(null);
        if (dir != null && !string.IsNullOrEmpty(dir.AbsolutePath))
            crashLogPath = System.IO.Path.Combine(dir.AbsolutePath, "native_crash.log");
    }
    catch (Exception e) { Debug.WriteLine($"[osu!] Could not resolve external files dir for crash log: {e.Message}"); }

    OboeAudioBridge.nInstallCrashHandler(crashLogPath);
}
catch (Exception e) { Debug.WriteLine($"[osu!] Failed to install native crash handler: {e.Message}"); }

What the next crash will produce

Android/data/sh.ppy.osulazer/files/native_crash.log (also in logcat under osu!crash):

[osu!] NATIVE CRASH
  signal      = SIGSEGV (11)
  si_code     = SEGV_MAPERR (address not mapped) (1)
  fault_addr  = 0x0000000000000000
  thread_tid  = 19486   pid = 19439   thread_name = SDLThread
Registers:
  x0=0x... x1=0x... x2=0x... x3=0x...
  ...
  sp=0x...  pc=0x0000000000000000  pstate=0x...
Backtrace:
  #00 pc 0x...  /system/lib64/libvulkan.so (vkAcquireNextImageKHR+0x...)
  #01 pc 0x...  /vendor/lib64/hw/vulkan.adreno.so (...+0x...)
  #02 pc 0x...  /data/.../libVeldrid.Vulkan.so (...)
  ...

That is sufficient to either land a one-line fix in this repo or, if confirmed to be in Veldrid/framework as suspected, point at the exact call site to patch in winnerspiros/osu-framework.

Deliberately not done

No further speculative startup-path edits. With five prior PRs unable to fix the crash from an opaque tombstone, additional guessing is lower-value than a symbolicated backtrace.

Copilot AI and others added 2 commits April 21, 2026 22:14
…tones

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/6792646b-591b-4936-be8f-3b37c75f326f

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
…er-accessible file

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/32bf9963-5972-40a3-829b-d3f16f69ad4f

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
@winnerspiros
winnerspiros marked this pull request as ready for review April 22, 2026 06:08
Copilot AI review requested due to automatic review settings April 22, 2026 06:08
@winnerspiros
winnerspiros merged commit 0bb4c24 into master Apr 22, 2026
5 of 19 checks passed
@gitar-bot

gitar-bot Bot commented Apr 22, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR improves Android native-crash diagnosability by keeping native symbols in release builds and adding an in-process native crash handler that writes a symbolicated backtrace (plus registers/signal info) to both logcat and a user-accessible file.

Changes:

  • Install a native signal handler early in OsuGameActivity.OnCreate() to capture SIGSEGV/SIGBUS/SIGILL/SIGFPE/SIGABRT and dump crash details.
  • Add new native crash handler implementation (crash_handler.{cpp,h}) and expose it via P/Invoke (OboeAudioBridge.nInstallCrashHandler).
  • Adjust native build flags to avoid stripping (-s) and explicitly link libdl for dladdr().

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
osu.Android/OsuGameActivity.cs Installs the native crash handler early and best-effort.
osu.Android/Native/crash_handler.h Declares nInstallCrashHandler() native entry point.
osu.Android/Native/crash_handler.cpp Implements signal handler, register dump, unwinding, dladdr symbol resolution, and file/logcat output.
osu.Android/Native/OboeAudioBridge.cs Adds P/Invoke declaration for nInstallCrashHandler.
osu.Android/Native/CMakeLists.txt Stops stripping native symbols and links crash handler + dl.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +173 to +178
writeHex64(st->fd, (uint64_t)off, 1);
writeStr(st->fd, ")");
} else if (info.dli_fbase) {
uintptr_t off = pc - (uintptr_t)info.dli_fbase;
writeStr(st->fd, " (lib+0x");
writeHex64(st->fd, (uint64_t)off, 1);
Comment on lines +306 to +309
if (g_inHandler) {
signal(sig, SIG_DFL);
raise(sig);
return;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants