ViewAlyzer Recorder
The recorder is the firmware side of ViewAlyzer: a small C library you compile into your target that writes a compact binary event stream (task switches, interrupts, sync objects, your own values and markers) to a transport the host reads. The host is any of the ViewAlyzer front ends: the desktop app, the headless CLI, the Python SDK, the BKPT Debug extension. This section is the user-facing API: what you call, what you configure, what each transport needs.
What it covers
| ADAPTER | WHAT IS TRACED FOR FREE | YOUR CALLS |
|---|---|---|
Bare metal (VA_RTOS_SELECT=VA_RTOS_NONE) | Nothing automatic: no kernel, no hooks | Values, events, markers, ISR entry and exit, GPIO, counters, heap gauge |
| FreeRTOS (v9.0 to 11.x) | Task switches, creation and deletion, stack usage, queues, semaphores, mutexes and their contention, notifications, event groups, timers, heap, tickless idle | The same value and marker API, plus ISR entry and exit in your handlers |
| Zephyr (Cortex-M) | Threads, mutexes, semaphores, message queues, k_event, k_work, k_timer, sleep, power management, heaps | The same value and marker API |
Three transports carry the stream, chosen at build time with VA_TRANSPORT: ITM over the SWO pin, SEGGER RTT, or a RAM ring buffer the probe reads over SWD (the default; works on every core, including Cortex-M0/M0+/M23 that have no ITM). A fourth, CUSTOM_TRANSPORT, hands COBS-framed packets to a function you supply (UART, USB, radio). See Transports and snapshots.
Five minutes to a first trace
- Add
core/ViewAlyzer.c(and the adapter file for your RTOS) to the build,core/(andfreertos/or the Zephyr module) to the include path. - Copy
core/va_config_template.hinto your project asva_config.h. Set your CMSIS device header there; the defaults already enable the recorder, select bare metal, and use the RAM buffer:
/* va_config.h */
#define VA_DEVICE_HEADER stm32g4xx.h
Add the directory containing va_config.h to your include path, then point the entire build at it with one definition:
add_definitions(-DVA_CONFIG_HEADER=va_config.h)
Keep va_config.h to settings that differ from the defaults. FreeRTOS users also select VA_RTOS_FREERTOS there; Zephyr users configure the module through Kconfig. The configuration reference lists every option.
- Start the recorder once clocks are up, register what you will log, then log from anywhere:
#include "ViewAlyzer.h"
enum { TRACE_SINE = 1, TRACE_LED = 2, EVENT_WORK = 1 };
int main(void)
{
SystemClock_Config();
VA_Init(SystemCoreClock); /* before registering user channels */
VA_RegisterUserTrace(TRACE_SINE, "Sine Wave", VA_USER_TYPE_GRAPH);
VA_RegisterUserTrace(TRACE_LED, "LED Toggle", VA_USER_TYPE_TOGGLE);
VA_RegisterUserEvent(EVENT_WORK, "Work Block");
for (;;) {
VA_EVENT_START(EVENT_WORK);
int32_t v = process_sample();
VA_EVENT_END(EVENT_WORK);
VA_LogTrace(TRACE_SINE, v);
VA_LogToggle(TRACE_LED, led_state);
VA_TickOverflowCheck(); /* housekeeping, see the API reference */
}
}
- Capture from the host:
viewalyzer-cli capture --config board.vacf --elf firmware.elf --output run.vadb --duration 10, or select the matching ELF File and press Record in ViewAlyzer-RS. Default RAM capture requires an ELF and a host with RAM metadata support; see RAM metadata. The.vacfnames the matching transport (stlink-rambuffor a RAM buffer over an ST-LINK, and so on): Transports and snapshots pairs them up.
Note: VA_Init resets every registry, so register after it, never before. It also checks that the timestamp source is counting and refuses to start (the host sees ERR:TS_*) when it is not, so check the timer configuration if initialization fails.
For optional buffered ITM, RTT or custom output, schedule VA_Drain() from an application loop, task or FreeRTOS idle hook and budget its caller's stack. The default RAM transport does not need it. See Scheduling the drain.
Using a custom timer
Use a custom timer when your core has no DWT cycle counter (Cortex-M0/M0+/M23) or the device does not implement one. Add two settings to va_config.h:
#define VA_TIMESTAMP_SOURCE CUSTOM_TIMER
#define VA_TIMER_BITS 32 /* 16 or 32 */
Provide a cheap, lock-free reader for a free-running hardware timer, start the timer, and pass its exact frequency to the three-argument form of VA_Init:
static uint32_t read_trace_ticks(void)
{
return TIM2->CNT;
}
/* Start TIM2 before this call; this example assumes a 1 MHz timer. */
VA_Init(SystemCoreClock, read_trace_ticks, 1000000u);
The reader must be safe from any context, including interrupts. During quiet periods, call VA_TickOverflowCheck() at least once per timer-wrap period. A 16-bit timer at 1 MHz wraps about every 65 ms, so prefer a 32-bit timer when one is available.
Where things are
- RAM metadata: attach to running firmware with its ELF, configure table capacity, or select setup-bundle capture.
- Integration: build steps per adapter, the FreeRTOS hook header, the Zephyr module and Kconfig.
- Configuration: every
VA_*knob, its default, and theva_config.hcontract. - API reference: every function and macro you call, including the housekeeping calls (
VA_TickOverflowCheck,VA_EmitSetupBundle,VA_Drain,VA_SnapshotFreeze). - Transports and snapshots: ITM, RTT, RAM buffer, custom transports, the post-mortem ring, and the host configuration that goes with each.
Worked examples for every adapter and transport live in the ViewAlyzer-Examples repository (baremetal/, freertos/, zephyr/, each with its .vacf).
Versions
The recorder reports two numbers. VA_RECORDER_VERSION (from VA_RECORDER_VERSION_MAJOR/MINOR/PATCH in core/ViewAlyzer.h) is the library release; it is sent to the host in the setup bundle and shown in the recording's metadata. VA_WIRE_VERSION (1) is the stream format, encoded in the sync marker; it changes only on a breaking framing change. RAM metadata attachment additionally requires a host that supports its descriptor protocol. Set VA_METADATA=0 when using older viewers. The recorder and the host app are released together as a pair.