edgefirst_tensor/covguard.rs
1// SPDX-FileCopyrightText: Copyright 2025 Au-Zone Technologies
2// SPDX-License-Identifier: Apache-2.0
3
4//! Coverage-capture resilience for crash-on-shutdown hardware (e.g. the i.MX
5//! Vivante EGL driver SIGABRTs during teardown after tests pass). Under
6//! coverage instrumentation only, install a SIGABRT handler that flushes the
7//! LLVM profile to LLVM_PROFILE_FILE *before* the abort completes, then
8//! restores the default disposition and re-raises so the process exit status
9//! is unchanged (the CI workflow already treats JUnit as the source of truth).
10//!
11//! No-op unless built with `-Cinstrument-coverage` (the `coverage` cfg, set by
12//! build.rs) on Linux. Never installed in shipped release builds.
13
14/// Install the coverage flush-on-abort handler. Idempotent; safe to call from
15/// multiple artifact constructors. No-op outside instrumented Linux builds.
16pub fn install() {
17 #[cfg(all(coverage, target_os = "linux"))]
18 {
19 use std::sync::Once;
20 static ONCE: Once = Once::new();
21 ONCE.call_once(|| unsafe {
22 libc::signal(
23 libc::SIGABRT,
24 flush_then_reraise as extern "C" fn(libc::c_int) as libc::sighandler_t,
25 );
26 });
27 }
28}
29
30#[cfg(all(coverage, target_os = "linux"))]
31extern "C" fn flush_then_reraise(_sig: libc::c_int) {
32 extern "C" {
33 // Provided by the LLVM profiling runtime under -Cinstrument-coverage.
34 fn __llvm_profile_write_file() -> libc::c_int;
35 }
36 // SAFETY: async-signal context. The profile writer is the established
37 // crash-coverage path; signal()/raise() are async-signal-safe. Best-effort:
38 // the heap may already be corrupt, but this is strictly better than losing
39 // all coverage. We do NOT _exit(0) — re-raising preserves the exit status.
40 unsafe {
41 __llvm_profile_write_file();
42 libc::signal(libc::SIGABRT, libc::SIG_DFL);
43 libc::raise(libc::SIGABRT);
44 }
45}