Skip to content

fix(android): keep native tombstones and record runtime crash breadcrumbs - #129

Open
ammarahm-ed wants to merge 1 commit into
perf/jni-09-bulk-arraysfrom
fix/android-crash-breadcrumbs
Open

ammarahm-ed wants to merge 1 commit into
perf/jni-09-bulk-arraysfrom
fix/android-crash-breadcrumbs

Conversation

@ammarahm-ed

Copy link
Copy Markdown
Collaborator

Port of NativeScript/android#2007 to both the Node-API and the direct runtime.

SIG_handler (here SIGHandler.h) was installed for SIGABRT and SIGSEGV and threw a NativeScriptException from inside the signal handler. The unwinder cannot cross the kernel's signal frame, so the throw never reached a catch block, and installing a SIGSEGV handler displaced debuggerd, so no tombstone was written. The handler destroyed the diagnostics and the process died anyway.

CrashBreadcrumbs replaces it in both runtime/android/napi/sighandler/ and runtime/android/jsi/sighandler/, as the upstream code with no changes:

  • The handler never throws. It writes a pre-rendered breadcrumb with two pwrite(2) calls to a file opened at startup (files/.ns-crash-breadcrumb), then restores the previous disposition and returns, so the faulting instruction re-faults and debuggerd writes the tombstone with the kernel's original siginfo. Signals raised by abort or kill (si_code <= 0) are re-raised instead. It is installed for SIGSEGV, SIGABRT, SIGBUS, SIGILL and SIGFPE with SA_SIGINFO | SA_ONSTACK.
  • The breadcrumb names each live runtime's id, thread id, whether it is the main runtime or a worker, the worker's script and the module it is loading. It is re-rendered into a double buffer on ordinary threads whenever a runtime starts or stops, a worker starts, or a module load begins or ends (ModuleScope, which also restores the enclosing module on throw paths).
  • The next process reads the file, logs it as TNS.Native at error level (not fatal, which would claim bionic's one abort-message slot), and truncates it.

std::set_terminate(LogAndAbortUncaught) is unchanged. The disabled spec that expected a catchable SIGABRT exception is removed, and so is m_androidVersion, which only the old API-level check used.

Results

Debug build on a Sony SO-01M (Android 11, arm64), V8, both trees. The app runs the benchmark worker; kill -11 or kill -6 is sent to the app process.

main this PR
Process after SIGSEGV exits exits
Tombstone (DEBUG lines with signal 11 and a backtrace) none written
Breadcrumb file not created 190 bytes
JNI Exception occurred thrown from the handler yes no
Next launch logs the runtime state no yes
SIGABRT (kill -6), same four rows same as SIGSEGV same as SIGSEGV

What the next launch logged, in both trees:

The previous process was killed by a fatal signal. Runtime state recorded at that moment (match tid against the tombstone in /data/tombstones):
fatal signal 11 on tid 4482
NativeScript runtime state (pid 4482):
  runtime=0 tid=4482 main module=<none>
  runtime=1 tid=4537 worker script=./marshalling-benchmark-worker.js module=<none>

The Jasmine suite on a debug build of this branch, same device:

main this PR
Node-API 523 specs, 0 failures 522 specs, 0 failures
Direct 451 specs, 1 failure (triggers interval, a timing spec) 450 specs, 0 failures

The spec count is one lower because the disabled spec is removed. The suite ran on this branch with the PRs below it in the stack.

Not verified

  • The signals were sent with kill, which exercises the re-raise path. A real fault that re-executes the faulting instruction (SEGV_MAPERR) and a crash while a module is loading were not induced, so module= was <none> in every run above.
  • Only V8 was built and run. JSC installs its own SIGSEGV handler for normal operation; the old code left SIGSEGV alone on JSC for that reason. This handler is installed first and chains to whatever was there before, so it should be safe, but it has not been run on JSC, Hermes, QuickJS or PrimJS.
  • One device, one Android version.
  • Fatal signals now surface as real native crashes instead of an exception thrown from the handler, as in the upstream PR. There is no opt-out, for the same reason given there.

@coderabbitai

coderabbitai Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 01cf6bb4-9b65-4af8-ac5e-fbedf53d147f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@ammarahm-ed
ammarahm-ed added this pull request to stack #121 November 9, 2026 07:25

This branch has not been deployed

No deployments
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.

1 participant