fapolicyd.timing
fapolicyd manual decision timing report
- Provided by: fapolicyd (Version: 2.0-1)
- Report a bug
fapolicyd manual decision timing report
The /run/fapolicyd/fapolicyd.timing file contains one manual decision timing run from fapolicyd(8). The daemon writes this file when a privileged manual timing stop request ends an armed timing run. A stop request while timing is not armed writes a short status report instead of timing data. The daemon may also stop collection and write the report when a timing counter would overflow.
Timing collection is disabled by default. Manual timing requests are honored only when the active daemon configuration has timing_collection=manual in fapolicyd.conf(5). The command fapolicyd-cli --timing-start arms a run, and fapolicyd-cli --timing-stop stops the run and prints this report. --timer-start and --timer-stop are accepted aliases.
The daemon stores aggregate counters only. It does not store one timing record per decision. Each decision worker owns local aggregate blocks containing a call count, total time, maximum time, and fixed latency buckets for each stage. The report snapshots and aggregates those blocks.
The first lines summarize the run:
The TL;DR section appears immediately after the run summary and before Overall decision latency . It surfaces two or three dominant findings drawn from the same data used by the later Derived observations section, such as helper dominance, response formatting cost, or queueing health. The detailed observations remain in their original location later in the report.
The Overall decision latency section describes end-to-end decision latency from the point a decision worker starts processing a dequeued fanotify event until the decision path returns after response handling.
The section prints average and maximum latency using compact latency units, approximate percentile buckets, and cumulative percentages for useful thresholds such as <=50us , <=100us , <=500us , <=1ms , and >10ms . Percentiles are bucket approximations, not exact per-decision percentiles. The 95th percentile is a useful tail-latency marker: roughly 95% of observed calls were at or below that bucket, so a high p95 points to a recurring slow path rather than a single worst-case outlier.
When high-end tail buckets are observed, the section also prints a compact tail line for >10ms , >25ms , >50ms , >100ms , and >250ms . The line includes both count and percentage so long outliers do not disappear inside a single >10ms bucket.
The Queueing section describes work waiting before a decision worker starts processing an event. It is based on time_in_queue:total when enqueue timestamps were observed during the timing run.
If no enqueue timestamps were observed, the section prints not observed and still reports max queue depth.
When more than one decision worker is active, the Per-worker timing skew section summarizes routing and latency balance by worker. The aggregate latency sections remain authoritative; this section exists to make uneven worker buckets visible.
The Decision phase timing section shows the main decision-thread phases when they were observed:
event_build:total evaluation:total response:total
These phase rows are the best first view for explaining where decision-thread time is being spent. Helper rows are separate because some lazy helper operations can be requested from more than one phase.
The report introduces helper attribution with:
Lazy helper attribution: Helper timings are attributed to the active logical driver: evaluation or response. Combined totals are evaluation + response.
The driver table and combined helper table follow this note.
The Lazy helper attribution by driver section shows helper time split by the logical context that caused it: evaluation or response.
The Combined lazy helper attribution section groups helper-related stages into stable combined paths. For helpers that are split by driver, the combined row is calculated by adding the phase-specific evaluation and response rows together. These rows answer how often a helper was needed and how expensive that helper was when amortized over all decisions.
The current helper groups are:
mime_detection:total mime_detection:fast_classification mime_detection:gather_elf mime_detection:libmagic_fallback trust_db_lookup:total trust_db_lookup:read trust_db_lookup:lock_wait hash_ima:total hash_sha:total proc_detail_lookup
The Derived observations section prints short deterministic observations when the required data was present in the run. Examples include:
When hot detailed stage rows have observations above 10ms, the report prints a Stage tail summary after the detailed table. Rows are sorted by >10ms count, limited to the top five rows, and extended with any additional row that has a nonzero >50ms count. Near-identical parent/child tail rows may be suppressed to keep the section focused.
stage:name: >10ms count/percent, >25ms count/percent, >50ms count/percent
The detailed table remains the canonical total-time ranking; the tail summary only adds high-end resolution for rows that crossed one of the tail thresholds. Thresholds with zero observations are omitted.
The Detailed stage timing table is sorted by total measured time by default. It answers which measured operations consumed the most total time during the run. This is the detailed view and is intentionally kept below the summary and helper attribution sections.
The decision:total row is included when observed and participates in the same total-time sort as the other rows.
Stages with zero calls are hidden from the table and listed under Not observed . If a stage is not observed, no measured call to that operation occurred during the timing window. For example, an absent evaluation:trust_db_lookup:total and response:trust_db_lookup:total line means no measured trust database lookup occurred from those drivers in that run.
Stage names use a colon-separated hierarchy:
phase:operation:child
Rows ending in :total are parent scopes. Child operation timings are useful contributors, but they can be nested, lazy, or shared by more than one code path, so child rows are not expected to add up to the parent total.
The main decision-thread view is:
decision:total time_in_queue:total event_build:total event_build:cache_flush event_build:proc_fingerprint event_build:fd_stat evaluation:lock_wait evaluation:total evaluation:mime_detection:total evaluation:mime_detection:fast_classification evaluation:mime_detection:gather_elf evaluation:mime_detection:libmagic_fallback evaluation:fd_path_resolution evaluation:proc_detail_lookup evaluation:hash_ima:total evaluation:hash_sha:total evaluation:trust_db_lookup:total evaluation:trust_db_lookup:lock_wait evaluation:trust_db_lookup:read response:total response:syslog_debug_format:total response:mime_detection:total response:mime_detection:fast_classification response:mime_detection:gather_elf response:mime_detection:libmagic_fallback response:trust_db_lookup:total response:trust_db_lookup:lock_wait response:trust_db_lookup:read response:audit_metadata:total response:fanotify_write
Lazy helper operations are attributed to the active logical driver when the helper is called. Rule evaluation uses evaluation: , response logging and audit response work use response: . Helper timing outside those logical drivers is treated as an instrumentation bug and is not reported as a separate phase.
For MIME and trust DB helper rows below, * means the driver prefix evaluation , response .
The notes footer reports the largest queued-time contributor when queue wait was observed, the largest helper contributor, the largest decision phase contributor, and the slowest observed row by maximum time.
For cache hit, miss, collision, eviction, subject deferral, and early subject eviction counters, see fapolicyd.metrics(5).
If adding a timing count, total, or bucket counter would overflow, fapolicyd disarms timing, records an overflow stop reason, and writes the timing report instead of allowing counters to wrap and skew the data.
fapolicyd(8), fapolicyd-cli(8), fapolicyd.conf(5), fapolicyd.state(5), fapolicyd.metrics(5), and fapolicyd.trust(5).