242 lines
7.7 KiB
Markdown
242 lines
7.7 KiB
Markdown
# 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):
|
|
|
|
```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":<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 |
|