1474 lines
48 KiB
C++
1474 lines
48 KiB
C++
#include "AdvancedErrorManagement.h"
|
|
#include "Atomic.h"
|
|
#include "ClassRegistryItem.h"
|
|
#include "ConfigurationDatabase.h"
|
|
#include "DataSourceI.h"
|
|
#include "DebugBrokerWrapper.h"
|
|
#include "DebugServiceBase.h"
|
|
#include "GAM.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;
|
|
discoverCacheValid = false;
|
|
treeCacheValid = false;
|
|
}
|
|
|
|
DebugServiceBase::~DebugServiceBase() {
|
|
for (uint32 i = 0u; i < signals.GetNumberOfElements(); 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.GetNumberOfElements(); i++) {
|
|
if (signals[i]->memoryAddress == memoryAddress) {
|
|
res = signals[i];
|
|
sigIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
if (res == NULL_PTR(DebugSignalInfo *)) {
|
|
sigIdx = signals.GetNumberOfElements();
|
|
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.Append(res);
|
|
// Invalidate caches — new signal registered after the last build.
|
|
discoverCacheValid = false;
|
|
treeCacheValid = false;
|
|
}
|
|
if (sigIdx != 0xFFFFFFFFu) {
|
|
bool found = false;
|
|
for (uint32 i = 0u; i < aliases.GetNumberOfElements(); i++) {
|
|
if (aliases[i].name == name) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
SignalAlias a;
|
|
a.name = name;
|
|
a.signalIndex = sigIdx;
|
|
aliases.Append(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,
|
|
Vector<uint32> *activeIndices, Vector<uint32> *activeSizes,
|
|
FastPollingMutexSem *activeMutex, volatile bool *anyBreakFlag,
|
|
Vector<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.Append(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.GetNumberOfElements(); 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.GetNumberOfElements(); 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.GetNumberOfElements(); 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.GetNumberOfElements(); 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.GetNumberOfElements(); 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.GetNumberOfElements(); 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.GetNumberOfElements(); 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;
|
|
Vector<StreamString> parts;
|
|
StreamString token;
|
|
while (fullPath.GetToken(token, ".", term)) {
|
|
parts.Append(token);
|
|
token = "";
|
|
}
|
|
|
|
uint32 count = 0u;
|
|
if (parts.GetNumberOfElements() >= 2u) {
|
|
StreamString signalName = parts[parts.GetNumberOfElements() - 1u];
|
|
StreamString dsPath;
|
|
for (uint32 i = 0u; i < parts.GetNumberOfElements() - 1u; i++) {
|
|
dsPath += parts[i];
|
|
if (i < parts.GetNumberOfElements() - 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.GetNumberOfElements();
|
|
// Re-use existing brokered signal ID if available
|
|
for (uint32 i = 0u; i < aliases.GetNumberOfElements(); i++) {
|
|
if (aliases[i].name == path ||
|
|
SuffixMatch(aliases[i].name.Buffer(), path)) {
|
|
m.internalID = signals[aliases[i].signalIndex]->internalID;
|
|
break;
|
|
}
|
|
}
|
|
monitoredSignals.Append(m);
|
|
count = 1u;
|
|
}
|
|
}
|
|
}
|
|
mutex.FastUnLock();
|
|
return count;
|
|
}
|
|
|
|
uint32 DebugServiceBase::UnmonitorSignal(const char8 *path) {
|
|
mutex.FastLock();
|
|
uint32 count = 0u;
|
|
for (uint32 i = 0u; i < monitoredSignals.GetNumberOfElements(); ) {
|
|
if (monitoredSignals[i].path == path ||
|
|
SuffixMatch(monitoredSignals[i].path.Buffer(), path)) {
|
|
// Shift elements after i one position left, then shrink by one.
|
|
uint32 n = monitoredSignals.GetNumberOfElements();
|
|
for (uint32 k = i; k + 1u < n; k++) {
|
|
monitoredSignals[k] = monitoredSignals[k + 1u];
|
|
}
|
|
monitoredSignals.SetSize(n - 1u);
|
|
count++;
|
|
// Do NOT increment i — re-examine the element now at position i.
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
mutex.FastUnLock();
|
|
return count;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Broker index maintenance
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void DebugServiceBase::UpdateBrokersActiveStatus() {
|
|
for (uint32 i = 0u; i < brokers.GetNumberOfElements(); i++) {
|
|
if (brokers[i].signalPointers == NULL_PTR(DebugSignalInfo **))
|
|
continue;
|
|
uint32 count = 0u;
|
|
Vector<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.Append(j);
|
|
tempSizes.Append((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->SetSize(0u);
|
|
for (uint32 k = 0u; k < tempInd.GetNumberOfElements(); k++)
|
|
brokers[i].activeIndices->Append(tempInd[k]);
|
|
}
|
|
if (brokers[i].activeSizes) {
|
|
brokers[i].activeSizes->SetSize(0u);
|
|
for (uint32 k = 0u; k < tempSizes.GetNumberOfElements(); k++)
|
|
brokers[i].activeSizes->Append(tempSizes[k]);
|
|
}
|
|
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.GetNumberOfElements(); i++) {
|
|
if (brokers[i].signalPointers == NULL_PTR(DebugSignalInfo **))
|
|
continue;
|
|
Vector<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.Append(j);
|
|
count++;
|
|
}
|
|
}
|
|
if (brokers[i].activeMutex)
|
|
brokers[i].activeMutex->FastLock();
|
|
if (brokers[i].breakIndices) {
|
|
brokers[i].breakIndices->SetSize(0u);
|
|
for (uint32 k = 0u; k < tempBreak.GetNumberOfElements(); k++)
|
|
brokers[i].breakIndices->Append(tempBreak[k]);
|
|
}
|
|
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.GetNumberOfElements(); 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) {
|
|
if (!discoverCacheValid)
|
|
BuildDiscoverCache();
|
|
out += discoverCache;
|
|
}
|
|
|
|
void DebugServiceBase::BuildDiscoverCache() {
|
|
discoverCacheValid = false;
|
|
discoverCache = "";
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Phase 1 — snapshot signal metadata under the mutex (fast, no allocs on
|
|
// the RT path because this runs only during initialisation).
|
|
// -----------------------------------------------------------------------
|
|
Vector<StreamString> enames;
|
|
Vector<uint32> eids;
|
|
Vector<const char8*> etypes;
|
|
Vector<uint8> edims;
|
|
Vector<uint32> eelems;
|
|
|
|
mutex.FastLock();
|
|
for (uint32 i = 0u; i < aliases.GetNumberOfElements(); i++) {
|
|
DebugSignalInfo *sig = signals[aliases[i].signalIndex];
|
|
enames.Append(aliases[i].name);
|
|
eids.Append(sig->internalID);
|
|
etypes.Append(TypeDescriptor::GetTypeNameFromTypeDescriptor(sig->type));
|
|
edims.Append(sig->numberOfDimensions);
|
|
eelems.Append(sig->numberOfElements);
|
|
}
|
|
for (uint32 i = 0u; i < monitoredSignals.GetNumberOfElements(); i++) {
|
|
bool found = false;
|
|
for (uint32 j = 0u; j < aliases.GetNumberOfElements(); j++) {
|
|
if (aliases[j].name == monitoredSignals[i].path) { found = true; break; }
|
|
}
|
|
if (!found) {
|
|
uint8 dims = 0u;
|
|
uint32 elems = 1u;
|
|
(void)monitoredSignals[i].dataSource->GetSignalNumberOfDimensions(
|
|
monitoredSignals[i].signalIdx, dims);
|
|
(void)monitoredSignals[i].dataSource->GetSignalNumberOfElements(
|
|
monitoredSignals[i].signalIdx, elems);
|
|
enames.Append(monitoredSignals[i].path);
|
|
eids.Append(monitoredSignals[i].internalID);
|
|
etypes.Append(TypeDescriptor::GetTypeNameFromTypeDescriptor(
|
|
monitoredSignals[i].dataSource->GetSignalType(
|
|
monitoredSignals[i].signalIdx)));
|
|
edims.Append(dims);
|
|
eelems.Append(elems);
|
|
}
|
|
}
|
|
mutex.FastUnLock();
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Phase 2 — build chunked response (no mutex; fullConfig is single-threaded
|
|
// at initialisation time when this is called from SetFullConfig).
|
|
// -----------------------------------------------------------------------
|
|
uint32 total = enames.GetNumberOfElements();
|
|
|
|
if (total == 0u) {
|
|
discoverCache = "{\"Signals\":[]}\nOK DISCOVER\n";
|
|
discoverCacheValid = true;
|
|
return;
|
|
}
|
|
|
|
for (uint32 start = 0u; start < total; start += DISCOVER_CHUNK_SIGNALS) {
|
|
uint32 end = start + DISCOVER_CHUNK_SIGNALS;
|
|
if (end > total) end = total;
|
|
bool isFinal = (end >= total);
|
|
|
|
discoverCache += "{\"Signals\":[\n";
|
|
for (uint32 idx = start; idx < end; idx++) {
|
|
if (idx > start) discoverCache += ",\n";
|
|
discoverCache.Printf(
|
|
" {\"name\":\"%s\",\"id\":%u,\"type\":\"%s\","
|
|
"\"dimensions\":%u,\"elements\":%u",
|
|
enames[idx].Buffer(), eids[idx],
|
|
etypes[idx] ? etypes[idx] : "Unknown",
|
|
edims[idx], eelems[idx]);
|
|
EnrichWithConfig(enames[idx].Buffer(), discoverCache);
|
|
discoverCache += "}";
|
|
}
|
|
discoverCache += "\n]}";
|
|
discoverCache += isFinal ? "\nOK DISCOVER\n" : "\nOK DISCOVER_PART\n";
|
|
}
|
|
|
|
discoverCacheValid = true;
|
|
}
|
|
|
|
void DebugServiceBase::BuildTreeCache() {
|
|
treeCacheValid = false;
|
|
treeCache = "";
|
|
treeCache += "{\"Name\":\"Root\",\"Class\":\"ObjectRegistryDatabase\","
|
|
"\"Children\":[\n";
|
|
(void)ExportTree(ObjectRegistryDatabase::Instance(), treeCache,
|
|
NULL_PTR(const char8 *));
|
|
treeCache += "\n]}\nOK TREE\n";
|
|
treeCacheValid = true;
|
|
}
|
|
|
|
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.GetNumberOfElements(); 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;
|
|
BuildDiscoverCache();
|
|
BuildTreeCache();
|
|
}
|
|
|
|
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") {
|
|
if (!treeCacheValid)
|
|
BuildTreeCache();
|
|
out += treeCache;
|
|
} 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;
|
|
const uint32 DebugServiceBase::DISCOVER_CHUNK_SIGNALS;
|
|
|
|
} // namespace MARTe
|