interface added and web ui created

This commit is contained in:
Martino Ferrari
2026-05-07 10:49:24 +02:00
parent 340b024ae4
commit 774808a054
21 changed files with 4986 additions and 362 deletions
@@ -3,7 +3,7 @@
#include "BrokerI.h"
#include "DataSourceI.h"
#include "DebugService.h"
#include "DebugServiceI.h"
#include "FastPollingMutexSem.h"
#include "HighResolutionTimer.h"
#include "MemoryMapBroker.h"
@@ -90,8 +90,11 @@ public:
// Spins while paused, then consumes one step counter tick (if stepping).
// Input brokers must NOT call this — they complete normally to avoid blocking
// cross-thread EventSem posts (RealTimeThreadSynchBroker would time out).
static void OutputPauseAndStep(DebugService *service, const char8 *gamName) {
if (service == NULL_PTR(DebugService *)) return;
static void OutputPauseAndStep(DebugServiceI *service, const char8 *gamName) {
if (service == NULL_PTR(DebugServiceI *)) return;
// Fast path: nothing to do when neither paused nor stepping.
// Avoids Threads::Name() lookup (and its mutex) on every 1000 Hz cycle.
if (!service->IsPaused() && !service->IsStepPending()) return;
// Wait if already paused (manual PAUSE or breakpoint from a previous cycle)
while (service->IsPaused()) Sleep::MSec(10);
// Pass the OS thread name so per-thread step filtering works.
@@ -100,13 +103,13 @@ public:
while (service->IsPaused()) Sleep::MSec(10);
}
static void Process(DebugService *service,
static void Process(DebugServiceI *service,
DebugSignalInfo **signalInfoPointers,
Vec<uint32> &activeIndices, Vec<uint32> &activeSizes,
FastPollingMutexSem &activeMutex,
volatile bool *anyBreakFlag,
Vec<uint32> *breakIndices) {
if (service == NULL_PTR(DebugService *))
if (service == NULL_PTR(DebugServiceI *))
return;
// NOTE: No spin here. Spinning for paused state is handled in Execute() of
@@ -118,7 +121,7 @@ public:
if (n > 0 && signalInfoPointers != NULL_PTR(DebugSignalInfo **)) {
// Capture timestamp ONCE per broker cycle for lowest impact
uint64 ts = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000000.0);
HighResolutionTimer::Period() * 1.0e9);
for (uint32 i = 0; i < n; i++) {
uint32 idx = activeIndices[i];
@@ -130,13 +133,33 @@ public:
}
}
// Conditional break check — zero cost when anyBreakFlag is false
// (single volatile read; the RT loop never pays for this when no break is set).
if (*anyBreakFlag && !service->IsPaused() &&
breakIndices != NULL_PTR(Vec<uint32> *)) {
uint32 nb = breakIndices->Size();
// FIX #3: copy break indices under lock into a local stack array, then
// release activeMutex BEFORE evaluating. EvaluateBreak reads signal
// memory which can stall (cache miss); holding activeMutex during that
// stall would block UpdateBrokersBreakStatus() in the Server thread and
// cause unnecessary priority inversion on the RT path.
bool shouldCheckBreak = (*anyBreakFlag && !service->IsPaused() &&
breakIndices != NULL_PTR(Vec<uint32> *) &&
signalInfoPointers != NULL_PTR(DebugSignalInfo **));
static const uint32 MAX_BREAK_INDICES = 64u;
uint32 localBreakIdx[MAX_BREAK_INDICES];
uint32 nb = 0u;
if (shouldCheckBreak) {
nb = breakIndices->Size();
if (nb > MAX_BREAK_INDICES) nb = MAX_BREAK_INDICES;
for (uint32 i = 0; i < nb; i++) {
uint32 idx = (*breakIndices)[i];
localBreakIdx[i] = (*breakIndices)[i];
}
}
activeMutex.FastUnLock();
// Evaluate break conditions outside the lock — safe because
// EvaluateBreak only reads signalInfoPointers[idx]->memoryAddress,
// which is RT data-bus memory and is never freed during the RT cycle.
if (shouldCheckBreak && nb > 0u) {
for (uint32 i = 0; i < nb; i++) {
uint32 idx = localBreakIdx[i];
DebugSignalInfo *s = signalInfoPointers[idx];
if (s != NULL_PTR(DebugSignalInfo *) && s->breakOp != BREAK_OFF &&
EvaluateBreak(s)) {
@@ -145,14 +168,12 @@ public:
}
}
}
activeMutex.FastUnLock();
}
// Pass numCopies explicitly so we can mock it
static void
InitSignals(BrokerI *broker, DataSourceI &dataSourceIn,
DebugService *&service, DebugSignalInfo **&signalInfoPointers,
DebugServiceI *&service, DebugSignalInfo **&signalInfoPointers,
uint32 numCopies, MemoryMapBrokerCopyTableEntry *copyTable,
const char8 *functionName, SignalDirection direction,
volatile bool *anyActiveFlag, Vec<uint32> *activeIndices,
@@ -164,16 +185,14 @@ public:
signalInfoPointers[i] = NULL_PTR(DebugSignalInfo *);
}
ReferenceContainer *root = ObjectRegistryDatabase::Instance();
Reference serviceRef = root->Find("DebugService");
if (serviceRef.IsValid()) {
service = dynamic_cast<DebugService *>(serviceRef.operator->());
}
// Use the singleton registered by DebugService::Initialise() — no ORD
// search on the Init path, and no dependency on a concrete type.
service = DebugServiceI::GetInstance();
if (service && (copyTable != NULL_PTR(MemoryMapBrokerCopyTableEntry *))) {
StreamString dsPath;
DebugService::GetFullObjectName(dataSourceIn, dsPath);
DebugServiceI::GetFullObjectName(dataSourceIn, dsPath);
fprintf(stderr, ">> %s broker for %s [%d]\n",
direction == InputSignals ? "Input" : "Output", dsPath.Buffer(),
numCopies);
@@ -225,7 +244,7 @@ public:
if (gamRef.IsValid()) {
StreamString absGamPath;
DebugService::GetFullObjectName(*(gamRef.operator->()), absGamPath);
DebugServiceI::GetFullObjectName(*(gamRef.operator->()), absGamPath);
// Register short path (In/Out) for GUI compatibility
gamFullName.Printf("%s.%s.%s", absGamPath.Buffer(), dirStrShort,
signalName.Buffer());
@@ -261,7 +280,7 @@ public:
template <typename BaseClass> class DebugBrokerWrapper : public BaseClass {
public:
DebugBrokerWrapper() : BaseClass() {
service = NULL_PTR(DebugService *);
service = NULL_PTR(DebugServiceI *);
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
numSignals = 0;
anyActive = false;
@@ -326,7 +345,7 @@ public:
return ret;
}
DebugService *service;
DebugServiceI *service;
DebugSignalInfo **signalInfoPointers;
uint32 numSignals;
volatile bool anyActive;
@@ -343,7 +362,7 @@ template <typename BaseClass>
class DebugBrokerWrapperNoOptim : public BaseClass {
public:
DebugBrokerWrapperNoOptim() : BaseClass() {
service = NULL_PTR(DebugService *);
service = NULL_PTR(DebugServiceI *);
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
numSignals = 0;
anyActive = false;
@@ -386,7 +405,7 @@ public:
return ret;
}
DebugService *service;
DebugServiceI *service;
DebugSignalInfo **signalInfoPointers;
uint32 numSignals;
volatile bool anyActive;
@@ -402,7 +421,7 @@ public:
class DebugMemoryMapAsyncOutputBroker : public MemoryMapAsyncOutputBroker {
public:
DebugMemoryMapAsyncOutputBroker() : MemoryMapAsyncOutputBroker() {
service = NULL_PTR(DebugService *);
service = NULL_PTR(DebugServiceI *);
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
numSignals = 0;
anyActive = false;
@@ -446,7 +465,7 @@ public:
}
return ret;
}
DebugService *service;
DebugServiceI *service;
DebugSignalInfo **signalInfoPointers;
uint32 numSignals;
volatile bool anyActive;
@@ -463,7 +482,7 @@ class DebugMemoryMapAsyncTriggerOutputBroker
public:
DebugMemoryMapAsyncTriggerOutputBroker()
: MemoryMapAsyncTriggerOutputBroker() {
service = NULL_PTR(DebugService *);
service = NULL_PTR(DebugServiceI *);
signalInfoPointers = NULL_PTR(DebugSignalInfo **);
numSignals = 0;
anyActive = false;
@@ -506,7 +525,7 @@ public:
}
return ret;
}
DebugService *service;
DebugServiceI *service;
DebugSignalInfo **signalInfoPointers;
uint32 numSignals;
volatile bool anyActive;