Files
Martino Ferrari 617b5bd712 Initial release
2026-05-29 13:29:59 +02:00

7.7 KiB

DebugService

DebugService is a MARTe2 Object that instruments a running MARTe2 application for real-time signal tracing, forcing, and breakpoints — without modifying the application source code.

It lives at Source/Components/Interfaces/DebugService/ in this repository and is built alongside TCPLogger at Source/Components/Interfaces/TCPLogger/.


Architecture

Core Mechanism: Registry Patching

On Initialise(), PatchRegistry() replaces the ObjectBuilder for every standard MemoryMap*Broker class in the ClassRegistryDatabase. When RealTimeApplication::ConfigureApplication() runs afterward it instantiates wrapped broker objects instead of the originals. The application sees no difference.

Patched broker types:

Class Wrapper
MemoryMapInputBroker DebugBrokerWrapper<MemoryMapInputBroker>
MemoryMapOutputBroker DebugBrokerWrapper<MemoryMapOutputBroker>
MemoryMapSynchronisedInputBroker DebugBrokerWrapper<MemoryMapSynchronisedInputBroker>
MemoryMapSynchronisedOutputBroker DebugBrokerWrapper<MemoryMapSynchronisedOutputBroker>
MemoryMapMultiBufferBroker DebugBrokerWrapper<MemoryMapMultiBufferBroker>
MemoryMapMultiBufferOutputBroker DebugBrokerWrapper<MemoryMapMultiBufferOutputBroker>
MemoryMapAsynchronousInputBroker DebugBrokerWrapperAsync<MemoryMapAsynchronousInputBroker>
MemoryMapAsynchronousOutputBroker DebugBrokerWrapperAsync<MemoryMapAsynchronousOutputBroker>
MemoryMapInterpolatedInputBroker DebugBrokerWrapper<MemoryMapInterpolatedInputBroker>
MemoryMapStatefulOutputBroker DebugBrokerWrapper<MemoryMapStatefulOutputBroker>
MemoryMapStatefulInputBroker DebugBrokerWrapper<MemoryMapStatefulInputBroker>

DebugServiceI Abstraction

DebugServiceI.h defines an abstract singleton interface decoupling the broker injection layer from the transport:

  • RT-path API (called every RT cycle): RegisterSignal, ProcessSignal, RegisterBroker, IsPaused/SetPaused, ConsumeStepIfNeeded
  • Control-path API: ForceSignal/UnforceSignal, TraceSignal, SetBreak/ClearBreak, RegisterMonitorSignal/UnmonitorSignal

Signal Registration and Aliases

Each signal is registered with two names:

  1. Canonical: <DataSourcePath>.<SignalName> (e.g. App.Data.DDB.Counter)
  2. GAM alias: <GAMPath>.In.<SignalName> or <GAMPath>.Out.<SignalName>

Both map to the same DebugSignalInfo*. Alias lookup is bidirectional — a short unqualified name matches any longer canonical or alias path.

Communication Channels

Port Protocol Purpose
8080 (default) TCP text Command and control
8081 (default) UDP binary High-speed telemetry stream
8082 (default) TCP stream Log forwarding via TcpLogger

TraceRingBuffer

Single-producer/single-consumer circular byte buffer (4 MB default) defined in DebugCore.h. Buffer layout per entry: [ID:4][Timestamp:8][Size:4][Data:N].

Push() is serialised by tracePushMutex (multiple RT threads write). Pop() is called exclusively by the Streamer thread.


Configuration

Add DebugService as a sibling of +App (not inside it):

+DebugService = {
    Class       = DebugService
    ControlPort = 8080
    UdpPort     = 8081
    LogPort     = 8082
}

+Logger = {
    Class = TcpLogger
    Port  = 8082
}

+App = {
    Class = RealTimeApplication
    ...
}

Call SetFullConfig(cdb) after ConfigureApplication() to enable the CONFIG and INFO commands.


TCP Command Interface (port 8080)

