major improvements for big apps
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user