Ferrous Systems's defmt Plugins

Modality includes a set of plugins to receive Ferrous Systems's defmt (opens new window) data from embedded systems. Data can be imported from a file or collected over RTT via these plugins:

  • defmt: importer for data in files.
  • defmt-rtt: collector for streaming data collected live over RTT.

The source code can be found in the plugin's repository (opens new window).

# Getting Started

  1. Follow the instructions in the Setup (opens new window) section of the [defmt book][defmt].
  2. Use the importer to import from a data file, or use one of the available collectors to collect data from a running system.
modality-reflector import defmt /path/to/trace.bin
modality-reflector run --collector defmt-rtt --config <your-config-file>

# Concepts

The following describes the default mapping between defmt concepts and Modality's concepts. See the configuration section for ways to change the default behavior.

  • Log messages are represented as events on the default timeline (main), unless using RTOS mode.
  • Log message details are represented as event attributes at the root level (e.g. event.level = trace for trace! messages).
  • Other defmt-specific information is represented as event and timeline attributes with the prefix event.internal.defmt and timeline.internal.defmt respectively.
  • Event names will use the log message's formatted string by default, and can be overridden using the conventions described below.

# Format String Conventions

The plugins will look for a specific convention in order to extract more semantically rich information from the log messages to produce Modality attribute key/value pairs.

The format string syntax is [<event_name>::][<attr_key>=<attr_val>,...].

  • When <event_name> is not present, the formatted string is used as the event name.
  • <attr_val> can be a literal or parameter.
  • <attr_val> only supports primitive types (e.g. integer/string/etc)

For example, the statement defmt::info!("my_event::k0={=u8},k1=something,k2={=str}", 2, "foo"); will produce an event with the following attributes (not exhaustive):

  • event.name = my_event
  • event.k0 = 2
  • event.k1 = "something"
  • event.k2 = "foo"
  • event.level = "info"

# RTOS Mode

The plugins can be configured to look for RTOS-specific instrumentation to determine task/ISR contexts and interactions.

Currently the only supported RTOS mode is rtic1. It requires using our RTIC fork (opens new window) (based on version 1.1.4) with trace instrumentation. You can try it out by adding the following to your Cargo.toml:

[dependencies.cortex-m-rtic]
git = "https://github.com/auxoncorp/cortex-m-rtic.git"
branch = "defmt-instrumentation"

# Configuration

Configuration fields should be placed in a [plugins.ingest.importers.defmt.metadata] or [plugins.ingest.collectors.defmt-rtt.metadata] table in a modality-reflector configuration file. All fields are optional.

# Common Fields

These fields are the same for each of the plugins.

client-timeout
string
Specify a modality-sdk ingest client timeout.
Accepts durations like "10ms" or "1minute 2seconds 22ms".
run-id
string
Use the provided run ID instead of generating a random one.
clock-id
string
Use the provided clock ID instead of generating a random one.
clock-rate
string
Use the provided rate as the time base for converting ticks to nanoseconds.
Format is 'numerator/denominator', which represents the clock frequency (in Hz).
init-task-name
string
Use the provided initial task/context name instead of the default (main).
disable-interactions
boolean
Don't synthesize interactions between tasks and ISRs when a context switch occurs, when in RTOS mode.
rtos-mode
string
The RTOS mode to use. The default is none.
elf-file
string
The ELF file containing the defmt symbol information.

# Importer Fields

These fields are specific to the importer plugin.

open-timeout
string
Specify an open-file retry timeout.
Accepts durations like "10ms" or "1minute 2seconds 22ms".
file
string
Path to the file to import.

# Example Configuration

# config.toml for modality-reflector

[plugins.ingest.importers.defmt.metadata]
elf-file = 'firmware.elf'

# RTT Collector Fields

These fields are specific to the RTT collector plugin.

attach-timeout
string
Specify a target attach retry timeout.
When provided, the plugin will continually attempt to attach and search for a valid RTT control block anywhere in the target RAM.
Accepts durations like "10ms" or "1minute 2seconds 22ms".
See the RTT timing section (opens new window) for more information.
control-block-address
integer
Use the provided RTT control block address instead of scanning the target memory for it.
up-channel
integer
The RTT up (target to host) channel number to poll on. The default value is 0.
setup-on-breakpoint
string
Set a breakpoint on the address of the given symbol used to signal when to enable RTT BlockIfFull channel mode and start reading.
Can be an absolute address or symbol name.
thumb
boolean
Assume thumb mode when resolving symbols from the ELF file for breakpoint addresses.
probe-selector
string
Select a specific probe instead of opening the first available one.
chip
string
The target chip to attach to (e.g. STM32F407VE).
protocol
string
Protocol used to connect to chip. Possible options: [swd, jtag].
The default value is swd.
speed
integer
The protocol speed in kHz. The default value is 4000.
core
integer
The selected core to target. The default value is 0.
reset
boolean
Reset the target on startup.
attach-under-reset
boolean
Attach to the chip under hard-reset.
This asserts the reset pin via the probe, plays the protocol init routines and deasserts the pin.
chip-description-path
string
Provides custom target descriptions based on CMSIS Pack files.
See the probe-rs target extraction (opens new window) section for more information.

# Example Configuration

# config.toml for modality-reflector

[plugins.ingest.collectors.defmt-rtt.metadata]
init-task-name = 'init'
attach-timeout = "10s"
chip = 'S32K344'
chip-description-path = "/tmp/S32K_Series.yaml"
protocol = 'swd'
core = 0
reset = true
rtos-mode = 'rtic1'
elf-file = 'firmware.elf'
control-block-address = 0xE50000