Commands are newline-terminated (\n) UTF-8 text strings. One client is served at a time. Rate limit: 100 commands/second. Idle timeout: 30 seconds.

DISCOVER

List all registered signals.

Request:  DISCOVER\n
Response: {"Signals":[{"id":<n>,"name":"…","alias":"…","type":"…","elements":<n>},...]}
          OK DISCOVER

TREE

Return the live ObjectRegistryDatabase hierarchy as JSON.

Request:  TREE\n
Response: {"name":"Root","children":[…]}
          OK TREE

INFO <path>

Return metadata for a specific ORD node, enriched with config fields.

LS [path]

List the immediate children of an ORD node.

TRACE <name> <0|1> [decimation]

Enable or disable high-speed tracing. Optional decimation controls how many RT cycles are skipped between samples (default 1 = every cycle).

Request:  TRACE App.Data.DDB.Counter 1\n
Response: OK TRACE <match_count>\n

FORCE <name> <value>

Inject a persistent value into a signal's memory location every RT cycle.

Request:  FORCE App.Data.DDB.Counter 9999\n
Response: OK FORCE <match_count>\n

UNFORCE <name>

Remove a forced value; the signal resumes normal data-flow.

BREAK <name> <op> <threshold>

Set a conditional breakpoint. Supported operators: >, <, ==, >=, <=, !=, OFF.

Request:  BREAK App.Data.DDB.Counter > 1000\n
Response: OK BREAK <match_count>\n

PAUSE / RESUME

Halt or release all patched RT threads.

STEP <n> [thread]

Resume for exactly n RT output-broker cycles, then pause again.

VALUE <name>

Read the current raw value of a signal. Arrays capped at 256 elements.

CONFIG

Return the full application configuration as JSON (requires SetFullConfig()).

MSG <object> <function> [key=value …]

Send a MARTe2 Message to any object in the ORD.

SERVICE_INFO

Return metadata about the debug service (ports, transport type).


UDP Telemetry Format (port 8081)

Packets are little-endian and packed.

TraceHeader (20 bytes)

Offset Type Field Description
0 uint32 magic Always 0xDA7A57AD
4 uint32 seq Monotonically incrementing sequence number
8 uint64 timestamp High-resolution hardware timestamp
16 uint32 count Number of samples in this datagram

Sample Entry (per signal)

Offset Type Field Description
0 uint32 id Signal ID from DISCOVER
4 uint64 timestamp Per-sample RT timestamp
12 uint32 size Payload size in bytes
16 bytes data Raw signal memory

Multiple samples are packed into one datagram up to STREAMER_MTU = 1400 bytes.


Log Forwarding (TcpLogger, port 8082)

TcpLogger connects to the MARTe2 global LoggerI and forwards every REPORT_ERROR call:

<level>|<object>|<function>|<message>\n

Up to 8 simultaneous log clients supported.


Performance Hardening

Fix Change
Multi-producer ring tracePushMutex serialises TraceRingBuffer::Push()
Break evaluation Break indices copied to stack-local array before evaluating outside the lock
Decimation counter Atomic::Add for lock-free per-signal decimation in multi-producer ProcessSignal
Ring corruption Pop() discards all entries only when size >= bufferSize
TCP rate limit Disconnect clients sending > 100 commands/second
Idle timeout Disconnect active TCP client after 30 s of silence
TCP framing inputBuffer accumulates partial commands across Read() calls; bounded at 8 KiB
VALUE output size Caps output at 256 elements; includes "Truncated" flag

Key Source Files

File Purpose
DebugCore.h DebugSignalInfo struct, TraceRingBuffer, BreakOp enum
DebugServiceI.h Abstract singleton interface, SignalAlias, BrokerInfo
DebugBrokerWrapper.h DebugBrokerHelper, wrapper templates, DebugBrokerBuilder
DebugService.h/.cpp TCP/UDP transport implementation; Server() and Streamer() threads
DebugServiceBase.h/.cpp Shared signal registry and alias matching logic
TcpLogger.h/.cpp LoggerConsumerI forwarding REPORT_ERROR events to TCP clients