Queries
query <verb> --recording <id|path> prints one JSON object shaped for a consumer, from a recording on disk. Every response carries schema_version, recording_id and query (most also carry path and budget); times are microseconds since the recording start; tasks are referenced by a compact code (T1, T2) defined in the response's task_legend, channels by C1, C2 in trace_legend.
The machine-readable schema catalog is JSON Schema draft 2020-12 and has one named schema per verb at #/$defs/<verb>. The shapes below are not inferred from examples: they are the documented schema_version: 2 contract.
$ viewalyzer-cli query summary --recording run.vadb
{"schema_version":2,"recording_id":"328d49aa4eb6","query":"summary","budget":"med","duration_us":5111531,"span_seconds":5.11,"cpu_freq_hz":170000000.0,"os":"BareMetal","total_events":21930,"context_switches":14679,"cpu_load_percent":0.27,"total_tasks":1,"lost_events":0,"corrupt_bytes":0,"pre_sync":{"lost_events":0,"corrupt_bytes":21,"corrupt_runs":1,"seq_gaps":0},"execution_ended_abnormally":false,"ended_abnormally":null,"execution_end":{"t_us":5111531,"task":"main","last_event":{"kind":"task_switch","object":"main"}},"capture_utc":"2026-08-29T08:29:39Z"}
Tiers and budgets
Three verbs (timeline, events, user-traces) come in three tiers, so a consumer never pulls a payload it cannot handle:
| --TIER | NEEDS | RETURNS |
|---|---|---|
summary (default) | nothing | whole-recording statistics |
bucketed | --t-start-us, --t-end-us, --bucket-us | one value per bucket over the window |
raw | --t-start-us, --t-end-us | the individual rows in the window |
--budget low|med|high scales the caps (rows 200 / 1000 / 5000, buckets 64 / 256 / 1024, serialized size 12 / 48 / 200 KB). A window that exceeds a cap does not truncate silently: it answers window_too_wide with a suggestion (a narrower t_end_us, a wider bucket, a --kinds or --channels filter, or a higher budget). The -all verbs (slices, slice-details, events-all, user-traces-all) are unbounded, for consumers that manage payload size themselves (--max-slices n).
$ viewalyzer-cli query user-traces --recording run.vadb --tier raw --t-start-us 1000000 --t-end-us 1010000 --channels "Sine Wave"
{"schema_version":2,"query":"user-traces","tier":"raw","t_start_us":1000000,"t_end_us":1010000,
"trace_legend":{"C1":{"name":"Sine Wave","va_id":42,"display":"graph","ns":"user_trace"}},
"sample_fields":["channel_code","t_us","value"],
"samples":[["C1",1000239,88.0],["C1",1002370,83.0],["C1",1004502,77.0],["C1",1006632,71.0],["C1",1008768,63.0]]}
The verbs
| VERB | PAYLOAD |
|---|---|
summary | Whole-recording scalars plus execution_ended_abnormally (boolean), ended_abnormally (fault/stack-trap time, task, and last event; otherwise null), execution_end (last event and task), recording integrity, and separate pre_sync acquisition counters |
timeline | Per-lane cards: cpu_percent, slice_count, mean/p50/p95/p99/max slice, preemptions, observed period p50/p95, max_jitter_us, anomalies[]; bucketed: per-lane CPU% per bucket; raw: slices: [[task_code, t_start_us, dur_us], ...] |
events | Counts by_kind and top_objects (--kinds filters); bucketed: kind counts per bucket; raw: {t_us, kind, start_end, code|obj, value_i, value_f, text} |
user-traces | Per channel {count, min, max, mean, last, rms, std_dev, rate_hz, min_at_us, max_at_us} (--channels by name or code); bucketed: [{min,max,mean,count} | null] per bucket; raw: samples: [[channel_code, t_us, value], ...] |
cpu | Top-level CPU model fields (there is intentionally no cpu wrapper): busy_pct, idle_pct, min and max load with their times over a sliding window, context_switches, preemptions, tasks[] (execution percentiles net of preemption, period and outliers, blocked time, inversions, sync and failed ops, stack), findings[]; --t-start-us/--t-end-us scope everything |
inversions | scope is mutexes_only; every mutex contention has type (inversion when the waiter outranks the holder, OS-aware), waiter/holder priorities and times. Semaphore events do not identify an owner, so semaphore inversion is not inferred |
verdicts | Unified verdicts[] from native CPU/fatal-end detectors and optional Trace Domain rules. detectors.native and detectors.trace_domains report status and finding counts; domain status distinguishes no_descriptors, all_disabled, no_rules, and ready |
timers | Per kernel timer: measured fires, predicted fires, arms and stops, phase-corrected lateness (mean/p99/max_lat_us), violations against --threshold-us (500), a 12-bin histogram, callback duration and callback_mean_evidence (measured, estimated, mixed, unknown); work[] k_work lanes. Raw: fire_records[] with callback evidence, event IDs and a cause on violations; measured stop_callbacks[] |
comms | paths[] (producer>via>consumer: kind, count, rate, median/p99/max latency, blocked) and resources[] (sends, receives, matched, still pending, empty receives, contentions, peak pending); with --bucket-us a per-resource backlog series. ipc[] adds queue/notification/flag values at the window end and recording_totals (including full/empty time) |
task-states | Observed task states and priority history; completed ready/blocked/sleeping/suspended times and wake-to-run latency. --task-id N filters by recording actor ID. Raw adds states[] and priority_history[] |
resources | Queue occupancy, indexed notification values, event flags, stream/message buffers, memory slabs, condition variables and poll resources. Raw adds observed value history[]; --resource-id N selects one actor |
queue-failures | Individual send/receive/peek failures with resource ID, event ID, task/ISR and observed occupancy; --resource-id N filters without combining same-named queues |
semaphores | Structured give/take/failure counts, completed/incomplete waits, median/P99/max blocking duration and relative outliers. Raw adds waits[]; --resource-id N selects one semaphore |
series | --kind cpu-load (total plus per-lane bands), event-rate, stack, heap (with failed_allocs_us and capacity), task-timing --task n --metric exec|period, interval --from task:a|trace:a|resource:a --to .... Compact curves use fields + values; stack additionally has canonical object rows stack: [{t_us,total_stack_used_bytes}] |
sql | --sql "SELECT ...": read-only, one statement, rows capped by budget, BLOBs as "<blob N bytes>" |
fingerprint, compare | Golden-run regression testing; compare result values are named current (candidate recording) and baseline (fingerprint/envelope). Exit 0 pass/warn, 2 regression, 1 command/input error; see Regression tests in CI |
$ viewalyzer-cli query cpu --recording run.vadb
{"schema_version":2,"query":"cpu","window":{"t_start_us":0,"t_end_us":5111531,"scoped":false},"busy_pct":0.27,"idle_pct":99.73,"min_load_pct":0.27,"max_load_pct":0.27,"sweep_window_us":500000,"context_switches":0,"preemptions":0,
"tasks":[{"name":"ISR:SysTick","kind":"isr","cpu_pct":0.27,"runs":5111,"mean_us":2.74,"p50_us":2.75,"p99_us":2.77,"max_us":2.87,"period_p50_us":1000.0,"period_p95_us":1000.1,"outliers":0,"preempt_count":0,"blocked":0,"inversions":0}],"findings":[]}
$ viewalyzer-cli query series --recording run.vadb --kind cpu-load --bucket-us 500000
{"schema_version":2,"query":"series","kind":"cpu-load","window_us":500000,"points":256,"fields":["t_us","load_pct"],"total":[[0,0.0],[19967,0.28],[39934,0.27], ...]}
RTOS states and IPC evidence
viewalyzer-cli query task-states --recording run.vadb --tier raw --task-id 1
viewalyzer-cli query resources --recording run.vadb --tier raw --resource-id 3 --t-start-us 100000 --t-end-us 200000
viewalyzer-cli query queue-failures --recording run.vadb --resource-id 3
viewalyzer-cli query semaphores --recording run.vadb --tier raw
viewalyzer-cli query timers --recording run.vadb --tier raw
The IDs above are examples: take task_id or resource_id from the query's unfiltered response. These are recording actor IDs, not recorder wire IDs or names. Two resources can have the same name. A filter that matches no actor returns an empty list. The --headless --query <verb> form works too.
task-states, resources and semaphores support summary (default) and raw. Raw may cover the whole recording; a time window is optional. bucketed is rejected for these verbs. queue-failures always returns individual events. All four accept --t-start-us, --t-end-us and --budget. New timestamps and durations are in microseconds relative to recording start, including recordings whose device clock starts at a nonzero value.
- Task states:
state_events_recorded: falsemeans no recorded state history; empty state arrays and zero completed totals do not establish how the task spent unknown time. Raw intervals are clipped to the window, withcomplete: falseif they cross the selection, event loss or capture end. Only whole, completed intervals contribute tocompleted_time_usand wake-to-run metrics.priority_at_startcarries the last observed priority before the window;priority_historycontains changes inside it. Missing priorities stay null.recording_ipc_totalsexplicitly covers the whole recording. - Resources:
initial_valueis the observed value at the window start;valueatat_usis the observed value at its end. Rawhistorycontains observations and loss markers inside the window. A null value means unknown, including after loss until another observation. Queue values are used slots, slab values are used blocks, and notification/flag values are bitfields. Stream-buffer transfer bytes do not establish occupancy.recording_totalsalways covers the whole recording. The existingcomms.ipcfields retain their originalat_ns/duration-in-nanoseconds names for compatibility. - Queue failures:
totalcounts matching detail events in the selected window, before row limits. With no window it matches the resource failure count. Legacy duplicate events and deferred requests are excluded. An ISR row hasexceptionset and nulltask_id/task; it is not attributed to the interrupted task.usedandcapacitycan be null. A failed operation alone cannot distinguish an expected nonblocking poll from a timeout. Older captures without queue-detail events have no exact failure list. - Semaphores: operation counts and waits cover the selected window. Incomplete waits remain visible with null
duration_us, and do not affect median/P99/max. Blocking ends at ready/running, before any later scheduling delay. With at least ten complete waits, outliers exceed four times the median with a 1 ms floor; otherwiseoutlier_threshold_usis null. These observations do not establish ownership or actual token count. The same findings appear incpu/verdicts, andverdicts.detectors.native.kindsincludessemaphore. - Timers:
measured_callbacksmeans explicit callback tracing was observed; it does not guarantee every exit arrived. Per-firecallback_tracedidentifies an explicit entry, andcallback_evidenceismeasured,estimated, orunknown. A missing traced exit stays unknown with nocb_end_us.callback_mean_evidencedescribes the durations contributing to the existingcb_mean_us, includingmixedif both measured and estimated samples occur. Rawstop_callbackscontains completed, measured start/end/duration records.
The existing slice-details query adds an rtos object with priority at the slice, its previous recorded wait (including completeness), and explicitly named recording_state_totals and recording_ipc_totals. Existing slice fields and compact slices/timeline arrays remain unchanged.
Responses retain schema version 2. Item lists obey the budget's item cap; raw histories share one row allowance across the new query response. Check every *_truncated/truncated flag. If a history is truncated, narrow the window or filter to one actor before interpreting its end. The byte cap can still return window_too_wide; a cap never silently establishes a complete history. No replay metric names or HIL pass/fail thresholds change.
SQL
When no verb shapes the data the way you need, sql runs one read-only statement against the recording's tables (The .vadb file):
va_events.text contains event payload text only; it does not duplicate object names. Resolve names with va_events.obj -> va_objects.id, for example select e.*, o.name as obj_name from va_events e left join va_objects o on o.id = e.obj.
$ viewalyzer-cli query sql --recording run.vadb --sql "select kind, count(*) as n from va_events group by kind order by n desc limit 5"
{"schema_version":2,"query":"sql","columns":["kind","n"],"rows":[["user_event",19136],["isr",10222],["user_trace",7176],["user_toggle",75],["string",5]],"row_count":5,"truncated":false}
Hardware-trace queries
Present when the recording was captured with hardware trace (--dwt ... or the hardware-trace block on an SWO transport, or DWT_PCSR polling on the others); no_hw_trace otherwise.
| VERB | DATA |
|---|---|
profile [--elf f] | PC-sample hotspots: total_samples, sleep_samples, span_s, sample_rate_hz, hotspots[] of {symbol, addr, samples, pct, file, line, isr} (symbolicated with --elf), source (dwt-swo or pcsr-poll), interval_cycles, sleep_pct, outside_elf_pct |
itm-console [--port N] | ports[] of {port, lines: [{t_us, text}], bytes}; lines split on newline, an unterminated tail is marked partial; --port keeps one port |
dwt-data | watches[] of {cmp, name, address, function, count, first: [{t_us, value, rw, pc?, symbol?, file?, line?}]} (up to 256 samples each; --elf symbolicates the PCs) |
dwt-exc | total, max_depth, exceptions[] of {num, name, enter, exit, return, max_depth}, events[] of {t_us, num, func, depth} (up to 4096) |
dwt-counters | counters.{cpi,exc,sleep,lsu,fold,cyc} = {wraps, cycles, rate_per_s}, span_s, cpu_load_pct (from the sleep counter, null without it), overflows, reliable |
swo-load | {bytes, seconds, bytes_per_s, swo_hz, share_pct, overflows} for the whole capture |
irq, etm and target-caps are reserved and error naming the reason (no_hw_trace, etm_not_present, bad_arguments).
load and export
load prints a recording's summary (recording.{duration_us, os, total_events, lanes_detail[], channels_detail[], lost_events, ...}) and converts or exports:
$ viewalyzer-cli load run.va --output run.vadb # a byte-log or raw wire dump into a .vadb
$ viewalyzer-cli load run.vadb --export traces --format csv --out - # events | traces | spans | tasks | channels
t_us,channel,va_id,value
6,Sine Wave,42,0
9,Tick Counter,43,0
11,Workload,46,0
replay run.vadb [--perf] re-parses a recording and prints [metric] category,name,value,unit lines (info, data, per-lane task.slices and task.cpu, per-channel trace, flow, result,status), the surface the HIL bench parses; --perf adds pipeline timings. Exit 0 when the recording carries task slices or user traces, 1 otherwise.