major improvements for big apps

This commit is contained in:
Martino Ferrari
2026-05-20 17:21:33 +02:00
parent f6eb0a7056
commit 74816028a8
10 changed files with 651 additions and 121 deletions
@@ -324,6 +324,8 @@ ErrorManagement::ErrorType DebugService::Server(ExecutionInfo &info) {
if (out.Size() > 0u) {
const char8 *wPtr = out.Buffer();
uint32 remaining = (uint32)out.Size();
lastDataTimeMs = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000.0);
while (remaining > 0u) {
uint32 wrote = remaining;
if (!activeClient->Write(wPtr, wrote) || wrote == 0u) {
@@ -331,6 +333,8 @@ ErrorManagement::ErrorType DebugService::Server(ExecutionInfo &info) {
}
wPtr += wrote;
remaining -= wrote;
lastDataTimeMs = (uint64)((float64)HighResolutionTimer::Counter() *
HighResolutionTimer::Period() * 1000.0);
}
}
}
@@ -94,7 +94,10 @@ private:
// Rate-limiting and idle-timeout constants / state
static const uint32 CMD_RATE_LIMIT = 100u;
static const uint32 CLIENT_IDLE_TIMEOUT_MS = 30000u;
// Idle timeout: time with no incoming bytes before the connection is closed.
// Large applications (1000+ signals) can take >30 s to process a DISCOVER/TREE
// response, during which no commands are sent — use a generous default.
static const uint32 CLIENT_IDLE_TIMEOUT_MS = 120000u;
static const uint32 INPUT_BUFFER_MAX = 8192u;
uint32 cmdCountInWindow;
@@ -180,6 +180,8 @@ DebugServiceBase::DebugServiceBase() : ReferenceContainer() {
isPaused = false;
stepRemaining = 0u;
manualConfigSet = false;
discoverCacheValid = false;
treeCacheValid = false;
}
DebugServiceBase::~DebugServiceBase() {
@@ -256,6 +258,9 @@ DebugSignalInfo *DebugServiceBase::RegisterSignal(void *memoryAddress,
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;
@@ -716,54 +721,102 @@ void DebugServiceBase::GetSignalValue(const char8 *name, StreamString &out) {
}
void DebugServiceBase::Discover(StreamString &out) {
out += "{\"Signals\":[\n";
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();
uint32 total = 0u;
for (uint32 i = 0u; i < aliases.GetNumberOfElements(); 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++;
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 (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;
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++;
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();
out += "\n]}\nOK DISCOVER\n";
// -----------------------------------------------------------------------
// 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) {
@@ -868,6 +921,8 @@ void DebugServiceBase::SetFullConfig(ConfigurationDatabase &config) {
config.MoveToRoot();
config.Copy(fullConfig);
manualConfigSet = true;
BuildDiscoverCache();
BuildTreeCache();
}
void DebugServiceBase::RebuildConfigFromRegistry() {
@@ -1234,11 +1289,9 @@ void DebugServiceBase::HandleCommand(const StreamString &cmdIn,
} 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";
if (!treeCacheValid)
BuildTreeCache();
out += treeCache;
} else if (token == "INFO") {
StreamString path;
if (cmd.GetToken(path, delims, term))
@@ -1415,5 +1468,6 @@ void DebugServiceBase::HandleCommand(const StreamString &cmdIn,
// Out-of-class constant definitions (C++98 ODR requirement)
// ---------------------------------------------------------------------------
const uint32 DebugServiceBase::GET_VALUE_MAX_ELEMENTS;
const uint32 DebugServiceBase::DISCOVER_CHUNK_SIGNALS;
} // namespace MARTe
@@ -125,6 +125,22 @@ public:
static const uint32 GET_VALUE_MAX_ELEMENTS = 256u;
/**
* @brief Pre-build DISCOVER and TREE response caches.
*
* Called automatically by SetFullConfig() once the application is fully
* initialised. After this point, DISCOVER and TREE are served from the
* pre-built string with no mutex contention and no JSON generation cost.
* Both caches are invalidated whenever a new signal is registered (which
* normally only happens during broker init, before SetFullConfig).
*/
void BuildDiscoverCache();
void BuildTreeCache();
// Number of signals per DISCOVER_PART TCP chunk. Responses larger than
// this are split so the Go client can start parsing immediately.
static const uint32 DISCOVER_CHUNK_SIGNALS = 256u;
protected:
// =========================================================================
// Virtual hooks for transport subclasses
@@ -183,6 +199,13 @@ protected:
ConfigurationDatabase fullConfig;
bool manualConfigSet;
// Pre-built response caches. Guarded by mutex (brief lock for swap,
// none needed for reads once cacheValid is true and construction is done).
StreamString discoverCache; // full chunked DISCOVER_PART+DISCOVER payload
StreamString treeCache; // full TREE payload including sentinel
volatile bool discoverCacheValid;
volatile bool treeCacheValid;
};
} // namespace MARTe