Skip to main content

Module monitor

Module monitor 

Source
Expand description

RFC-0052 / phase-296 W3b.4/.5 — on-target contract monitors.

The baked shape mirrors Phase 211.H’s qos_overrides: codegen emits a &'static [MonitorSpec] table (plus one static PubMonitorCell per contracted publisher) from the SystemModel’s contract layer; the entry installs it on the executor before entity creation. An uncontracted image bakes an empty table — every path below dead-code-eliminates.

Publish counting is an atomic bump on the publisher handle (no clock, no lock on the hot path); the rate check runs on spin ticks over a ~RATE_CHECK_INTERVAL_US window and pushes violations into a small ring the entry glue drains into the nros-diagnostics reporter.

W3b.5 adds three more rules on the same drain:

  • max-age-runtime — subscriber take-age (epoch_now - header.stamp peeked from the raw CDR buffer at RosMessage::STAMP_OFFSET, recorded into a SubMonitorCell on the take path).
  • max-latency-runtime — node-path (take → publish) latency: the dispatch elapsed time is attributed to every monitored publisher whose counter advanced during that dispatch (an upper bound on take → publish, measured on the executor’s monotonic clock). The span is callback entry to exit, for every dispatch kind: a timer callback is timed whether it fires in the drains or in the trigger-miss sweep (phase-474 I6), and a service call it makes is inside the span. The timer’s release jitter (a late start) is not part of it; that is release-jitter-runtime.
  • deadline-miss-runtime — a dispatched callback ran past its bound SchedContext’s deadline_us; what ELSE happens is the tier’s DeadlineAction.

phase10-B3 adds the route rule beside it:

  • route-latency-runtime – a node path whose trigger is an INPUT is budgeted from the TAKE of that input to its output, and on a node that reads its input on a timer tick that span includes the wait for the next tick. max-latency-runtime times one dispatch and cannot see the wait, so a route made of a long tick wait and a slow dispatch could exceed its budget while every callback passed. The executor stamps the dispatch that takes the trigger input (the earliest take not yet answered) and, at the end of a dispatch that published one of the path’s outputs, judges take-to-dispatch-end against the path’s max_latency_ms (route_take, route_judge). Record only: the rule has no action.

phase-462 W2 adds one more on the same drain, and it is the other half of what the contract’s on_violation lowers to:

  • silence-runtime – a contracted subscription that took NOTHING for a whole max_age_ms window (check_silence). A violation is either “the callback ran too long”, which is the deadline action, or “the input stopped coming”, which is this; the four rules above can only see the first kind.

Structs§

AgeMonitorSpec
One monitored subscriber endpoint (W3b.5 age contracts). Separate table from MonitorSpec — sub contracts key different endpoints and need no publish counter.
GuardSpec
One fault a hazard covers: a topic, or a redundant set of topics.
HazardCell
One hazard’s observer state. Baked as a static beside the spec, the way PubMonitorCell is, so the observer adds a slice to the Executor value and nothing per hazard (issue 0961).
HazardSpec
One contracted hazard (contracts.hazards), lowered by the bake.
MonitorArming
phase-474 I2 – when an executor’s contract monitors start judging.
MonitorSpec
One monitored publisher endpoint.
MonitorTableFull
A monitor table longer than the executor can watch – phase-496 W1.
PubMonitorCell
One contracted publisher’s counters. Baked as a static by codegen (or declared by the fixture); the publisher handle bumps count on every publish, the executor reads deltas on spin ticks.
ReactionEvent
phase-503 – what the reaction hook receives when a supervised route passes its deadline. repr(C): the C ABI hands the same struct through.
SubMonitorCell
One contracted subscriber’s take-age accumulator (W3b.5). The take path records epoch_now - header.stamp per message (fetch_max); the age check drains it (swap 0) per window.
Violation
A detected contract violation, in the play_launch rule-id vocabulary.
ViolationRecord
phase-474 I1 – the image-wide violation record a debugger reads by NAME, for a board whose console reaches nobody.
ViolationSlot
One slot of the SWD record. Every field is a u32 on every target, so the layout is the same on a 32-bit board and a 64-bit host.

Enums§

GuardRef
One guard member: which monitor row’s verdict says this topic has faulted.

Constants§

