Major functionality implemented (missing reconfig).

This commit is contained in:
Martino Ferrari
2026-04-14 01:40:43 +02:00
parent 96d98dfc3d
commit 3a1ecd3aba
6 changed files with 1173 additions and 107 deletions
@@ -97,6 +97,7 @@ DebugService::DebugService()
isServer = false;
suppressTimeoutLogs = true;
isPaused = false;
stepRemaining = 0u;
manualConfigSet = false;
activeClient = NULL_PTR(BasicTCPSocket *);
streamerPacketOffset = 0u;
@@ -431,6 +432,8 @@ DebugSignalInfo *DebugService::RegisterSignal(void *memoryAddress,
res->internalID = sigIdx;
res->decimationFactor = 1;
res->decimationCounter = 0;
res->breakOp = BREAK_OFF;
res->breakThreshold = 0.0;
signals.Push(res);
}
if (sigIdx != 0xFFFFFFFF) {
@@ -474,7 +477,10 @@ void DebugService::RegisterBroker(DebugSignalInfo **signalPointers,
volatile bool *anyActiveFlag,
Vec<uint32> *activeIndices,
Vec<uint32> *activeSizes,
FastPollingMutexSem *activeMutex) {
FastPollingMutexSem *activeMutex,
volatile bool *anyBreakFlag,
Vec<uint32> *breakIndices,
const char8 *gamName, bool isOutput) {
mutex.FastLock();
BrokerInfo b;
b.signalPointers = signalPointers;
@@ -484,6 +490,11 @@ void DebugService::RegisterBroker(DebugSignalInfo **signalPointers,
b.activeIndices = activeIndices;
b.activeSizes = activeSizes;
b.activeMutex = activeMutex;
b.anyBreakFlag = anyBreakFlag;
b.breakIndices = breakIndices;
b.isOutput = isOutput;
if (gamName != NULL_PTR(const char8 *))
b.gamName = gamName;
brokers.Push(b);
mutex.FastUnLock();
}
@@ -525,6 +536,28 @@ void DebugService::UpdateBrokersActiveStatus() {
}
}
void DebugService::UpdateBrokersBreakStatus() {
for (uint32 i = 0; i < brokers.Size(); i++) {
Vec<uint32> tempBreak;
uint32 count = 0;
for (uint32 j = 0; j < brokers[i].numSignals; j++) {
DebugSignalInfo *s = brokers[i].signalPointers[j];
if (s != NULL_PTR(DebugSignalInfo *) && s->breakOp != BREAK_OFF) {
tempBreak.Push(j);
count++;
}
}
if (brokers[i].activeMutex)
brokers[i].activeMutex->FastLock();
if (brokers[i].breakIndices)
*(brokers[i].breakIndices) = tempBreak;
if (brokers[i].anyBreakFlag)
*(brokers[i].anyBreakFlag) = (count > 0);
if (brokers[i].activeMutex)
brokers[i].activeMutex->FastUnLock();
}
}
ErrorManagement::ErrorType DebugService::Execute(ExecutionInfo &info) {
return ErrorManagement::FatalError;
}
@@ -731,6 +764,72 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
(void)client->Write(resp.Buffer(), s);
}
}
} else if (token == "STEP") {
StreamString nStr;
uint32 n = 1u;
if (cmd.GetToken(nStr, delims, term)) {
AnyType nVal(UnsignedInteger32Bit, 0u, &n);
AnyType nS(CharString, 0u, nStr.Buffer());
(void)TypeConvert(nVal, nS);
}
// Optional thread name: STEP <n> [<threadName>]
StreamString threadStr;
const char8 *threadArg = NULL_PTR(const char8 *);
if (cmd.GetToken(threadStr, delims, term) && threadStr.Size() > 0u) {
threadArg = threadStr.Buffer();
}
Step(n, threadArg);
if (client) {
StreamString resp;
resp.Printf("OK STEP %u\n", n);
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
}
} else if (token == "STEP_STATUS") {
GetStepStatus(client);
} else if (token == "VALUE") {
StreamString sigName;
if (cmd.GetToken(sigName, delims, term)) {
GetSignalValue(sigName.Buffer(), client);
} else if (client) {
const char8 *errResp = "{\"Error\": \"Missing signal name\"}\nOK VALUE\n";
uint32 s = StringHelper::Length(errResp);
(void)client->Write(errResp, s);
}
} else if (token == "BREAK") {
// BREAK <signal> <op> <threshold> — set break condition
// BREAK <signal> OFF — clear break condition
StreamString name, opStr;
if (cmd.GetToken(name, delims, term) && cmd.GetToken(opStr, delims, term)) {
uint32 count = 0;
if (opStr == "OFF") {
count = ClearBreak(name.Buffer());
} else {
StreamString threshStr;
if (cmd.GetToken(threshStr, delims, term)) {
uint8 op = BREAK_OFF;
if (opStr == ">") op = BREAK_GT;
else if (opStr == "<") op = BREAK_LT;
else if (opStr == "==") op = BREAK_EQ;
else if (opStr == ">=") op = BREAK_GEQ;
else if (opStr == "<=") op = BREAK_LEQ;
else if (opStr == "!=") op = BREAK_NEQ;
if (op != BREAK_OFF) {
float64 threshold = 0.0;
AnyType thrVal(Float64Bit, 0u, &threshold);
AnyType thrStr(CharString, 0u, threshStr.Buffer());
(void)TypeConvert(thrVal, thrStr);
count = SetBreak(name.Buffer(), op, threshold);
}
}
}
if (client) {
StreamString resp;
resp.Printf("OK BREAK %u\n", count);
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
}
}
} else if (token == "DISCOVER")
Discover(client);
else if (token == "MSG") {
@@ -1369,6 +1468,159 @@ uint32 DebugService::TraceSignal(const char8 *name, bool enable,
return count;
}
void DebugService::ConsumeStepIfNeeded(const char8 *gamName,
const char8 *threadName) {
if (stepRemaining == 0u) return;
mutex.FastLock();
// If a thread filter is set, only the matching OS thread consumes step credits.
if (stepThreadFilter.Size() > 0u &&
(threadName == NULL_PTR(const char8 *) || stepThreadFilter != threadName)) {
mutex.FastUnLock();
return;
}
if (stepRemaining > 0u) {
stepRemaining--;
if (stepRemaining == 0u) {
isPaused = true;
pausedAtGam = (gamName != NULL_PTR(const char8 *)) ? gamName : "";
}
}
mutex.FastUnLock();
}
void DebugService::Step(uint32 n, const char8 *threadName) {
mutex.FastLock();
stepRemaining = n;
isPaused = false;
stepThreadFilter = (threadName != NULL_PTR(const char8 *)) ? threadName : "";
mutex.FastUnLock();
}
void DebugService::GetStepStatus(BasicTCPSocket *client) {
if (client == NULL_PTR(BasicTCPSocket *)) return;
mutex.FastLock();
bool paused = isPaused;
uint32 remaining = stepRemaining;
StreamString gam = pausedAtGam;
StreamString threadFilter = stepThreadFilter;
mutex.FastUnLock();
StreamString resp;
resp.Printf("{\"Paused\": %s, \"PausedAtGam\": \"%s\", \"StepRemaining\": %u, \"StepThread\": \"%s\"}\nOK STEP_STATUS\n",
paused ? "true" : "false",
gam.Buffer(),
remaining,
threadFilter.Buffer());
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
}
void DebugService::GetSignalValue(const char8 *name, BasicTCPSocket *client) {
if (client == NULL_PTR(BasicTCPSocket *)) return;
mutex.FastLock();
DebugSignalInfo *sig = NULL_PTR(DebugSignalInfo *);
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == name ||
SuffixMatch(aliases[i].name.Buffer(), name)) {
sig = signals[aliases[i].signalIndex];
break;
}
}
if (sig == NULL_PTR(DebugSignalInfo *)) {
mutex.FastUnLock();
StreamString resp;
resp.Printf("{\"Error\": \"Signal not found: %s\"}\nOK VALUE\n", name);
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
return;
}
TypeDescriptor td = sig->type;
uint32 nElem = sig->numberOfElements;
uint32 byteSize = (td.numberOfBits > 0u) ? (td.numberOfBits / 8u) : 1u;
uint32 totalBytes = byteSize * nElem;
if (totalBytes > 1024u) { totalBytes = 1024u; nElem = totalBytes / byteSize; }
// Copy bytes while holding the mutex to avoid data races with the RT thread
uint8 localBuf[1024];
memset(localBuf, 0, sizeof(localBuf));
if (sig->memoryAddress != NULL_PTR(void *)) {
memcpy(localBuf, sig->memoryAddress, totalBytes);
}
mutex.FastUnLock();
// Build the value string via CharString TypeConvert — the same path used by
// EnrichWithConfig/JsonifyDatabase, which is known to work for all types.
StreamString valueStr;
if (nElem > 1u) {
// Array or matrix: produce comma-separated text
for (uint32 i = 0u; i < nElem; i++) {
char8 elemBuf[128] = {'\0'};
AnyType srcElem(td, 0u, (void *)(localBuf + i * byteSize));
AnyType dstStr(CharString, 0u, elemBuf);
dstStr.SetNumberOfElements(0u, 128u);
(void)TypeConvert(dstStr, srcElem);
if (i > 0u) valueStr += ", ";
valueStr += elemBuf;
}
} else {
char8 elemBuf[256] = {'\0'};
AnyType srcElem(td, 0u, (void *)localBuf);
AnyType dstStr(CharString, 0u, elemBuf);
dstStr.SetNumberOfElements(0u, 256u);
(void)TypeConvert(dstStr, srcElem);
valueStr = elemBuf;
}
StreamString resp;
resp += "{\"Name\": \"";
resp += name;
resp += "\", \"Value\": \"";
// Escape the value text (quotes inside a string value)
const char8 *vp = valueStr.Buffer();
while (vp != NULL_PTR(const char8 *) && *vp != '\0') {
if (*vp == '"') resp += "\\\"";
else if (*vp == '\\') resp += "\\\\";
else { char8 tmp[2] = { *vp, '\0' }; resp += tmp; }
vp++;
}
resp += "\", \"Elements\": ";
resp.Printf("%u}\nOK VALUE\n", nElem);
uint32 s = resp.Size();
(void)client->Write(resp.Buffer(), s);
}
uint32 DebugService::SetBreak(const char8 *name, uint8 op, float64 threshold) {
mutex.FastLock();
uint32 count = 0;
for (uint32 i = 0; i < aliases.Size(); i++) {
if (aliases[i].name == name ||
SuffixMatch(aliases[i].name.Buffer(), name)) {
DebugSignalInfo *s = signals[aliases[i].signalIndex];
s->breakThreshold = threshold;
s->breakOp = op;
count++;
}
}
if (count > 0)
UpdateBrokersBreakStatus();
mutex.FastUnLock();
return count;
}
uint32 DebugService::ClearBreak(const char8 *name) {
mutex.FastLock();
uint32 count = 0;
for (uint32 i = 0; i < aliases.Size(); i++) {
if (aliases[i].name == name ||
SuffixMatch(aliases[i].name.Buffer(), name)) {
signals[aliases[i].signalIndex]->breakOp = BREAK_OFF;
count++;
}
}
if (count > 0)
UpdateBrokersBreakStatus();
mutex.FastUnLock();
return count;
}
bool DebugService::IsInstrumented(const char8 *fullPath, bool &traceable,
bool &forcable) {
mutex.FastLock();