Files
marte-debug/Source/Components/Interfaces/DebugService/DebugServiceBase.cpp
T
2026-05-07 12:12:14 +02:00

1220 lines
49 KiB
C++

#include "AdvancedErrorManagement.h"
#include "Atomic.h"
#include "ClassRegistryItem.h"
#include "ConfigurationDatabase.h"
#include "DataSourceI.h"
#include "DebugBrokerWrapper.h"
#include "DebugServiceBase.h"
#include "ErrorManagement.h"
#include "GAM.h"
#include "GlobalObjectsDatabase.h"
#include "HighResolutionTimer.h"
#include "Message.h"
#include "MessageI.h"
#include "ObjectRegistryDatabase.h"
#include "ReferenceT.h"
#include "StreamString.h"
#include "TimeoutType.h"
#include "TypeConversion.h"
namespace MARTe {
// DebugServiceI static member — defined here (only once, shared by all transports).
DebugServiceI *DebugServiceI::instance = NULL_PTR(DebugServiceI *);
// ---------------------------------------------------------------------------
// DebugServiceI::GetFullObjectName — static utility used by broker wrappers
// ---------------------------------------------------------------------------
bool DebugServiceI::GetFullObjectName(const Object &obj, StreamString &fullPath) {
fullPath = "";
// FindPathInContainer is a file-scope static below; forward-declare it here.
// The actual definition appears after this function.
extern bool DSB_FindPath(ReferenceContainer *, const Object *, StreamString &);
if (DSB_FindPath(ObjectRegistryDatabase::Instance(), &obj, fullPath)) {
return true;
}
const char8 *name = obj.GetName();
if (name != NULL_PTR(const char8 *))
fullPath = name;
return true;
}
// ---------------------------------------------------------------------------
// File-scope helper functions (NOT exported)
// ---------------------------------------------------------------------------
static void EscapeJson(const char8 *src, StreamString &dst) {
if (src == NULL_PTR(const char8 *)) return;
while (*src != '\0') {
if (*src == '"') dst += "\\\"";
else if (*src == '\\') dst += "\\\\";
else if (*src == '\n') dst += "\\n";
else if (*src == '\r') dst += "\\r";
else if (*src == '\t') dst += "\\t";
else dst += *src;
src++;
}
}
static bool SuffixMatch(const char8 *target, const char8 *pattern) {
uint32 tLen = StringHelper::Length(target);
uint32 pLen = StringHelper::Length(pattern);
if (pLen > tLen) return false;
const char8 *suffix = target + (tLen - pLen);
if (StringHelper::Compare(suffix, pattern) == 0) {
if (tLen == pLen || *(suffix - 1) == '.') return true;
}
return false;
}
// Named with DSB_ prefix so GetFullObjectName can forward-declare it.
bool DSB_FindPath(ReferenceContainer *container, const Object *target,
StreamString &path) {
if (container == NULL_PTR(ReferenceContainer *)) return false;
uint32 n = container->Size();
for (uint32 i = 0u; i < n; i++) {
Reference ref = container->Get(i);
if (!ref.IsValid()) continue;
if (ref.operator->() == target) { path = ref->GetName(); return true; }
ReferenceContainer *sub = dynamic_cast<ReferenceContainer *>(ref.operator->());
if (sub != NULL_PTR(ReferenceContainer *)) {
if (DSB_FindPath(sub, target, path)) {
StreamString full;
full.Printf("%s.%s", ref->GetName(), path.Buffer());
path = full;
return true;
}
}
}
return false;
}
static void BuildCDBFromContainer(ReferenceContainer *container,
ConfigurationDatabase &cdb) {
if (container == NULL_PTR(ReferenceContainer *)) return;
uint32 n = container->Size();
for (uint32 i = 0u; i < n; i++) {
Reference child = container->Get(i);
if (!child.IsValid()) continue;
const char8 *name = child->GetName();
if (name == NULL_PTR(const char8 *)) continue;
bool created = cdb.CreateRelative(name);
if (!created) { if (!cdb.MoveRelative(name)) continue; }
const char8 *cn = child->GetClassProperties()->GetName();
if (cn != NULL_PTR(const char8 *)) (void)cdb.Write("Class", cn);
{
ConfigurationDatabase exp;
if (child->ExportData(exp)) {
exp.MoveToRoot();
uint32 ne = exp.GetNumberOfChildren();
for (uint32 j = 0u; j < ne; j++) {
const char8 *ek = exp.GetChildName(j);
if (StringHelper::Compare(ek, "Class") == 0 ||
StringHelper::Compare(ek, "Name") == 0 ||
StringHelper::Compare(ek, "IsContainer") == 0) continue;
if (exp.MoveRelative(ek)) { exp.MoveToAncestor(1u); continue; }
AnyType at = exp.GetType(ek);
if (at.GetDataPointer() != NULL_PTR(void *)) {
char8 buf[1024];
AnyType st(CharString, 0u, buf);
st.SetNumberOfElements(0, 1024);
if (TypeConvert(st, at)) (void)cdb.Write(ek, buf);
}
}
}
}
ReferenceContainer *rc = dynamic_cast<ReferenceContainer *>(child.operator->());
if (rc != NULL_PTR(ReferenceContainer *)) BuildCDBFromContainer(rc, cdb);
(void)cdb.MoveToAncestor(1u);
}
}
static void PatchItemInternal(const char8 *originalName, ObjectBuilder *debugBuilder) {
ClassRegistryItem *item = ClassRegistryDatabase::Instance()->Find(originalName);
if (item != NULL_PTR(ClassRegistryItem *)) {
item->SetObjectBuilder(debugBuilder);
}
}
// ---------------------------------------------------------------------------
// DebugServiceBase constructor / destructor
// ---------------------------------------------------------------------------
DebugServiceBase::DebugServiceBase()
: ReferenceContainer() {
isPaused = false;
stepRemaining = 0u;
manualConfigSet = false;
}
DebugServiceBase::~DebugServiceBase() {
for (uint32 i = 0u; i < signals.Size(); i++) {
if (signals[i] != NULL_PTR(DebugSignalInfo *)) {
delete signals[i];
}
}
}
// ---------------------------------------------------------------------------
// PatchRegistry
// ---------------------------------------------------------------------------
void DebugServiceBase::PatchRegistry() {
PatchItemInternal("MemoryMapInputBroker",
new DebugMemoryMapInputBrokerBuilder());
PatchItemInternal("MemoryMapOutputBroker",
new DebugMemoryMapOutputBrokerBuilder());
PatchItemInternal("MemoryMapSynchronisedInputBroker",
new DebugMemoryMapSynchronisedInputBrokerBuilder());
PatchItemInternal("MemoryMapSynchronisedOutputBroker",
new DebugMemoryMapSynchronisedOutputBrokerBuilder());
PatchItemInternal("MemoryMapInterpolatedInputBroker",
new DebugMemoryMapInterpolatedInputBrokerBuilder());
PatchItemInternal("MemoryMapMultiBufferInputBroker",
new DebugMemoryMapMultiBufferInputBrokerBuilder());
PatchItemInternal("MemoryMapMultiBufferOutputBroker",
new DebugMemoryMapMultiBufferOutputBrokerBuilder());
PatchItemInternal("MemoryMapSynchronisedMultiBufferInputBroker",
new DebugMemoryMapSynchronisedMultiBufferInputBrokerBuilder());
PatchItemInternal("MemoryMapSynchronisedMultiBufferOutputBroker",
new DebugMemoryMapSynchronisedMultiBufferOutputBrokerBuilder());
PatchItemInternal("MemoryMapAsyncOutputBroker",
new DebugMemoryMapAsyncOutputBrokerBuilder());
PatchItemInternal("MemoryMapAsyncTriggerOutputBroker",
new DebugMemoryMapAsyncTriggerOutputBrokerBuilder());
}
// ---------------------------------------------------------------------------
// RT-path implementations
// ---------------------------------------------------------------------------
DebugSignalInfo *DebugServiceBase::RegisterSignal(void *memoryAddress,
TypeDescriptor type,
const char8 *name,
uint8 numberOfDimensions,
uint32 numberOfElements) {
mutex.FastLock();
DebugSignalInfo *res = NULL_PTR(DebugSignalInfo *);
uint32 sigIdx = 0xFFFFFFFFu;
for (uint32 i = 0u; i < signals.Size(); i++) {
if (signals[i]->memoryAddress == memoryAddress) {
res = signals[i]; sigIdx = i; break;
}
}
if (res == NULL_PTR(DebugSignalInfo *)) {
sigIdx = signals.Size();
res = new DebugSignalInfo();
res->memoryAddress = memoryAddress;
res->type = type;
res->name = name;
res->numberOfDimensions = numberOfDimensions;
res->numberOfElements = numberOfElements;
res->isTracing = false;
res->isForcing = false;
res->internalID = sigIdx;
res->decimationFactor = 1u;
res->decimationCounter = 0;
res->breakOp = BREAK_OFF;
res->breakThreshold = 0.0;
signals.Push(res);
}
if (sigIdx != 0xFFFFFFFFu) {
bool found = false;
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == name) { found = true; break; }
}
if (!found) {
SignalAlias a;
a.name = name;
a.signalIndex = sigIdx;
aliases.Push(a);
}
}
mutex.FastUnLock();
return res;
}
void DebugServiceBase::ProcessSignal(DebugSignalInfo *signalInfo,
uint32 size, uint64 timestamp) {
if (signalInfo == NULL_PTR(DebugSignalInfo *)) return;
if (signalInfo->isForcing) {
memcpy(signalInfo->memoryAddress, signalInfo->forcedValue, size);
}
if (signalInfo->isTracing) {
Atomic::Add((volatile int32 *)&signalInfo->decimationCounter, 1);
if ((uint32)signalInfo->decimationCounter >= signalInfo->decimationFactor) {
int32 old = Atomic::Exchange((volatile int32 *)&signalInfo->decimationCounter, 0);
if ((uint32)old >= signalInfo->decimationFactor) {
tracePushMutex.FastLock();
traceBuffer.Push(signalInfo->internalID, timestamp,
(uint8 *)signalInfo->memoryAddress, size);
tracePushMutex.FastUnLock();
}
}
}
}
void DebugServiceBase::RegisterBroker(DebugSignalInfo **signalPointers,
uint32 numSignals,
MemoryMapBroker *broker,
volatile bool *anyActiveFlag,
Vec<uint32> *activeIndices,
Vec<uint32> *activeSizes,
FastPollingMutexSem *activeMutex,
volatile bool *anyBreakFlag,
Vec<uint32> *breakIndices,
const char8 *gamName,
bool isOutput) {
mutex.FastLock();
BrokerInfo b;
b.signalPointers = signalPointers;
b.numSignals = numSignals;
b.broker = broker;
b.anyActiveFlag = anyActiveFlag;
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();
}
void DebugServiceBase::ConsumeStepIfNeeded(const char8 *gamName,
const char8 *threadName) {
if (stepRemaining == 0u) return;
mutex.FastLock();
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();
}
// ---------------------------------------------------------------------------
// Control-path implementations
// ---------------------------------------------------------------------------
uint32 DebugServiceBase::ForceSignal(const char8 *name, const char8 *valueStr) {
mutex.FastLock();
uint32 count = 0u;
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == name || SuffixMatch(aliases[i].name.Buffer(), name)) {
DebugSignalInfo *s = signals[aliases[i].signalIndex];
uint32 elemBytes = (uint32)(s->type.numberOfBits) / 8u;
uint32 totalBytes = elemBytes * s->numberOfElements;
if (elemBytes == 0u || totalBytes > (uint32)sizeof(s->forcedValue)) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"ForceSignal: signal '%s' requires %u bytes but forcedValue "
"buffer is only %u bytes — force request ignored.",
s->name.Buffer(), totalBytes, (uint32)sizeof(s->forcedValue));
continue;
}
s->isForcing = true;
AnyType dest(s->type, 0u, s->forcedValue);
AnyType source(CharString, 0u, valueStr);
(void)TypeConvert(dest, source);
count++;
}
}
UpdateBrokersActiveStatus();
mutex.FastUnLock();
return count;
}
uint32 DebugServiceBase::UnforceSignal(const char8 *name) {
mutex.FastLock();
uint32 count = 0u;
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == name || SuffixMatch(aliases[i].name.Buffer(), name)) {
signals[aliases[i].signalIndex]->isForcing = false;
count++;
}
}
UpdateBrokersActiveStatus();
mutex.FastUnLock();
return count;
}
uint32 DebugServiceBase::TraceSignal(const char8 *name, bool enable,
uint32 decimation) {
mutex.FastLock();
uint32 count = 0u;
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == name || SuffixMatch(aliases[i].name.Buffer(), name)) {
DebugSignalInfo *s = signals[aliases[i].signalIndex];
s->isTracing = enable;
s->decimationFactor = decimation;
s->decimationCounter = 0;
count++;
}
}
UpdateBrokersActiveStatus();
mutex.FastUnLock();
return count;
}
uint32 DebugServiceBase::SetBreak(const char8 *name, uint8 op,
float64 threshold) {
mutex.FastLock();
uint32 count = 0u;
for (uint32 i = 0u; 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 > 0u) UpdateBrokersBreakStatus();
mutex.FastUnLock();
return count;
}
uint32 DebugServiceBase::ClearBreak(const char8 *name) {
mutex.FastLock();
uint32 count = 0u;
for (uint32 i = 0u; 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 > 0u) UpdateBrokersBreakStatus();
mutex.FastUnLock();
return count;
}
bool DebugServiceBase::IsInstrumented(const char8 *fullPath,
bool &traceable, bool &forcable) {
mutex.FastLock();
bool found = false;
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == fullPath ||
SuffixMatch(aliases[i].name.Buffer(), fullPath)) {
found = true; break;
}
}
mutex.FastUnLock();
traceable = found;
forcable = found;
return found;
}
uint32 DebugServiceBase::RegisterMonitorSignal(const char8 *path,
uint32 periodMs) {
mutex.FastLock();
// If already monitored, just update period
for (uint32 j = 0u; j < monitoredSignals.Size(); j++) {
if (monitoredSignals[j].path == path) {
monitoredSignals[j].periodMs = periodMs;
mutex.FastUnLock();
return 1u;
}
}
// Tokenise path to separate DataSource path from signal name
StreamString fullPath = path;
fullPath.Seek(0u);
char8 term;
Vec<StreamString> parts;
StreamString token;
while (fullPath.GetToken(token, ".", term)) { parts.Push(token); token = ""; }
uint32 count = 0u;
if (parts.Size() >= 2u) {
StreamString signalName = parts[parts.Size() - 1u];
StreamString dsPath;
for (uint32 i = 0u; i < parts.Size() - 1u; i++) {
dsPath += parts[i];
if (i < parts.Size() - 2u) dsPath += ".";
}
ReferenceT<DataSourceI> ds =
ObjectRegistryDatabase::Instance()->Find(dsPath.Buffer());
if (ds.IsValid()) {
uint32 idx = 0u;
if (ds->GetSignalIndex(idx, signalName.Buffer())) {
MonitoredSignal m;
m.dataSource = ds;
m.signalIdx = idx;
m.path = path;
m.periodMs = periodMs;
m.lastPollTime = 0u;
m.size = 0u;
(void)ds->GetSignalByteSize(idx, m.size);
if (m.size == 0u) m.size = 4u;
m.internalID = 0x80000000u | monitoredSignals.Size();
// Re-use existing brokered signal ID if available
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == path ||
SuffixMatch(aliases[i].name.Buffer(), path)) {
m.internalID = signals[aliases[i].signalIndex]->internalID;
break;
}
}
monitoredSignals.Push(m);
count = 1u;
}
}
}
mutex.FastUnLock();
return count;
}
uint32 DebugServiceBase::UnmonitorSignal(const char8 *path) {
mutex.FastLock();
uint32 count = 0u;
for (uint32 i = 0u; i < monitoredSignals.Size(); i++) {
if (monitoredSignals[i].path == path ||
SuffixMatch(monitoredSignals[i].path.Buffer(), path)) {
(void)monitoredSignals.Remove(i); i--; count++;
}
}
mutex.FastUnLock();
return count;
}
// ---------------------------------------------------------------------------
// Broker index maintenance
// ---------------------------------------------------------------------------
void DebugServiceBase::UpdateBrokersActiveStatus() {
for (uint32 i = 0u; i < brokers.Size(); i++) {
if (brokers[i].signalPointers == NULL_PTR(DebugSignalInfo **)) continue;
uint32 count = 0u;
Vec<uint32> tempInd, tempSizes;
for (uint32 j = 0u; j < brokers[i].numSignals; j++) {
DebugSignalInfo *s = brokers[i].signalPointers[j];
if (s != NULL_PTR(DebugSignalInfo *) && (s->isTracing || s->isForcing)) {
tempInd.Push(j);
tempSizes.Push((brokers[i].broker != NULL_PTR(MemoryMapBroker *))
? brokers[i].broker->GetCopyByteSize(j) : 4u);
count++;
}
}
if (brokers[i].activeMutex) brokers[i].activeMutex->FastLock();
if (brokers[i].activeIndices) brokers[i].activeIndices->Swap(tempInd);
if (brokers[i].activeSizes) brokers[i].activeSizes->Swap(tempSizes);
if (brokers[i].anyActiveFlag) *(brokers[i].anyActiveFlag) = (count > 0u);
if (brokers[i].activeMutex) brokers[i].activeMutex->FastUnLock();
}
}
void DebugServiceBase::UpdateBrokersBreakStatus() {
for (uint32 i = 0u; i < brokers.Size(); i++) {
if (brokers[i].signalPointers == NULL_PTR(DebugSignalInfo **)) continue;
Vec<uint32> tempBreak;
uint32 count = 0u;
for (uint32 j = 0u; 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->Swap(tempBreak);
if (brokers[i].anyBreakFlag) *(brokers[i].anyBreakFlag) = (count > 0u);
if (brokers[i].activeMutex) brokers[i].activeMutex->FastUnLock();
}
}
// ---------------------------------------------------------------------------
// Step control
// ---------------------------------------------------------------------------
void DebugServiceBase::Step(uint32 n, const char8 *threadName) {
mutex.FastLock();
stepRemaining = n;
isPaused = false;
stepThreadFilter = (threadName != NULL_PTR(const char8 *)) ? threadName : "";
mutex.FastUnLock();
}
// ---------------------------------------------------------------------------
// Command-channel output helpers
// ---------------------------------------------------------------------------
void DebugServiceBase::GetStepStatus(StreamString &out) {
mutex.FastLock();
bool paused = isPaused;
uint32 rem = stepRemaining;
StreamString gam = pausedAtGam;
StreamString thr = stepThreadFilter;
mutex.FastUnLock();
out.Printf("{\"Paused\":%s,\"PausedAtGam\":\"%s\","
"\"StepRemaining\":%u,\"StepThread\":\"%s\"}\nOK STEP_STATUS\n",
paused ? "true" : "false", gam.Buffer(), rem, thr.Buffer());
}
void DebugServiceBase::GetSignalValue(const char8 *name, StreamString &out) {
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();
out.Printf("{\"Error\":\"Signal not found: %s\"}\nOK VALUE\n", name);
return;
}
TypeDescriptor td = sig->type;
uint32 nElem = sig->numberOfElements;
uint32 bySz = (td.numberOfBits > 0u) ? (td.numberOfBits / 8u) : 1u;
bool trunc = (nElem > GET_VALUE_MAX_ELEMENTS);
if (trunc) nElem = GET_VALUE_MAX_ELEMENTS;
uint32 total = bySz * nElem;
if (total > 1024u) { total = 1024u; nElem = total / bySz; }
uint8 lb[1024]; memset(lb, 0, sizeof(lb));
if (sig->memoryAddress != NULL_PTR(void *)) memcpy(lb, sig->memoryAddress, total);
mutex.FastUnLock();
StreamString valueStr;
if (nElem > 1u) {
for (uint32 i = 0u; i < nElem; i++) {
char8 eb[128] = { '\0' };
AnyType se(td, 0u, (void *)(lb + i * bySz));
AnyType ds(CharString, 0u, eb);
ds.SetNumberOfElements(0u, 128u);
(void)TypeConvert(ds, se);
if (i > 0u) valueStr += ", ";
valueStr += eb;
}
} else {
char8 eb[256] = { '\0' };
AnyType se(td, 0u, (void *)lb);
AnyType ds(CharString, 0u, eb);
ds.SetNumberOfElements(0u, 256u);
(void)TypeConvert(ds, se);
valueStr = eb;
}
out += "{\"Name\":\"";
out += name;
out += "\",\"Value\":\"";
const char8 *vp = valueStr.Buffer();
while (vp != NULL_PTR(const char8 *) && *vp != '\0') {
if (*vp == '"') out += "\\\"";
else if (*vp == '\\') out += "\\\\";
else { char8 tmp[2] = { *vp, '\0' }; out += tmp; }
vp++;
}
out.Printf("\",\"Elements\":%u,\"Truncated\":%s}\nOK VALUE\n",
nElem, trunc ? "true" : "false");
}
void DebugServiceBase::Discover(StreamString &out) {
out += "{\"Signals\":[\n";
mutex.FastLock();
uint32 total = 0u;
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (total > 0u) out += ",\n";
DebugSignalInfo *sig = signals[aliases[i].signalIndex];
const char8 *tn = TypeDescriptor::GetTypeNameFromTypeDescriptor(sig->type);
out.Printf(" {\"name\":\"%s\",\"id\":%u,\"type\":\"%s\","
"\"dimensions\":%u,\"elements\":%u",
aliases[i].name.Buffer(), sig->internalID,
tn ? tn : "Unknown", sig->numberOfDimensions, sig->numberOfElements);
EnrichWithConfig(aliases[i].name.Buffer(), out);
out += "}";
total++;
}
for (uint32 i = 0u; i < monitoredSignals.Size(); i++) {
bool found = false;
for (uint32 j = 0u; j < aliases.Size(); j++) {
if (aliases[j].name == monitoredSignals[i].path) { found = true; break; }
}
if (!found) {
if (total > 0u) out += ",\n";
const char8 *tn = TypeDescriptor::GetTypeNameFromTypeDescriptor(
monitoredSignals[i].dataSource->GetSignalType(monitoredSignals[i].signalIdx));
uint8 dims = 0u; uint32 elems = 1u;
(void)monitoredSignals[i].dataSource->GetSignalNumberOfDimensions(
monitoredSignals[i].signalIdx, dims);
(void)monitoredSignals[i].dataSource->GetSignalNumberOfElements(
monitoredSignals[i].signalIdx, elems);
out.Printf(" {\"name\":\"%s\",\"id\":%u,\"type\":\"%s\","
"\"dimensions\":%u,\"elements\":%u",
monitoredSignals[i].path.Buffer(), monitoredSignals[i].internalID,
tn ? tn : "Unknown", dims, elems);
EnrichWithConfig(monitoredSignals[i].path.Buffer(), out);
out += "}";
total++;
}
}
mutex.FastUnLock();
out += "\n]}\nOK DISCOVER\n";
}
void DebugServiceBase::InfoNode(const char8 *path, StreamString &out) {
Reference ref = ObjectRegistryDatabase::Instance()->Find(path);
out += "{";
if (ref.IsValid()) {
out += "\"Name\":\""; EscapeJson(ref->GetName(), out);
out += "\",\"Class\":\""; EscapeJson(ref->GetClassProperties()->GetName(), out);
out += "\"";
ConfigurationDatabase db;
if (ref->ExportData(db)) {
out += ",\"Config\":{";
db.MoveToRoot();
uint32 nc = db.GetNumberOfChildren();
for (uint32 i = 0u; i < nc; i++) {
const char8 *cn = db.GetChildName(i);
AnyType at = db.GetType(cn);
char8 buf[1024]; AnyType st(CharString, 0u, buf); st.SetNumberOfElements(0, 1024);
if (TypeConvert(st, at)) {
out += "\""; EscapeJson(cn, out); out += "\":\"";
EscapeJson(buf, out); out += "\"";
if (i < nc - 1u) out += ",";
}
}
out += "}";
}
EnrichWithConfig(path, out);
} else {
StreamString enrichAlias;
mutex.FastLock();
bool found = false;
for (uint32 i = 0u; i < aliases.Size(); i++) {
if (aliases[i].name == path ||
SuffixMatch(aliases[i].name.Buffer(), path)) {
DebugSignalInfo *s = signals[aliases[i].signalIndex];
const char8 *tn = TypeDescriptor::GetTypeNameFromTypeDescriptor(s->type);
out.Printf("\"Name\":\"%s\",\"Class\":\"Signal\",\"Type\":\"%s\","
"\"ID\":%u",
s->name.Buffer(), tn ? tn : "Unknown", s->internalID);
enrichAlias = aliases[i].name;
found = true; break;
}
}
mutex.FastUnLock();
if (found) EnrichWithConfig(enrichAlias.Buffer(), out);
else out += "\"Error\":\"Object not found\"";
}
out += "}\nOK INFO\n";
}
void DebugServiceBase::ListNodes(const char8 *path, StreamString &out) {
Reference ref =
(path == NULL_PTR(const char8 *) || StringHelper::Length(path) == 0 ||
StringHelper::Compare(path, "/") == 0)
? ObjectRegistryDatabase::Instance()
: ObjectRegistryDatabase::Instance()->Find(path);
out.Printf("Nodes under %s:\n", path ? path : "/");
if (ref.IsValid()) {
ReferenceContainer *rc = dynamic_cast<ReferenceContainer *>(ref.operator->());
if (rc != NULL_PTR(ReferenceContainer *)) {
uint32 n = rc->Size();
for (uint32 i = 0u; i < n; i++) {
Reference c = rc->Get(i);
if (c.IsValid()) {
out.Printf(" %s [%s]\n", c->GetName(),
c->GetClassProperties()->GetName());
}
}
}
} else {
out += " (not found)\n";
}
out += "OK LS\n";
}
void DebugServiceBase::ServeConfig(StreamString &out) {
if (!manualConfigSet) RebuildConfigFromRegistry();
fullConfig.MoveToRoot();
JsonifyDatabase(fullConfig, out);
out += "\nOK CONFIG\n";
}
// ---------------------------------------------------------------------------
// Config management
// ---------------------------------------------------------------------------
void DebugServiceBase::SetFullConfig(ConfigurationDatabase &config) {
config.MoveToRoot();
config.Copy(fullConfig);
manualConfigSet = true;
}
void DebugServiceBase::RebuildConfigFromRegistry() {
fullConfig = ConfigurationDatabase();
BuildCDBFromContainer(ObjectRegistryDatabase::Instance(), fullConfig);
// Let the transport subclass write back its specific port numbers etc.
RebuildTransportConfig();
}
// ---------------------------------------------------------------------------
// Tree export
// ---------------------------------------------------------------------------
uint32 DebugServiceBase::ExportTree(ReferenceContainer *container,
StreamString &json,
const char8 *pathPrefix) {
if (container == NULL_PTR(ReferenceContainer *)) return 0u;
uint32 size = container->Size();
uint32 valid = 0u;
for (uint32 i = 0u; i < size; i++) {
Reference child = container->Get(i);
if (!child.IsValid()) continue;
if (valid > 0u) json += ",\n";
const char8 *cname = child->GetName();
if (cname == NULL_PTR(const char8 *)) cname = "unnamed";
StreamString cp;
if (pathPrefix != NULL_PTR(const char8 *)) cp.Printf("%s.%s", pathPrefix, cname);
else cp = cname;
StreamString nj;
nj += "{\"Name\":\""; EscapeJson(cname, nj);
nj += "\",\"Class\":\""; EscapeJson(child->GetClassProperties()->GetName(), nj);
nj += "\"";
ReferenceContainer *inner = dynamic_cast<ReferenceContainer *>(child.operator->());
DataSourceI *ds = dynamic_cast<DataSourceI *>(child.operator->());
GAM *gam = dynamic_cast<GAM *>(child.operator->());
if (inner != NULL_PTR(ReferenceContainer *) ||
ds != NULL_PTR(DataSourceI *) ||
gam != NULL_PTR(GAM *)) {
nj += ",\"Children\":[\n";
uint32 sc = 0u;
if (inner != NULL_PTR(ReferenceContainer *))
sc += ExportTree(inner, nj, cp.Buffer());
if (ds != NULL_PTR(DataSourceI *)) {
uint32 ns = ds->GetNumberOfSignals();
for (uint32 j = 0u; j < ns; j++) {
if (sc > 0u) { nj += ",\n"; } sc++;
StreamString sn; (void)ds->GetSignalName(j, sn);
const char8 *st = TypeDescriptor::GetTypeNameFromTypeDescriptor(ds->GetSignalType(j));
uint8 d = 0u; (void)ds->GetSignalNumberOfDimensions(j, d);
uint32 el = 0u; (void)ds->GetSignalNumberOfElements(j, el);
StreamString sfp; sfp.Printf("%s.%s", cp.Buffer(), sn.Buffer());
bool tr = false, fo = false; (void)IsInstrumented(sfp.Buffer(), tr, fo);
nj += "{\"Name\":\""; EscapeJson(sn.Buffer(), nj);
nj += "\",\"Class\":\"Signal\",\"Type\":\""; EscapeJson(st ? st : "Unknown", nj);
nj.Printf("\",\"Dimensions\":%u,\"Elements\":%u,"
"\"IsTraceable\":%s,\"IsForcable\":%s}",
d, el, tr ? "true" : "false", fo ? "true" : "false");
}
}
if (gam != NULL_PTR(GAM *)) {
uint32 nIn = gam->GetNumberOfInputSignals();
for (uint32 j = 0u; j < nIn; j++) {
if (sc > 0u) { nj += ",\n"; } sc++;
StreamString sn; (void)gam->GetSignalName(InputSignals, j, sn);
const char8 *st = TypeDescriptor::GetTypeNameFromTypeDescriptor(
gam->GetSignalType(InputSignals, j));
uint32 d = 0u; (void)gam->GetSignalNumberOfDimensions(InputSignals, j, d);
uint32 el = 0u; (void)gam->GetSignalNumberOfElements(InputSignals, j, el);
StreamString sfp; sfp.Printf("%s.In.%s", cp.Buffer(), sn.Buffer());
bool tr = false, fo = false; (void)IsInstrumented(sfp.Buffer(), tr, fo);
nj += "{\"Name\":\"In."; EscapeJson(sn.Buffer(), nj);
nj += "\",\"Class\":\"InputSignal\",\"Type\":\"";
EscapeJson(st ? st : "Unknown", nj);
nj.Printf("\",\"Dimensions\":%u,\"Elements\":%u,"
"\"IsTraceable\":%s,\"IsForcable\":%s}",
d, el, tr ? "true" : "false", fo ? "true" : "false");
}
uint32 nOut = gam->GetNumberOfOutputSignals();
for (uint32 j = 0u; j < nOut; j++) {
if (sc > 0u) { nj += ",\n"; } sc++;
StreamString sn; (void)gam->GetSignalName(OutputSignals, j, sn);
const char8 *st = TypeDescriptor::GetTypeNameFromTypeDescriptor(
gam->GetSignalType(OutputSignals, j));
uint32 d = 0u; (void)gam->GetSignalNumberOfDimensions(OutputSignals, j, d);
uint32 el = 0u; (void)gam->GetSignalNumberOfElements(OutputSignals, j, el);
StreamString sfp; sfp.Printf("%s.Out.%s", cp.Buffer(), sn.Buffer());
bool tr = false, fo = false; (void)IsInstrumented(sfp.Buffer(), tr, fo);
nj += "{\"Name\":\"Out."; EscapeJson(sn.Buffer(), nj);
nj += "\",\"Class\":\"OutputSignal\",\"Type\":\"";
EscapeJson(st ? st : "Unknown", nj);
nj.Printf("\",\"Dimensions\":%u,\"Elements\":%u,"
"\"IsTraceable\":%s,\"IsForcable\":%s}",
d, el, tr ? "true" : "false", fo ? "true" : "false");
}
}
nj += "\n]";
}
nj += "}";
json += nj;
valid++;
}
return valid;
}
// ---------------------------------------------------------------------------
// EnrichWithConfig
// ---------------------------------------------------------------------------
void DebugServiceBase::EnrichWithConfig(const char8 *path, StreamString &json) {
if (path == NULL_PTR(const char8 *)) return;
if (!manualConfigSet) RebuildConfigFromRegistry();
fullConfig.MoveToRoot();
const char8 *current = path;
bool ok = true;
while (ok) {
const char8 *nextDot = StringHelper::SearchString(current, ".");
StreamString part;
if (nextDot != NULL_PTR(const char8 *)) {
uint32 len = (uint32)(nextDot - current);
(void)part.Write(current, len);
current = nextDot + 1;
} else {
part = current; ok = false;
}
bool found = false;
if (fullConfig.MoveRelative(part.Buffer())) { found = true; }
if (!found) {
StreamString pref; pref.Printf("+%s", part.Buffer());
if (fullConfig.MoveRelative(pref.Buffer())) found = true;
}
if (!found) {
if (part == "In") {
if (fullConfig.MoveRelative("InputSignals")) found = true;
else if (fullConfig.MoveRelative("+InputSignals")) found = true;
} else if (part == "Out") {
if (fullConfig.MoveRelative("OutputSignals")) found = true;
else if (fullConfig.MoveRelative("+OutputSignals")) found = true;
}
}
if (!found) { fullConfig.MoveToRoot(); return; }
}
ConfigurationDatabase db;
fullConfig.Copy(db);
fullConfig.MoveToRoot();
db.MoveToRoot();
uint32 n = db.GetNumberOfChildren();
for (uint32 i = 0u; i < n; i++) {
const char8 *name = db.GetChildName(i);
AnyType at = db.GetType(name);
if (!at.GetTypeDescriptor().isStructuredData) {
json += ", \"";
EscapeJson(name, json);
json += "\": \"";
char8 buf[1024];
AnyType st(CharString, 0u, buf);
st.SetNumberOfElements(0, 1024);
if (TypeConvert(st, at)) EscapeJson(buf, json);
json += "\"";
}
}
}
// ---------------------------------------------------------------------------
// JsonifyDatabase (static)
// ---------------------------------------------------------------------------
void DebugServiceBase::JsonifyDatabase(ConfigurationDatabase &db,
StreamString &json) {
json += "{";
uint32 n = db.GetNumberOfChildren();
bool first = true;
for (uint32 i = 0u; i < n; i++) {
const char8 *key = db.GetChildName(i);
if (key == NULL_PTR(const char8 *)) continue;
if (!first) json += ", ";
first = false;
json += "\""; EscapeJson(key, json); json += "\": ";
if (db.MoveRelative(key)) {
JsonifyDatabase(db, json);
(void)db.MoveToAncestor(1u);
} else {
AnyType at = db.GetType(key);
if (at.GetDataPointer() != NULL_PTR(void *)) {
char8 buf[512] = { '\0' };
AnyType dst(CharString, 0u, buf);
dst.SetNumberOfElements(0u, 512u);
if (TypeConvert(dst, at)) {
json += "\""; EscapeJson(buf, json); json += "\"";
} else {
json += "null";
}
} else {
json += "null";
}
}
}
json += "}";
}
// ---------------------------------------------------------------------------
// HandleCommand — unified command dispatcher
// ---------------------------------------------------------------------------
void DebugServiceBase::HandleCommand(const StreamString &cmdIn, StreamString &out) {
StreamString cmd = cmdIn;
StreamString token;
cmd.Seek(0u);
char8 term;
const char8 *delims = " \r\n";
if (!cmd.GetToken(token, delims, term)) return;
if (token == "FORCE") {
StreamString name, val;
if (cmd.GetToken(name, delims, term) && cmd.GetToken(val, delims, term)) {
uint32 c = ForceSignal(name.Buffer(), val.Buffer());
out.Printf("OK FORCE %u\n", c);
}
} else if (token == "UNFORCE") {
StreamString name;
if (cmd.GetToken(name, delims, term)) {
uint32 c = UnforceSignal(name.Buffer());
out.Printf("OK UNFORCE %u\n", c);
}
} else if (token == "TRACE") {
StreamString name, state, decim;
if (cmd.GetToken(name, delims, term) && cmd.GetToken(state, delims, term)) {
bool en = (state == "1");
uint32 d = 1u;
if (cmd.GetToken(decim, delims, term)) {
AnyType dv(UnsignedInteger32Bit, 0u, &d);
AnyType ds(CharString, 0u, decim.Buffer());
(void)TypeConvert(dv, ds);
}
uint32 c = TraceSignal(name.Buffer(), en, d);
out.Printf("OK TRACE %u\n", c);
}
} else if (token == "BREAK") {
StreamString name, opStr;
if (cmd.GetToken(name, delims, term) && cmd.GetToken(opStr, delims, term)) {
uint32 c = 0u;
if (opStr == "OFF") {
c = ClearBreak(name.Buffer());
} else {
StreamString thrStr;
if (cmd.GetToken(thrStr, 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 thr = 0.0;
AnyType tv(Float64Bit, 0u, &thr);
AnyType ts(CharString, 0u, thrStr.Buffer());
(void)TypeConvert(tv, ts);
c = SetBreak(name.Buffer(), op, thr);
}
}
}
out.Printf("OK BREAK %u\n", c);
}
} else if (token == "PAUSE") {
SetPaused(true);
out += "OK\n";
} else if (token == "RESUME") {
SetPaused(false);
out += "OK\n";
} else if (token == "STEP") {
StreamString nStr;
uint32 n = 1u;
if (cmd.GetToken(nStr, delims, term)) {
AnyType nv(UnsignedInteger32Bit, 0u, &n);
AnyType ns(CharString, 0u, nStr.Buffer());
(void)TypeConvert(nv, ns);
}
StreamString thr;
const char8 *ta = NULL_PTR(const char8 *);
if (cmd.GetToken(thr, delims, term) && thr.Size() > 0u) ta = thr.Buffer();
Step(n, ta);
out.Printf("OK STEP %u\n", n);
} else if (token == "STEP_STATUS") {
GetStepStatus(out);
} else if (token == "VALUE") {
StreamString sn;
if (cmd.GetToken(sn, delims, term)) {
GetSignalValue(sn.Buffer(), out);
} else {
out += "{\"Error\":\"Missing signal name\"}\nOK VALUE\n";
}
} else if (token == "DISCOVER") {
Discover(out);
} else if (token == "TREE") {
out += "{\"Name\":\"Root\",\"Class\":\"ObjectRegistryDatabase\","
"\"Children\":[\n";
(void)ExportTree(ObjectRegistryDatabase::Instance(), out,
NULL_PTR(const char8 *));
out += "\n]}\nOK TREE\n";
} else if (token == "INFO") {
StreamString path;
if (cmd.GetToken(path, delims, term)) InfoNode(path.Buffer(), out);
} else if (token == "LS") {
StreamString path;
if (cmd.GetToken(path, delims, term)) ListNodes(path.Buffer(), out);
else ListNodes(NULL_PTR(const char8 *), out);
} else if (token == "CONFIG") {
ServeConfig(out);
} else if (token == "MONITOR") {
StreamString sub, name, period;
if (cmd.GetToken(sub, delims, term) && sub == "SIGNAL" &&
cmd.GetToken(name, delims, term) &&
cmd.GetToken(period, delims, term)) {
uint32 p = 100u;
AnyType pv(UnsignedInteger32Bit, 0u, &p);
AnyType ps(CharString, 0u, period.Buffer());
(void)TypeConvert(pv, ps);
uint32 c = RegisterMonitorSignal(name.Buffer(), p);
out.Printf("OK MONITOR %u\n", c);
}
} else if (token == "UNMONITOR") {
StreamString sub, name;
if (cmd.GetToken(sub, delims, term) && sub == "SIGNAL" &&
cmd.GetToken(name, delims, term)) {
uint32 c = UnmonitorSignal(name.Buffer());
out.Printf("OK UNMONITOR %u\n", c);
}
} else if (token == "SERVICE_INFO") {
GetServiceInfo(out);
} else if (token == "MSG") {
// MSG <destination> <function> <wait(0|1)> [key=value\nkey=value...]
StreamString dest, func, waitStr;
if (cmd.GetToken(dest, delims, term) &&
cmd.GetToken(func, delims, term) &&
cmd.GetToken(waitStr, delims, term)) {
bool wait = (waitStr == "1");
const char8 *pStart = cmd.Buffer() + cmd.Position();
StreamString rawPayload = pStart;
// Decode escaped newlines (\n → real newline)
StreamString payload;
rawPayload.Seek(0u);
char8 c;
while (rawPayload.Size() > rawPayload.Position()) {
uint32 rs = 1u;
if (rawPayload.Read(&c, rs)) {
if (c == '\\') {
char8 next;
if (rawPayload.Read(&next, rs)) {
if (next == 'n') payload += '\n';
else { payload += c; payload += next; }
} else {
payload += c;
}
} else {
payload += c;
}
}
}
ReferenceT<Message> msg(
"Message", GlobalObjectsDatabase::Instance()->GetStandardHeap());
ConfigurationDatabase msgConfig;
msgConfig.Write("Destination", dest.Buffer());
msgConfig.Write("Function", func.Buffer());
if (wait) msgConfig.Write("Mode", "ExpectsReply");
ReferenceT<ConfigurationDatabase> paramCdb(
"ConfigurationDatabase",
GlobalObjectsDatabase::Instance()->GetStandardHeap());
if (payload.Size() > 0u && paramCdb.IsValid()) {
payload.Seek(0u);
StreamString line;
while (payload.GetToken(line, "\n", term)) {
if (line.Size() > 0u) {
const char8 *eq = StringHelper::SearchChar(line.Buffer(), '=');
if (eq != NULL_PTR(const char8 *)) {
uint32 eqPos = (uint32)(eq - line.Buffer());
static const uint32 KEY_BUF_SIZE = 256u;
static const uint32 VAL_BUF_SIZE = 1024u;
if (eqPos >= KEY_BUF_SIZE) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"MSG: key length %u exceeds buffer (%u) — line skipped.",
eqPos, KEY_BUF_SIZE);
line = ""; continue;
}
(void)line.Seek(0u);
char8 keyBuf[KEY_BUF_SIZE];
MemoryOperationsHelper::Set(keyBuf, '\0', KEY_BUF_SIZE);
uint32 keyReadSize = eqPos;
(void)line.Read(keyBuf, keyReadSize);
(void)line.Seek(eqPos + 1u);
uint32 valLen = (uint32)(line.Size() - eqPos - 1u);
if (valLen >= VAL_BUF_SIZE) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"MSG: value length %u truncated to %u for key '%s'.",
valLen, VAL_BUF_SIZE - 1u, keyBuf);
valLen = VAL_BUF_SIZE - 1u;
}
char8 valBuf[VAL_BUF_SIZE];
MemoryOperationsHelper::Set(valBuf, '\0', VAL_BUF_SIZE);
(void)line.Read(valBuf, valLen);
// Trim trailing whitespace from value
for (int32 ti = (int32)valLen - 1; ti >= 0; ti--) {
if (valBuf[ti] == ' ' || valBuf[ti] == '\r' ||
valBuf[ti] == '\t')
valBuf[ti] = '\0';
else break;
}
// Trim leading whitespace from key
const char8 *kp = keyBuf;
while (*kp == ' ' || *kp == '\t') kp++;
if (*kp != '\0') (void)paramCdb->Write(kp, valBuf);
}
line = "";
}
}
}
ErrorManagement::ErrorType err = ErrorManagement::ParametersError;
if (msg->Initialise(msgConfig)) {
if (paramCdb.IsValid() && payload.Size() > 0u) {
(void)msg->Insert(paramCdb);
}
Reference destObj = ObjectRegistryDatabase::Instance()->Find(dest.Buffer());
if (destObj.IsValid()) {
StreamString myPath;
Object *sender = this;
if (!GetFullObjectName(*this, myPath)) {
sender = NULL_PTR(Object *);
}
if (wait) {
err = MessageI::WaitForReply(msg, TTInfiniteWait);
} else {
(void)MessageI::SendMessage(msg, sender);
err = ErrorManagement::NoError;
}
} else {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"MSG: destination '%s' not found in ORD.", dest.Buffer());
}
} else {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"MSG: Message initialisation failed.");
}
if (err == ErrorManagement::NoError) out += "OK MSG\n";
else out += "ERROR MSG\n";
}
}
}
// ---------------------------------------------------------------------------
// Out-of-class constant definitions (C++98 ODR requirement)
// ---------------------------------------------------------------------------
const uint32 DebugServiceBase::GET_VALUE_MAX_ELEMENTS;
} // namespace MARTe