Fault-Model Traceability Matrix

June 9, 2026 ยท View on GitHub

When to read this: When changing high-risk timing semantics, reviewing a fault-model audit, or deciding whether a proposed fix belongs in runtime code, tests, docs, or a follow-up issue.

Fault-Model Traceability Matrix

This matrix is a maintainer traceability artifact for issue #309 under the release-readiness umbrella #302. It does not prescribe new runtime behavior. Use it to keep high-consequence timing failure modes connected to their current guards, evidence homes, and explicit support boundaries before editing the core stop/repair path, MPI descriptor exchange, or MPI+OpenMP summary code.

Fault categoryTriggerUser-visible symptom if brokenExisting guardTest or doc homeMissing counterexample or non-goal
Strict nesting and repairstop() names a timer that is not the current stack top.Times are attributed to the wrong parent context, active stacks are corrupted, or repair creates timing gaps.Strict mode leaves state unchanged; warn/repair modes use one captured timestamp, unwind to the target, stop it, and restart unwound timers in reverse. Repair-restarted timers intentionally resume timing without counting a new user start.src/ftimer_core.F90; tests/test_nesting.pf; docs/semantics.md "Mismatch Handling" and "Local Summary Contract".Non-goal: do not simplify the stop/repair path without a concrete bug or characterization gap. #314 and #316 should preserve this guardrail.
Callback suppression during repairWarn/repair mismatch recovery unwinds and restarts internal activations.External observers see phantom start/stop events and infer user work that did not happen.Callback hooks fire only for normal user-visible start/stop events; internal repair transitions are callback-silent.src/ftimer_core.F90; tests/test_callbacks.pf; docs/semantics.md "Callback Contract"; docs/design.md callback design note.Non-goal: callbacks are lightweight intra-run hooks, not a stable profiler-backend event stream or semantic id map.
Active local snapshots versus stopped-run reductionsA caller requests a local summary while timers are active, or requests MPI/OpenMP summaries before stopping all work.A final report silently includes partial active intervals, or a collective summary mixes incomplete rank/lane data.Local summaries are live snapshots with has_active_timers and per-entry is_active; strict MPI, sparse MPI, local OpenMP, strict hybrid, and sparse hybrid summaries are stopped-run preflight points that reject active timers.docs/semantics.md "Local Summary Contract", "MPI Guarantees", and OpenMP summary sections; tests/test_summary.pf; tests/mpi/test_mpi_summary.pf; tests/test_openmp_summary_smoke.F90; tests/test_openmp_mpi_summary_smoke.F90.Non-goal: local get_summary() is not changed into a stopped-run-only API.
Nonmonotonic custom or backend clocksAn injected clock or processor clock moves backward, wraps, or mixes epochs after data exists.Negative elapsed time or misleading % Total denominators appear rather than a hidden corrected interval.Clock changes are rejected after timing data exists; successful no-data clock changes restart the summary window. fTimer assumes monotonic clocks and does not clamp backward movement or synthesize corrected elapsed time.src/ftimer_core.F90; docs/semantics.md "Default serial system_clock assumptions" and "Clock Configuration Contract"; README.md operational notes.Missing counterexample: no dedicated behavioral test asserts that a deliberately backward mock clock remains visible as negative elapsed time; add only through a follow-up if maintainers want that contract pinned.
MPI descriptor mismatchCommunicator ranks materialize different timer names, parent stacks, or context trees for strict MPI summary/report calls.MPI reductions combine unrelated entries, hang, or return plausible but wrong global statistics.Strict MPI summary performs descriptor-hash preflight across the init communicator before reducing timing fields and returns FTIMER_ERR_MPI_INCON on mismatch.src/ftimer_mpi.F90; tests/mpi/test_mpi_consistency.pf; tests/mpi/test_mpi_summary.pf; docs/history/mpi-descriptor-preflight-decision.md; docs/semantics.md "MPI Guarantees".Non-goal: do not replace or refactor the descriptor exchange from #309. #314/#316 must not weaken this guardrail.
MPI communicator lifetime and agreementA timer captures a freed subcommunicator, uses MPI-enabled fTimer outside MPI_Init/MPI_Finalize, or different rank groups enter different communicators.Undefined MPI behavior or a hang instead of an fTimer status.Public API captures mpi_f08 type(MPI_Comm) handles as non-owning borrowed communicators and documents consistent-communicator lifetime as caller-owned.docs/semantics.md "MPI Guarantees" and "MPI lifecycle and communicator ownership"; docs/troubleshooting.md; tests/check_mpi_lifecycle_contract_docs.cmake; README.md operational notes.Non-goal: no cross-communicator rescue rendezvous or communicator ownership/duplication is promised.
Sparse participation semanticsRank- or lane-conditional timers are summarized through sparse MPI or sparse hybrid APIs.Absent contributors are mistaken for present zero work, or strict APIs silently relax.Sparse APIs are separate result/report/CSV families with explicit participation and missing counts; statistics are over participating contributors only. Strict APIs keep identical-descriptor requirements.src/ftimer_mpi.F90; src/ftimer_openmp.F90; tests/mpi/test_mpi_summary.pf; tests/test_openmp_mpi_summary_smoke.F90; docs/history/mpi-sparse-summary-decision.md; docs/semantics.md sparse MPI and sparse hybrid sections.Non-goal: no all-rank zero-filled or amortized sparse fields are part of the current result model.
Legacy OpenMP worker no-op compatibilityExisting ftimer or ftimer_core APIs are called from non-master OpenMP threads when FTIMER_USE_OPENMP=ON.Worker-only timers vanish, call counts are master-only, or users infer per-thread timing that was never recorded.Guarded core operations run only on thread 0; worker calls are skipped before validation, emit no stderr, and leave caller-provided ierr unchanged.src/ftimer_core.F90; tests/test_openmp_guards.pf; examples/openmp_example.F90; docs/openmp-timing-modes.md; docs/semantics.md "Consequences for timing data".Non-goal: this compatibility path is not general thread safety or true worker timing; use ftimer_openmp_t for that.
Strict versus sparse hybrid lane/rank mismatchesMPI+OpenMP ranks or eligible lanes differ in descriptors, domains, lane participation, or mixed OpenMP epoch team-size precision.Missing lane/rank data is zero-filled under a strict result, or sparse summaries lose explicit missing-contributor meaning.Strict hybrid summaries reject descriptor, eligible-lane, and unknown missing-lane precision mismatches with FTIMER_ERR_MPI_INCON; sparse hybrid summaries are a separate union API with rank/lane participation metadata, participating-sample statistics, and explicit unknown missing-sample flags.src/ftimer_openmp.F90; tests/test_openmp_mpi_summary_smoke.F90; tests/test_openmp_mpi_summary_3rank_smoke.F90; tests/check_openmp_mpi_union_descriptor_contract.cmake; docs/openmp-timing-modes.md; docs/semantics.md strict and sparse hybrid sections.Non-goal: do not replace sparse participation metadata with all-rank/lane zero-filled fields; mixed OpenMP epochs keep aggregate eligible counts but must expose unknown missing-lane precision.
Scoped-guard ownership versus public idsA guard activation is manually stopped, repaired away, copied, or confused with a cached lookup id.Guard finalization stops the wrong activation or hides a mismatch.Guards store internal segment indexes plus activation tokens; public cached lookup ids are opaque handles. guard%stop(ierr) may stop only the exact activation it started.src/ftimer_core.F90; src/ftimer_types.F90; tests/test_scoped_guard.pf; tests/test_oop_scoped_guard.pf; docs/semantics.md "Scoped Guard Contract"; README.md API notes.Non-goal: ftimer_scope_id remains deferred; guard internals are unstable public-by-necessity, not downstream API.
Worker diagnostic draining pathsftimer_openmp_t observes invalid worker calls and later reaches a serial lifecycle or summary/report call.Diagnostics are lost, emitted out of order from workers, or double-reported on collectives.Worker paths queue bounded diagnostics. Serial explicit-ierr draining returns the first status without stderr; omitted-ierr paths emit aggregate diagnostics, while collective MPI+OpenMP summary diagnostics are intentionally rank-gated when status will be surfaced elsewhere.src/ftimer_openmp.F90; tests/test_openmp_api_diagnostics.F90; tests/test_openmp_api_ierr_silence.F90; tests/check_openmp_api_diagnostics.cmake; tests/check_openmp_api_ierr_silence.cmake; docs/semantics.md OpenMP object contract.Non-goal: do not unify explicit and silent diagnostic paths unless a concrete user-facing ambiguity is demonstrated.

Maintenance Notes

  • Treat this page as a map to existing evidence, not a replacement for docs/semantics.md.
  • If a row exposes missing evidence, file a focused follow-up before adding runtime changes. #317 owns mixed OpenMP epoch / eligible-lane interpretation evidence and records that those aggregate eligible counts carry explicit unknown missing-lane precision when team sizes differ across epochs.
  • Keep #314 and #316 refactors documentation-preserving: they may reduce duplication, but should not weaken the core stop/repair or descriptor preflight guardrails captured above.