Repository navigation
Conversation
7ea304b to
11f94c3
Compare
6460208 to
527d4dc
Compare
d9b9c4f to
9a72f7e
Compare
9a72f7e to
8a8f24f
Compare
ede6e89 to
fb847b2
Compare
7ad144a to
9abff94
Compare
73c0dcf to
39ea704
Compare
* feat(native): Support ELF modules at non-zero file offsets The minidump writer and `capture_modules_from_proc_maps()` grouped `/proc/<pid>/maps` entries by file name and expected an ELF header at offset 0. An ELF image can also start inside a larger file, such as an uncompressed library that Android maps straight from an APK, and such images were dropped from the module list. Start a module at each mapping whose file offset holds an ELF header, group the image's other segments by its load range, and read the build ID, size, and SONAME at that offset. Move `compute_elf_size_from_phdrs()` to `sentry_elf.h` to share it. This prepares the native backend for Android, where AGP's default packaging maps every app library from the APK. Ref: #1725 * fix(native): Apply the ELF offset to the build ID section headers The previous commit adjusted the section header offset in `elf_locate_symtab()` instead of `extract_elf_build_id_for_module()`, where `elf_offset` is not declared, so the crash daemon failed to compile. Move the adjustment to where the build ID is read. * fix(native): Keep embedded ELFs mapped below their file offset The `offset > start` guard in `capture_modules_from_proc_maps()` dropped mappings before an ELF image inside a larger file could be detected. The guard only protects the `start - offset` load base of a regular ELF file, so apply it there instead.
* feat(native): Support ELF modules at non-zero file offsets The minidump writer and `capture_modules_from_proc_maps()` grouped `/proc/<pid>/maps` entries by file name and expected an ELF header at offset 0. An ELF image can also start inside a larger file, such as an uncompressed library that Android maps straight from an APK, and such images were dropped from the module list. Start a module at each mapping whose file offset holds an ELF header, group the image's other segments by its load range, and read the build ID, size, and SONAME at that offset. Move `compute_elf_size_from_phdrs()` to `sentry_elf.h` to share it. This prepares the native backend for Android, where AGP's default packaging maps every app library from the APK. Ref: #1725 * fix(native): Apply the ELF offset to the build ID section headers The previous commit adjusted the section header offset in `elf_locate_symtab()` instead of `extract_elf_build_id_for_module()`, where `elf_offset` is not declared, so the crash daemon failed to compile. Move the adjustment to where the build ID is read. * fix(native): Keep embedded ELFs mapped below their file offset The `offset > start` guard in `capture_modules_from_proc_maps()` dropped mappings before an ELF image inside a larger file could be detected. The guard only protects the `start - offset` load base of a regular ELF file, so apply it there instead. * feat(unwinder): Unwind remote threads with libunwindstack The crash daemon could only unwind the threads that did not crash with `libunwind_remote`, which Android builds lack because they use libunwindstack. Add a `libunwindstack_remote` backend, compiled in only with the native backend, that attaches to a thread with ptrace, reads its registers with `Regs::RemoteGet()`, and unwinds it. Share the ptrace attach loop with `libunwind_remote`. This prepares the native backend for Android. * feat(native): Support preloading the crash handler `sentry__backend_preload()` was a no-op for the native backend. Install its signal handlers early, so that they keep their place in the signal chain ahead of a managed runtime, and let a later `sentry_init()` adopt them. Like with `inproc`, shutdown keeps the preloaded handlers installed, forwarding crashes to the previous handlers until the next initialization.
…2273) Allow `SENTRY_HANDLER_STRATEGY_CHAIN_AT_START` in the native backend on Android, as in the inproc backend, so that a signal that Mono turns into a managed exception, such as a `NullReferenceException`, is not captured as a native crash. As in the inproc backend, fall back to the default strategy below API 26, where debuggerd kills the process after the chained handler runs. This prepares the native backend for Android support (#1725).
Add `sentry__http_transport_set_dump_failed()`. When enabled, a network error makes the HTTP transport write the envelope to the run folder, like the shutdown dump does, instead of queuing it for retry or discarding it, so that the next launch sends it. This prepares the Android crash daemon to keep crash envelopes that fail to upload (#1725).
39ea704 to
ef29393
Compare
Add `sentry__http_transport_set_dump_failed()`. When enabled, a network error makes the HTTP transport write the envelope to the run folder, like the shutdown dump does, instead of queuing it for retry or discarding it, so that the next launch sends it. This prepares the Android crash daemon to keep crash envelopes that fail to upload (#1725).
Run the crash daemon on Android in one of two ways. With the Sentry Android SDK, its crash service hands the IPC fds to the app with `sentry_android_crash_daemon_init()` and runs the daemon from `libsentry.so` with `sentry_android_crash_daemon_run()`. Otherwise, the backend forks and execs `libsentry-crash.so` next to `libsentry.so`, like the `sentry-crash` executable on Linux. Share the Linux crash IPC, but back the shared memory with a file in the database directory and guard initialization with a mutex, because bionic has no `shm_open()` or `sem_open()`. Before notifying the daemon, the crash handler makes the app dumpable and allows any ptracer, as debuggerd does, because the kernel otherwise denies the daemon `ptrace`, `process_vm_readv` and `/proc` access to non-debuggable apps. The service-hosted daemon writes the crash envelope to the run folder and releases the crashed process so that the platform can write a tombstone. It returns a handle that defers the upload to the service, which can merge the tombstone into the envelope first, and keeps the envelope for the next launch if the upload fails. The native backend on Android is experimental.
Add the opt-in `sentry-native-ndk-outproc` package, which builds the same Java and JNI sources as `sentry-native-ndk` with the experimental native backend instead of inproc. Both provide `libsentry.so` and must not be used together. Android SDK apps select it through the Sentry Gradle plugin. Expose the crash daemon to the sentry-java crash service through `SentryNdk.initCrashDaemon()` and `SentryNdk.runCrashDaemon()`. The returned `CrashDaemon` owns the daemon handle. `sendEnvelope()` uploads the possibly tombstone-merged envelope and reports whether it was sent, kept for the next launch or neither, and `close()` discards the envelope if nothing was sent. Upload through a `sentry_http_client_t` that calls `HttpURLConnection` over JNI, so the crash service uses the platform network and TLS stack instead of a curl build. Use `SENTRY_CRASH_REPORTING_MODE_NATIVE`, like inproc, because a minidump takes precedence over the native stacktrace during processing and makes crash envelopes about 3 MB. Package `libsentry-crash.so` only for the instrumented tests, which fork/exec it, and run them in the Android CI job.
Add an Android job to the e2e workflow. It builds a fixture app against the local native and Java SDKs in an isolated Maven repository and runs it on an API 36 emulator. Verify native crash events with and without tombstone merging, for debuggable and non-debuggable builds, including threads, debug images, user/release metadata, and Java/native frames in merged stacks. Verify that a process that a broadcast starts in the background gets the crash service, and that the service survives more than a minute in the background. The fixture uses default packaging, so native libraries stay inside the APK as in consuming apps. Skip the standalone e2e tests on Android, since they cannot send events without the http transport.
ef29393 to
8ecb9a7
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8ecb9a7. Configure here.
| } else { | ||
| sentry_envelope_free(envelope); | ||
| envelope = NULL; | ||
| } |
There was a problem hiding this comment.
Standalone Android crashes drop envelopes
High Severity
After writing the crash envelope, the Android path always waits for a {envelope}.merged tombstone file and then frees the original envelope if that file never appears. The service-hosted daemon can defer upload to Java, but the standalone daemon (daemon is NULL) has no merger, so the wait times out and sentry__capture_envelope is skipped. Out-of-process Android crashes therefore never leave the daemon over HTTP.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 8ecb9a7. Configure here.


Warning
WIP 🚧🔨⏳⛔
sequenceDiagram participant App participant Service participant Android participant Sentry App->>Service: Start at SDK init Note over App: Native crash App->>Service: Notify crash activate App Service->>App: Capture crash deactivate App activate Service App->>Android: Propagate crash Note over Android: Write tombstone Android-xApp: Terminate process deactivate Service Note over Service: Merge tombstone Service->>Sentry: Send reportSee also:
Close: #2115