MARKER_VIOLATION
phase-474 I1 – trace marker: a stored violation, seq << 8 | rule code. The id block continues callback_trace’s 16-20; ids 1-7 are the application’s and are never used here.
MARKER_VIOLATION_DECLARED
phase-474 I1 – trace marker: declared.
MARKER_VIOLATION_FQN
phase-474 I1 – trace marker: the endpoint ref’s fqn_hash.
MARKER_VIOLATION_MEASURED
phase-474 I1 – trace marker: measured.
MAX_AGE_MONITORS
Max AGE-monitored subscriptions per executor – the second table, on its own knob (NROS_EXECUTOR_MAX_AGE_MONITORS, derived from age_rows) so an image with rate contracts and no age contracts pays for one table only.
MAX_MONITORS
Max RATE/LATENCY-monitored endpoints per executor (inline table, no_std).
MAX_VIOLATIONS
Violation ring depth, per executor, and the slot count of the image-wide SWD record (ViolationRecord).
MECHANISM_EXECUTOR
phase-503 – MonitorSpec::mechanism: checked in the executor’s spin loop. Catches a slow route, not a callback that never returns.
MECHANISM_TIMER
phase-503 – MonitorSpec::mechanism: an independent timer outside the executor (the port’s), which also catches a stuck callback.
NO_SINK
HazardSpec::sink when the hazard’s reaction states no safe_state publisher this image monitors: the observer reports detection and recovery, and no reaction.
RATE_CHECK_INTERVAL_US
Rate-check window (µs). Matches play_launch’s ~5 s time-based trigger so both runtimes converge on comparable cadence.
REACTION_NONE
phase-503 – MonitorSpec::reaction_id: no reaction named.
RECORD_HEADER_WORDS
u32 words in the ViolationRecord header, before the first slot.
RECORD_MAGIC
"NRVR" – nano-ros violation record.
RECORD_SLOT_WORDS
u32 words in one ViolationSlot.
RECORD_VERSION
ViolationRecord layout version. Bump on any field change; a reader refuses a version it does not know (scripts/read-violation-record.py).
REPORT_DEFER_MAX_US
phase-474 I7 – the longest a pending verdict waits for a slot with slack before it is reported anyway, in microseconds. Bounds the deferral on an executor whose timers never leave a gap as long as the report costs.
REPORT_QUEUE
phase-474 I7 – how many stored verdicts wait for the sink. A verdict stored while the queue is full evicts the oldest pending one, which is counted (ReportQueue::overflowed); both stay in the ring and the SWD record. Four is the DiagnosticArray status capacity of the generated type, so one coalesced report never needs more.
ROUTE_PENDING
phase10-B3 – PubMonitorCell::route: a take of the trigger input is waiting for this output.
ROUTE_SLOT_MASK
phase10-B3 – PubMonitorCell::route: the bound slot + 1.
ROUTE_VIOLATED
phase10-B3 – PubMonitorCell::route: the last judged route was over its budget (report once until one is within it, as the latency rule does).
RULE_IDS
phase-474 I1 – every rule id the executor reports, in WIRE ORDER: a rule’s numeric code is its index here plus one, and 0 means “not a rule this table knows”.

Functions§

check_capacity_against
check_table_capacity against explicit capacities, so the refusal is testable at any knob value without rebuilding the crate.
check_table_capacity
Whether a table of n_rows rate/latency rows and n_ages age rows fits this build’s executor. The rate table is judged first.
fqn_hash
32-bit FNV-1a of an endpoint ref – the identity a trace marker and the SWD record carry for Violation::fqn (a string neither can hold).
observe_publish_stamp
Peek Time { i32 sec; u32 nanosec } little-endian at offset in a raw CDR receive buffer (encapsulation header included) and return µs since the UNIX epoch. None when the buffer is too short or the stamp is pre-epoch/zero (unstamped messages never fire age monitors). Record the age of the stamp a publisher just put on the wire.
peek_stamp_us
request_monitor_arming
phase-474 I2 – the application says its start-up is over: arm the contract monitors of every executor in the image that waits for this (NROS_MONITOR_ARM_ON_CALL, or Executor::set_monitor_arming).
route_next_due_us
phase-503 – time from now to the earliest deadline of a pending supervised route, in microseconds (0 when one is already due), or None when no supervised route is waiting. This is the executor’s wake source for the rule, and what a port’s independent timer (MECHANISM_TIMER) arms on.
rule_code
The wire code of rule (RULE_IDS index + 1), or 0 for a rule the table does not know.
rule_name
The rule a wire code names, or None for 0 / an unknown code.
violation_marker_words
phase-474 I1 – the four (marker_id, arg) events one stored violation becomes in a trace (callback_trace emits them, in this order, when the trace-callbacks feature is on and a sink is installed). seq is the executor’s sequence number; its low 24 bits ride in the first event.

Type Aliases§

ReactionHookFn
phase-503 – the reaction hook: called on the spin thread, inside spin_once, once per missed route deadline. It must not block; it starts the fallback (or hands it to another thread) and returns. ctx is what was registered with the hook.
ViolationSink
Issue 1635 – where an executor hands its violations when the image asked it to (Executor::set_violation_sink).