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:
- Canonical:
<DataSourcePath>.<SignalName>(e.g.App.Data.DDB.Counter) - 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 |