Fixed issues with config and tracing

This commit is contained in:
Martino Ferrari
2026-04-08 23:44:01 +02:00
parent 7adbecdb6e
commit b86ede99b9
5 changed files with 174 additions and 49 deletions
@@ -26,6 +26,30 @@
namespace MARTe { namespace MARTe {
// Recursive search for any object with a given name anywhere in the registry.
// Used to find GAMs regardless of nesting level.
static Reference FindByNameRecursive(ReferenceContainer *container,
const char8 *name) {
if (container == NULL_PTR(ReferenceContainer *))
return Reference();
uint32 n = container->Size();
for (uint32 i = 0; i < n; i++) {
Reference child = container->Get(i);
if (!child.IsValid())
continue;
if (StringHelper::Compare(child->GetName(), name) == 0)
return child;
ReferenceContainer *sub =
dynamic_cast<ReferenceContainer *>(child.operator->());
if (sub != NULL_PTR(ReferenceContainer *)) {
Reference found = FindByNameRecursive(sub, name);
if (found.IsValid())
return found;
}
}
return Reference();
}
/** /**
* @brief Helper for optimized signal processing within brokers. * @brief Helper for optimized signal processing within brokers.
*/ */
@@ -126,21 +150,14 @@ public:
(direction == InputSignals) ? "InputSignals" : "OutputSignals"; (direction == InputSignals) ? "InputSignals" : "OutputSignals";
const char8 *dirStrShort = (direction == InputSignals) ? "In" : "Out"; const char8 *dirStrShort = (direction == InputSignals) ? "In" : "Out";
// Try to find the GAM with different path variations // Search recursively through the entire registry for the GAM by name.
// Direct Find("GAM1") only checks top-level; the GAM may be nested
// several levels deep inside a RealTimeApplication container.
Reference gamRef = Reference gamRef =
ObjectRegistryDatabase::Instance()->Find(functionName); FindByNameRecursive(ObjectRegistryDatabase::Instance(),
if (!gamRef.IsValid()) { functionName);
// Try with "App.Functions." prefix fprintf(stderr, ">> GAM lookup '%s': %s\n", functionName,
StreamString tryPath; gamRef.IsValid() ? "FOUND" : "NOT FOUND");
tryPath.Printf("App.Functions.%s", functionName);
gamRef = ObjectRegistryDatabase::Instance()->Find(tryPath.Buffer());
}
if (!gamRef.IsValid()) {
// Try with "Functions." prefix
StreamString tryPath;
tryPath.Printf("Functions.%s", functionName);
gamRef = ObjectRegistryDatabase::Instance()->Find(tryPath.Buffer());
}
if (gamRef.IsValid()) { if (gamRef.IsValid()) {
StreamString absGamPath; StreamString absGamPath;
@@ -93,6 +93,7 @@ DebugService::DebugService()
isServer = false; isServer = false;
suppressTimeoutLogs = true; suppressTimeoutLogs = true;
isPaused = false; isPaused = false;
manualConfigSet = false;
activeClient = NULL_PTR(BasicTCPSocket *); activeClient = NULL_PTR(BasicTCPSocket *);
} }
@@ -157,18 +158,10 @@ bool DebugService::Initialise(StructuredDataI &data) {
suppressTimeoutLogs = (suppress == 1); suppressTimeoutLogs = (suppress == 1);
} }
// Try to capture full configuration autonomously if data is a // Do NOT call MoveToRoot() on the shared CDB here — that corrupts the
// ConfigurationDatabase // ReferenceContainer::Initialise() traversal cursor for sibling objects.
ConfigurationDatabase *cdb = dynamic_cast<ConfigurationDatabase *>(&data); // Just capture the local subtree; full config is rebuilt lazily from the
if (cdb != NULL_PTR(ConfigurationDatabase *)) { // live ObjectRegistryDatabase when ServeConfig/EnrichWithConfig is called.
// Save current position
StreamString currentPath;
// In MARTe2 ConfigurationDatabase there isn't a direct GetCurrentPath,
// but we can at least try to copy from root if we are at root.
// For now, we rely on explicit SetFullConfig or documentary injection.
}
// Copy local branch as fallback
(void)data.Copy(fullConfig); (void)data.Copy(fullConfig);
if (isServer) { if (isServer) {
@@ -196,6 +189,50 @@ bool DebugService::Initialise(StructuredDataI &data) {
void DebugService::SetFullConfig(ConfigurationDatabase &config) { void DebugService::SetFullConfig(ConfigurationDatabase &config) {
config.MoveToRoot(); config.MoveToRoot();
config.Copy(fullConfig); config.Copy(fullConfig);
manualConfigSet = true;
}
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 *className = child->GetClassProperties()->GetName();
if (className != NULL_PTR(const char8 *))
(void)cdb.Write("Class", className);
// Export the object's own properties (standard fields, parameters).
// Custom config-file-only fields (PVName etc.) are not available here.
(void)child->ExportData(cdb);
ReferenceContainer *sub =
dynamic_cast<ReferenceContainer *>(child.operator->());
if (sub != NULL_PTR(ReferenceContainer *))
BuildCDBFromContainer(sub, cdb);
(void)cdb.MoveToAncestor(1u);
}
}
void DebugService::RebuildConfigFromRegistry() {
ConfigurationDatabase newConfig;
BuildCDBFromContainer(ObjectRegistryDatabase::Instance(), newConfig);
newConfig.MoveToRoot();
newConfig.Copy(fullConfig);
} }
static void PatchItemInternal(const char8 *originalName, static void PatchItemInternal(const char8 *originalName,
@@ -247,7 +284,7 @@ DebugSignalInfo *DebugService::RegisterSignal(void *memoryAddress,
const char8 *name, const char8 *name,
uint8 numberOfDimensions, uint8 numberOfDimensions,
uint32 numberOfElements) { uint32 numberOfElements) {
printf("<debug> registering: %s\n", name); fprintf(stderr, "<debug> RegisterSignal[%p]: %s\n", (void*)this, name);
mutex.FastLock(); mutex.FastLock();
DebugSignalInfo *res = NULL_PTR(DebugSignalInfo *); DebugSignalInfo *res = NULL_PTR(DebugSignalInfo *);
uint32 sigIdx = 0xFFFFFFFF; uint32 sigIdx = 0xFFFFFFFF;
@@ -785,8 +822,13 @@ void DebugService::HandleCommand(StreamString cmd, BasicTCPSocket *client) {
void DebugService::EnrichWithConfig(const char8 *path, StreamString &json) { void DebugService::EnrichWithConfig(const char8 *path, StreamString &json) {
if (path == NULL_PTR(const char8 *)) if (path == NULL_PTR(const char8 *))
return; return;
if (!manualConfigSet) {
RebuildConfigFromRegistry();
}
fullConfig.MoveToRoot(); fullConfig.MoveToRoot();
fprintf(stderr, "[EnrichWithConfig] path=%s\n", path);
const char8 *current = path; const char8 *current = path;
bool ok = true; bool ok = true;
while (ok) { while (ok) {
@@ -801,29 +843,49 @@ void DebugService::EnrichWithConfig(const char8 *path, StreamString &json) {
ok = false; ok = false;
} }
if (fullConfig.MoveRelative(part.Buffer())) { // Normalise short direction names to both forms so we search consistently.
// Found exact // Paths use "In"/"Out" (alias convention); CDBs may store either form.
} else {
bool found = false; bool found = false;
// 1. Try exact match (bare name - rebuilt-from-registry CDBs use this)
if (fullConfig.MoveRelative(part.Buffer())) {
fprintf(stderr, "[EnrichWithConfig] nav exact '%s' OK\n", part.Buffer());
found = true;
}
// 2. Try +name (raw config-file CDBs prefix nodes with '+')
if (!found) {
StreamString prefixed;
prefixed.Printf("+%s", part.Buffer());
if (fullConfig.MoveRelative(prefixed.Buffer())) {
fprintf(stderr, "[EnrichWithConfig] nav prefixed '%s' OK\n", prefixed.Buffer());
found = true;
}
}
// 3. Expand short direction aliases: In -> InputSignals / Out -> OutputSignals
if (!found) {
if (part == "In") { if (part == "In") {
if (fullConfig.MoveRelative("InputSignals")) { if (fullConfig.MoveRelative("InputSignals")) {
fprintf(stderr, "[EnrichWithConfig] nav 'In'->InputSignals OK\n");
found = true;
} else if (fullConfig.MoveRelative("+InputSignals")) {
fprintf(stderr, "[EnrichWithConfig] nav 'In'->+InputSignals OK\n");
found = true; found = true;
} }
} else if (part == "Out") { } else if (part == "Out") {
if (fullConfig.MoveRelative("OutputSignals")) { if (fullConfig.MoveRelative("OutputSignals")) {
found = true; found = true;
} else if (fullConfig.MoveRelative("+OutputSignals")) {
found = true;
}
} }
} }
if (!found) { if (!found) {
StreamString prefixed; fprintf(stderr, "[EnrichWithConfig] FAILED at part '%s'\n", part.Buffer());
prefixed.Printf("+%s", part.Buffer()); fullConfig.MoveToRoot();
if (fullConfig.MoveRelative(prefixed.Buffer())) { return;
// Found prefixed
} else {
return; // Not found
}
}
} }
} }
@@ -886,6 +948,14 @@ void DebugService::JsonifyDatabase(ConfigurationDatabase &db,
void DebugService::ServeConfig(BasicTCPSocket *client) { void DebugService::ServeConfig(BasicTCPSocket *client) {
if (client == NULL_PTR(BasicTCPSocket *)) if (client == NULL_PTR(BasicTCPSocket *))
return; return;
// If no manual config was injected (test case), rebuild from the live registry.
// In production, Initialise() only captures the DebugService subtree (to avoid
// corrupting the shared CDB cursor), so we always need to rebuild here.
if (!manualConfigSet) {
RebuildConfigFromRegistry();
}
StreamString json; StreamString json;
fullConfig.MoveToRoot(); fullConfig.MoveToRoot();
JsonifyDatabase(fullConfig, json); JsonifyDatabase(fullConfig, json);
@@ -930,6 +1000,7 @@ void DebugService::InfoNode(const char8 *path, BasicTCPSocket *client) {
} }
EnrichWithConfig(path, json); EnrichWithConfig(path, json);
} else { } else {
StreamString enrichAlias;
mutex.FastLock(); mutex.FastLock();
bool found = false; bool found = false;
for (uint32 i = 0; i < aliases.Size(); i++) { for (uint32 i = 0; i < aliases.Size(); i++) {
@@ -941,13 +1012,21 @@ void DebugService::InfoNode(const char8 *path, BasicTCPSocket *client) {
json.Printf("\"Name\": \"%s\", \"Class\": \"Signal\", \"Type\": " json.Printf("\"Name\": \"%s\", \"Class\": \"Signal\", \"Type\": "
"\"%s\", \"ID\": %d", "\"%s\", \"ID\": %d",
s->name.Buffer(), tname ? tname : "Unknown", s->internalID); s->name.Buffer(), tname ? tname : "Unknown", s->internalID);
EnrichWithConfig(aliases[i].name.Buffer(), json); enrichAlias = aliases[i].name;
found = true; found = true;
break; break;
} }
} }
if (!found) {
fprintf(stderr, "[InfoNode][%p] signal '%s' NOT found in %u aliases:\n", (void*)this, path, (uint32)aliases.Size());
for (uint32 i = 0; i < aliases.Size(); i++) {
fprintf(stderr, " alias[%u] = '%s'\n", i, aliases[i].name.Buffer());
}
}
mutex.FastUnLock(); mutex.FastUnLock();
if (!found) if (found)
EnrichWithConfig(enrichAlias.Buffer(), json);
else
json += "\"Error\": \"Object not found\""; json += "\"Error\": \"Object not found\"";
} }
json += "}\nOK INFO\n"; json += "}\nOK INFO\n";
@@ -74,6 +74,7 @@ public:
void ListNodes(const char8 *path, BasicTCPSocket *client); void ListNodes(const char8 *path, BasicTCPSocket *client);
void ServeConfig(BasicTCPSocket *client); void ServeConfig(BasicTCPSocket *client);
void SetFullConfig(ConfigurationDatabase &config); void SetFullConfig(ConfigurationDatabase &config);
void RebuildConfigFromRegistry();
struct MonitoredSignal { struct MonitoredSignal {
ReferenceT<DataSourceI> dataSource; ReferenceT<DataSourceI> dataSource;
@@ -150,6 +151,7 @@ private:
BasicTCPSocket *activeClient; BasicTCPSocket *activeClient;
ConfigurationDatabase fullConfig; ConfigurationDatabase fullConfig;
bool manualConfigSet;
static DebugService *instance; static DebugService *instance;
}; };
+1 -1
View File
@@ -80,7 +80,7 @@ int main() {
Sleep::MSec(1000); Sleep::MSec(1000);
printf("\n--- Test 5: Config & Metadata Enrichment ---\n"); printf("\n--- Test 5: Config & Metadata Enrichment ---\n");
// TestConfigCommands(); // Skipping for now TestConfigCommands();
Sleep::MSec(1000); Sleep::MSec(1000);
printf("\n--- Test 6: TREE Command Enhancement ---\n"); printf("\n--- Test 6: TREE Command Enhancement ---\n");
+34 -7
View File
@@ -11,13 +11,16 @@ fi
# 2. Paths # 2. Paths
MARTE_EX="${MARTe2_DIR}/Build/${TARGET}/App/MARTeApp.ex" MARTE_EX="${MARTe2_DIR}/Build/${TARGET}/App/MARTeApp.ex"
DEBUG_LIB="$(pwd)/Build/libmarte_dev.so" BUILD_DIR="$(pwd)/Build/${TARGET}"
# Our plugin libraries
DEBUG_LIB="${BUILD_DIR}/Components/Interfaces/DebugService/libDebugService.so"
TCPLOGGER_LIB="${BUILD_DIR}/Components/Interfaces/TCPLogger/libTcpLogger.so"
# Component library base search path # Component library base search path
COMPONENTS_BUILD_DIR="${MARTe2_Components_DIR}/Build/${TARGET}/Components" COMPONENTS_BUILD_DIR="${MARTe2_Components_DIR}/Build/${TARGET}/Components"
# DYNAMICALLY FIND ALL COMPONENT DIRS # DYNAMICALLY FIND ALL COMPONENT DIRS and add to LD_LIBRARY_PATH
# MARTe2 Loader needs the specific directories containing .so files in LD_LIBRARY_PATH
ALL_COMPONENT_DIRS=$(find "$COMPONENTS_BUILD_DIR" -type d) ALL_COMPONENT_DIRS=$(find "$COMPONENTS_BUILD_DIR" -type d)
for dir in $ALL_COMPONENT_DIRS; do for dir in $ALL_COMPONENT_DIRS; do
if ls "$dir"/*.so >/dev/null 2>&1; then if ls "$dir"/*.so >/dev/null 2>&1; then
@@ -26,15 +29,39 @@ for dir in $ALL_COMPONENT_DIRS; do
done done
# Ensure our build dir and core dir are included # Ensure our build dir and core dir are included
export LD_LIBRARY_PATH="$(pwd)/Build:${MARTe2_DIR}/Build/${TARGET}/Core:${LD_LIBRARY_PATH}" export LD_LIBRARY_PATH="${BUILD_DIR}/Components/Interfaces/DebugService:${BUILD_DIR}/Components/Interfaces/TCPLogger:${MARTe2_DIR}/Build/${TARGET}/Core:${LD_LIBRARY_PATH}"
# 3. Cleanup # 3. Cleanup
echo "Cleaning up lingering processes..." echo "Cleaning up lingering processes..."
pkill -9 MARTeApp.ex pkill -9 MARTeApp.ex
sleep 1 sleep 1
# 4. Launch Application # 4. Validate libraries exist
for lib in "$DEBUG_LIB" "$TCPLOGGER_LIB"; do
if [ ! -f "$lib" ]; then
echo "ERROR: Library not found: $lib"
echo "Run: make -f Makefile.gcc core"
exit 1
fi
done
# 5. Launch Application
echo "Launching standard MARTeApp.ex with debug_test.cfg..." echo "Launching standard MARTeApp.ex with debug_test.cfg..."
# PRELOAD ensures our DebugService class is available to the registry early # LD_PRELOAD ensures our classes are registered before the config is parsed.
export LD_PRELOAD="${DEBUG_LIB}" # MARTeApp.ex does not use dlopen for plugins — preloading is the standard approach.
# All required component .so files are collected via the find loop into LD_PRELOAD too.
PRELOAD_LIBS="${DEBUG_LIB}:${TCPLOGGER_LIB}"
# Collect any additional component .so files referenced by the config
for lib in \
"${COMPONENTS_BUILD_DIR}/DataSources/RealTimeThreadSynchronisation/RealTimeThreadSynchronisation.so" \
"${COMPONENTS_BUILD_DIR}/DataSources/LinuxTimer/LinuxTimer.so" \
"${COMPONENTS_BUILD_DIR}/DataSources/LoggerDataSource/LoggerDataSource.so" \
"${COMPONENTS_BUILD_DIR}/GAMs/IOGAM/IOGAM.so"; do
if [ -f "$lib" ]; then
PRELOAD_LIBS="${PRELOAD_LIBS}:${lib}"
fi
done
export LD_PRELOAD="${PRELOAD_LIBS}"
"$MARTE_EX" -f Test/Configurations/debug_test.cfg -l RealTimeLoader -s State1 "$MARTE_EX" -f Test/Configurations/debug_test.cfg -l RealTimeLoader -s State1