Skip to content

wkhadgar/zview

ZView, a Zephyr RTOS runtime visualizer

Real-time system observability for Zephyr RTOS, delivered over SWD.

Stop guessing your stack margins and heap health. ZView provides a zero-footprint, high-fidelity visualization of your Zephyr application’s runtime, delivered over SWD with zero instrumentation.

No UART, no RTT, and no manual code changes. Just Kconfig and your probe.

Looking for the standalone pip installation? See README_pip.md.


Prerequisites

To properly analyze your Zephyr app, your ELF binary must be compiled with specific Kconfig options enabled:

## prj.conf
CONFIG_INIT_STACKS=y            # Required for stack watermarks
CONFIG_THREAD_MONITOR=y         # Required for thread discovery
CONFIG_THREAD_STACK_INFO=y      # Required for thread metadata

# Optional Features
CONFIG_THREAD_NAME=y            # Enables thread name display
CONFIG_THREAD_RUNTIME_STATS=y   # Enables CPU usage tracking
CONFIG_SYS_HEAP_RUNTIME_STATS=y # Enables heap runtime stats and fragmentation map

Installation

Add this snippet to your west manifest:

manifest:
  projects:
    - name: zview
      url: https://github.com/wkhadgar/zview
      revision: main
      path: modules/tools/zview
      west-commands: scripts/west-commands.yml

Then fetch it with:

west update zview

How to Use

If your board is already flashed and probed, ZView can often infer the ELF path and runner from the build directory — just run:

# From project root
west zview

Or pass arguments explicitly:

# Example: nRF5340 DK via JLink
west zview -e build/zephyr/zephyr.elf -r jlink -t nRF5340_xxAA

Commands

ZView is invoked through one of four commands. Bare zview ... is a shortcut for zview live ....

Command Purpose TUI
live Attach to a probe and render the TUI. Default when no command is given. yes
record Capture a live session to a .ndjson.gz recording file and exit. no
replay Render the TUI from a previously captured recording. yes
dump Emit a single polling frame and exit. no

Common arguments

Argument Used by Description
-e, --elf-file all Path to the firmware .elf file (e.g. build/zephyr/zephyr.elf).
-r, --runner live, record, dump Debug runner: jlink, pyocd, or gdb.
-t, --runner-target live, record, dump MCU descriptor for the chosen runner (see below).
--period live, record, dump Polling period in seconds (default: 0.10).

Command-specific arguments

Argument Command Description
-o, --output record Recording target path (.ndjson.gz).
--duration record Recording upper bound, in seconds.
--frames record Recording upper bound, in data frames.
--heap record Capture per-frame fragmentation for the named k_heap variable.
-i, --input replay, dump Recording source path (.ndjson.gz).
--no-pacing replay Drain the recording as fast as possible instead of honoring its wall-clock cadence.
--frame dump Which polling frame to emit (1-indexed; default: 1).
--json dump Emit the frame as JSON on stdout.
Finding the right value for -t

The -t argument is the MCU descriptor name as expected by your chosen runner. How to find it depends on the runner:

JLink (-r jlink) Use the device name from the J-Link Supported Devices list.

# Example: Nordic nRF5340 DK
west zview -e build/zephyr/zephyr.elf -r jlink -t nRF5340_xxAA

pyOCD (-r pyocd) Run pyocd list --targets to see the available target names for your installed packs.

pyocd list --targets          # find your target name

# Example: STM32F401 Nucleo
west zview -e build/zephyr/zephyr.elf -r pyocd -t stm32f401xe

GDB server (-r gdb) Pass the host:port of your GDB server instead of a device name.

west zview -e build/zephyr/zephyr.elf -r gdb -t localhost:1234

Tip: The runner and target are often already defined in build/zephyr/runners.yaml (generated by west build). In that case you can omit -r and -t entirely and ZView will pick them up automatically.

Navigation

ZView acts as a TUI. Navigate with UP and DOWN arrows from the default view:

  • ENTER: Get details for a specific thread/heap (hit ENTER again to return).
  • S / I: Sort the data and invert the sorting order.
  • H: Access the Heap Runtime visualization (hit H again to return).

TUI navigation

TUI navigation 2

TUI thread tracking

TUI heap navigation

TUI heap navigation 2

TUI heap fragmentation map

Offline workflows

ZView can record a live session to disk, replay it later without a probe, or emit a single frame as JSON for CI.

Record a live session:

# 30 s of live polling from a JLink probe, saved to disk
west zview record -e build/zephyr/zephyr.elf -r jlink -t nRF5340_xxAA \
  -o capture.ndjson.gz --duration 30

Bound the recording by either --duration (seconds) or --frames (number of data frames).

To also capture the fragmentation map for a specific heap, pass --heap <name> where <name> matches a k_heap variable from your firmware (e.g. my_kernel_heap):

west zview record -e build/zephyr/zephyr.elf -r jlink -t nRF5340_xxAA \
  -o capture.ndjson.gz --duration 30 --heap my_kernel_heap

Replay it later — no hardware needed:

# Feed the recording into the TUI
west zview replay -e build/zephyr/zephyr.elf -i capture.ndjson.gz

By default the replay honors the recording's original cadence. Pass --no-pacing to drain it as fast as possible.

The ELF is still required: DWARF offsets are resolved at replay time and are not stored in the recording.

CI-friendly single-frame snapshot:

# One polling frame from a live probe, dumped as JSON on stdout
west zview dump -e build/zephyr/zephyr.elf -r jlink -t nRF5340_xxAA --json \
  | jq '.threads[] | select(.runtime.stack_watermark_percent > 80)'

dump accepts either a live source (-r/-t) or a recording (-i). Omit --json for a human-readable one-shot dump. Use --frame N to emit the Nth polling frame instead of the first — useful when the first frame's CPU baseline hasn't settled.

Note: Status messages (probe connect, ELF load) are emitted on stderr, so stdout stays clean for piping into jq or similar.


Advanced

QEMU/GDB Targets

If debugserver is available:

west debugserver

Or launch a GDB server from QEMU directly with -s — this will be necessary if you have no runners.yaml (e.g. mps2/an385):

qemu-system-arm \
  -machine mps2-an385 \
  -cpu cortex-m3 \
  -kernel build/zephyr/zephyr.elf \
  -nographic \
  -serial mon:stdio \
  -s

Then attach ZView:

west zview -r gdb -t localhost:1234

Note: Due to GDB server limitations, when using -r gdb the target is briefly halted prior to memory reads. Be aware of this side effect.

How it works

ZView achieves a minimal footprint by avoiding on-target processing or UART/Shell output. It utilizes the debug probe's ability to read memory via the APB bus without halting the CPU. By parsing the ELF file, ZView identifies kernel object locations, performs analysis of stack watermarks, thread CPU usages, and executes a deterministic walk of physical heap chunks to map memory fragmentation.

Note: The idle thread is implicit and only expresses itself on the used CPU %, when available.

Feel free to open an issue if you feel like this has some potential!

About

ZView - A real-time thread (and heaps) viewer for Zephyr RTOS. Just like htop, but for Zephyr

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors

Languages