# 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` | | `MemoryMapOutputBroker` | `DebugBrokerWrapper` | | `MemoryMapSynchronisedInputBroker` | `DebugBrokerWrapper` | | `MemoryMapSynchronisedOutputBroker` | `DebugBrokerWrapper` | | `MemoryMapMultiBufferBroker` | `DebugBrokerWrapper` | | `MemoryMapMultiBufferOutputBroker` | `DebugBrokerWrapper` | | `MemoryMapAsynchronousInputBroker` | `DebugBrokerWrapperAsync` | | `MemoryMapAsynchronousOutputBroker` | `DebugBrokerWrapperAsync` | | `MemoryMapInterpolatedInputBroker` | `DebugBrokerWrapper` | | `MemoryMapStatefulOutputBroker` | `DebugBrokerWrapper` | | `MemoryMapStatefulInputBroker` | `DebugBrokerWrapper` | ### 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**: `.` (e.g. `App.Data.DDB.Counter`) 2. **GAM alias**: `.In.` or `.Out.` 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): ```text +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":,"name":"…","alias":"…","type":"…","elements":},...]} OK DISCOVER ``` ### `TREE` Return the live `ObjectRegistryDatabase` hierarchy as JSON. ``` Request: TREE\n Response: {"name":"Root","children":[…]} OK TREE ``` ### `INFO ` Return metadata for a specific ORD node, enriched with config fields. ### `LS [path]` List the immediate children of an ORD node. ### `TRACE <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 \n ``` ### `FORCE ` Inject a persistent value into a signal's memory location every RT cycle. ``` Request: FORCE App.Data.DDB.Counter 9999\n Response: OK FORCE \n ``` ### `UNFORCE ` Remove a forced value; the signal resumes normal data-flow. ### `BREAK ` Set a conditional breakpoint. Supported operators: `>`, `<`, `==`, `>=`, `<=`, `!=`, `OFF`. ``` Request: BREAK App.Data.DDB.Counter > 1000\n Response: OK BREAK \n ``` ### `PAUSE` / `RESUME` Halt or release all patched RT threads. ### `STEP [thread]` Resume for exactly `n` RT output-broker cycles, then pause again. ### `VALUE ` Read the current raw value of a signal. Arrays capped at 256 elements. ### `CONFIG` Return the full application configuration as JSON (requires `SetFullConfig()`). ### `MSG [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: ``` |||\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 |