Files
2026-05-07 10:49:24 +02:00

270 lines
13 KiB
Markdown

# System Architecture
## 1. Overview
The MARTe2 Debug Suite is a C++ server library that instruments running MARTe2 applications
without modifying their source code. It exposes an abstract `DebugServiceI` interface that
decouples the broker injection layer from the communication transport, allowing multiple
transport backends to coexist (currently TCP/UDP and HTTP/SSE).
```
┌────────────────────────────────────────────────────────────────────┐
│ MARTe2 Application │
│ ┌──────────┐ ┌───────────────────────┐ ┌──────────────────┐ │
│ │ GAM(s) │ │ DebugBrokerWrapper │ │ FastScheduler │ │
│ │ │◄──│ (Registry-patched) │ │ (unmodified) │ │
│ └──────────┘ └──────────┬────────────┘ └──────────────────┘ │
│ │ RT-path API │
└────────────────────────────┼───────────────────────────────────────┘
┌────────▼────────┐
│ DebugServiceI │ ← Abstract interface (singleton)
└────────┬────────┘
┌─────────────┴──────────────┐
│ │
┌────────▼────────┐ ┌─────────▼────────┐
│ DebugService │ │ WebDebugService │
│ TCP/UDP │ │ HTTP/SSE │
└────────┬────────┘ └─────────┬─────────┘
│ │
┌──────────┼──────────┐ ┌──────────┼──────────┐
│TCP 8080 │UDP 8081 │ │HTTP 8090 │SSE stream │
│(commands)│(telemetry│ │(commands)│(telemetry)│
└────┬─────┴────┬─────┘ └─────┬────┴────┬──────┘
│ │ │ │
┌────▼──────────▼─────┐ ┌─────────▼─────────▼──────┐
│ Rust/egui GUI │ │ Web Browser (built-in UI)│
│ (Tools/gui_client) │ │ http://localhost:8090 │
└─────────────────────┘ └───────────────────────────┘
```
---
## 2. Core Mechanism: Registry Patching
The "Zero-Code-Change" requirement is met by intercepting the MARTe2 `ClassRegistryDatabase`.
When `DebugService` or `WebDebugService` initialises, `PatchRegistry()` replaces the
`ObjectBuilder` for every standard `MemoryMap*Broker` class in the registry. 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>` |
---
## 3. DebugServiceI Abstraction Layer
`Source/Components/Interfaces/DebugService/DebugServiceI.h` defines the abstract singleton
interface. It has two logical groups:
### 3.1 RT-path API
Called from broker threads on every RT cycle. Must be as cheap as possible; the only
permissible synchronisation primitive is `FastPollingMutexSem`.
| Method | Purpose |
|---|---|
| `RegisterSignal(addr, type, name, …)` | Called once per signal during broker `Init()` |
| `ProcessSignal(info, size, timestamp)` | Apply forcing, check break, push trace sample |
| `RegisterBroker(…)` | Cache broker metadata for active/break index updates |
| `IsPaused() / SetPaused(bool)` | Query / set global pause state |
| `ConsumeStepIfNeeded(gamName, thread)` | Decrement step counter on output-broker Execute |
### 3.2 Control-path API
Called from server/command-handler threads; latency acceptable.
| Method | Purpose |
|---|---|
| `ForceSignal / UnforceSignal` | Persistent value injection |
| `TraceSignal(name, enable, decimation)` | Enable/disable ring-buffer tracing |
| `SetBreak / ClearBreak` | Conditional breakpoints |
| `IsInstrumented(path, …)` | Query whether a signal is reachable |
| `RegisterMonitorSignal / UnmonitorSignal` | Slow-rate signal polling |
### 3.3 Static singleton pattern
```cpp
// Concrete implementation registers itself:
DebugServiceI::SetInstance(this); // in Initialise()
DebugServiceI::SetInstance(NULL_PTR()); // in destructor
// Broker wrappers retrieve it (no ORD search on the hot path):
DebugServiceI *svc = DebugServiceI::GetInstance();
if (svc != NULL_PTR(DebugServiceI*)) { ... }
```
Shared data structures (`SignalAlias`, `BrokerInfo`) are also defined in `DebugServiceI.h`
so both transport implementations can use them without a circular dependency.
---
## 4. Transport Implementations
### 4.1 DebugService (TCP/UDP)
`Source/Components/Interfaces/DebugService/DebugService.h/.cpp`
Runs two `SingleThreadService` threads:
| Thread | Role |
|---|---|
| `Server()` | Accepts one TCP client at a time; reads text commands; writes JSON/text responses |
| `Streamer()` | Drains `TraceRingBuffer`; assembles and sends UDP datagrams; polls monitored signals |
Communication ports (all configurable):
| Port | Protocol | Purpose |
|---|---|---|
| `ControlPort` (default 8080) | TCP text | Commands and responses |
| `UdpPort` (default 8081) | UDP binary | High-speed telemetry (`TraceHeader` + samples) |
| `LogPort` (default 8082) | TCP stream | Log forwarding via `TcpLogger` sidecar |
### 4.2 WebDebugService (HTTP/SSE)
`Source/Components/Interfaces/WebDebugService/WebDebugService.h/.cpp`
Serves a single HTTP port (default 8090) with an embedded browser UI. Runs two
`SingleThreadService` threads:
| Thread | Role |
|---|---|
| `HttpServer()` | Accepts TCP connections; routes GET/POST/OPTIONS; upgrades SSE clients |
| `Streamer()` | Drains `TraceRingBuffer` → SSE events; polls monitored signals; heartbeat |
HTTP endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
| `/` | GET | Serves embedded single-page application (HTML/CSS/JS) |
| `/api/command` | POST | Execute a debug command; returns text response |
| `/api/events` | GET | Server-Sent Events stream for real-time telemetry (up to 8 clients) |
The embedded web UI (`Source/Components/Interfaces/WebDebugService/WebUI.h`) is a
self-contained single-page application using Chart.js for real-time signal plots and a
Catppuccin Mocha dark theme. No install or server-side tooling required.
---
## 5. Signal Flow
### 5.1 Registration (startup, single-threaded)
```
ConfigureApplication()
└─► DebugBrokerWrapper::Init()
└─► DebugBrokerHelper::InitSignals()
├─► DebugServiceI::RegisterSignal() (canonical name + GAM alias)
└─► DebugServiceI::RegisterBroker()
```
### 5.2 RT loop (hot path, multi-threaded)
```
RealTimeThread::Execute()
└─► DebugBrokerWrapper::Execute()
├─► Base::Execute() (data moved in/out of GAM memory)
└─► DebugBrokerHelper::Process()
├─► For each active signal:
│ └─► DebugServiceI::ProcessSignal()
│ ├─► if isForcing: memcpy forcedValue → signal memory
│ ├─► if isTracing & decimation fires:
│ │ └─► tracePushMutex → TraceRingBuffer::Push()
│ └─► if breakOp set: evaluate condition → SetPaused(true)
└─► (output brokers only) DebugServiceI::ConsumeStepIfNeeded()
```
### 5.3 Telemetry delivery
**DebugService (UDP)**
```
Streamer thread
└─► TraceRingBuffer::Pop()
└─► assemble into streamerPacketBuffer (MTU=1400)
└─► BasicUDPSocket::Write() → client
```
**WebDebugService (SSE)**
```
Streamer thread
└─► TraceRingBuffer::Pop()
└─► BroadcastSse("message", {"type":"trace","name":…,"value":…})
└─► write "event: message\ndata: {…}\n\n" to each SSE socket
```
---
## 6. TraceRingBuffer
Single-producer / single-consumer circular byte buffer 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; one Streamer reads).
- `Pop()` is called exclusively by the Streamer thread.
- Ring is initialised to 4 MB in both transport implementations.
- On corrupt `size` field (`size >= bufferSize`): discards all entries to prevent cascading
bad reads (Fix #5).
---
## 7. Performance Hardening
The following fixes were applied after initial implementation to improve correctness and
real-time safety.
| Fix | Area | Change |
|---|---|---|
| **#1** | UDP streamer | Named constants `STREAMER_MTU = 1400` and `STREAMER_BUFFER_SIZE = 4096`; removed magic numbers |
| **#2** | Multi-producer ring | `tracePushMutex` serialises `TraceRingBuffer::Push()` from concurrent RT threads |
| **#3** | Break evaluation | Break indices copied to stack-local array while holding `activeMutex`; condition evaluated outside the lock |
| **#4** | Decimation counter | `Atomic::Add` used for lock-free per-signal decimation in multi-producer `ProcessSignal` |
| **#5** | Ring corruption | `Pop()` skips only the current corrupt entry; falls back to full discard only when `size >= bufferSize` |
| **#6** | TCP rate limit | Server disconnects clients sending > 100 commands/second (`CMD_RATE_LIMIT = 100`) |
| **#7** | Broker index swap | `Vec::Swap` inside spinlock followed by free outside; avoids holding the lock during deallocation |
| **#8** | Idle timeout | Active TCP client disconnected after 30 s of silence (`CLIENT_IDLE_TIMEOUT_MS = 30000`) |
| **#9** | VALUE output size | `GetSignalValue` caps output at 256 elements (`GET_VALUE_MAX_ELEMENTS`); includes `"Truncated"` flag |
| **#10** | TCP framing | `inputBuffer` accumulates partial commands across `Read()` calls; bounded at 8 KiB (`INPUT_BUFFER_MAX`) |
| **#11** | CDB cursor | `Initialise()` never calls `MoveToRoot()` on the CDB passed by the framework; avoids corrupting `ReferenceContainer` traversal |
---
## 8. Repository Layout
```
Source/
Core/Types/
Vec/ — RT-safe dynamic array (no STL)
Result/ — Error-carrying return type
Components/Interfaces/
DebugService/
DebugCore.h — DebugSignalInfo, TraceRingBuffer, BreakOp
DebugServiceI.h — Abstract interface + SignalAlias, BrokerInfo
DebugBrokerWrapper.h — DebugBrokerHelper, wrapper templates, registry builder
DebugService.h/.cpp — TCP/UDP transport implementation
WebDebugService/
WebUI.h — Embedded single-page HTML/CSS/JS application
WebDebugService.h/.cpp — HTTP/SSE transport implementation
TCPLogger/
TcpLogger.h/.cpp — LoggerConsumerI forwarding logs to TCP clients
Test/
Integration/ — Integration test suite (180 s SIGALRM guard)
UnitTests/ — Unit test suite
Tools/
gui_client/ — Rust/egui native GUI (DebugService TCP/UDP transport)
```