From 62c36f63913dee98f2cd19e8ae95386ed7bea5b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 09:37:49 +0000 Subject: [PATCH 1/3] Check loaded modules before system modules in Dmod_Mgr_IsLoaded/IsEnabled Dmod_Mgr_IsSystemModule() runs Dmod_ApiSignature_IsModule() over every built-in input API - 351 entries in dmod-boot, ~137k instructions per call. Dmod_Mgr_IsLoaded() and Dmod_Mgr_IsEnabled() called it first, so every Dmod_IsModuleLoaded(), Dmod_IsModuleEnabled() and Dmod_LoadModuleByName() paid that scan even for an ordinary module that is already loaded. dmdevfs calls these several times per configured driver: in dmod-boot it was 624 calls, ~185 ms of the driver configuration in Renode. Both functions now look the module up in the context table first and only fall back to the system module check when it is not found there. The result is the same - only the order of the two checks changed. dmod-boot in Renode: one GPIO pin is configured in 2.6 ms instead of 4.0 ms, boot to the shell prompt 2052 ms -> 1892 ms. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V9XGvT9zLJVAagoEVhYh9T --- src/system/private/dmod_mgr.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/system/private/dmod_mgr.c b/src/system/private/dmod_mgr.c index 173b4170..925cc6dc 100644 --- a/src/system/private/dmod_mgr.c +++ b/src/system/private/dmod_mgr.c @@ -53,12 +53,14 @@ void Dmod_Mgr_PrintSystemModules( void ) */ bool Dmod_Mgr_IsLoaded( const char* ModuleName ) { - if(Dmod_Mgr_IsSystemModule( ModuleName )) + /* Loaded modules first: a lookup in the context table is cheap, while + * Dmod_Mgr_IsSystemModule() checks the signature of every built-in API. */ + if(Dmod_Context_Get( ModuleName ) != NULL) { - return true; + return true; } - return Dmod_Context_Get( ModuleName ) != NULL; + return Dmod_Mgr_IsSystemModule( ModuleName ); } /** @@ -70,10 +72,12 @@ bool Dmod_Mgr_IsLoaded( const char* ModuleName ) */ bool Dmod_Mgr_IsEnabled( const char* ModuleName ) { - if(Dmod_Mgr_IsSystemModule( ModuleName )) + /* See Dmod_Mgr_IsLoaded() for why loaded modules are checked first */ + Dmod_Context_t* context = Dmod_Context_Get( ModuleName ); + if(context != NULL && Dmod_IsEnabled( context )) { - return true; + return true; } - Dmod_Context_t* context = Dmod_Context_Get( ModuleName ); - return context != NULL && Dmod_IsEnabled( context ); + + return Dmod_Mgr_IsSystemModule( ModuleName ); } From 39b92abc7d1f8f089d1830001c70c5a5001fa7e5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 09:55:27 +0000 Subject: [PATCH 2/3] Look system modules up in a list of their names built once Dmod_Mgr_IsSystemModule() ran Dmod_ApiSignature_IsModule() over every built-in input API on each call - ~105k instructions per call even after loaded modules are checked first, still ~330 calls and ~75 ms of the boot of dmod-boot in Renode (required modules of every loaded module, modules that are not loaded yet). Dmod_Initialize() now builds the list of the module names of the built-in APIs once (Dmod_Mgr_InitSystemModules), packed into a single allocation - 7 names, 44 bytes in dmod-boot - and Dmod_Mgr_IsSystemModule() only compares the name with those. The list is built on first use if Dmod_Initialize() was not called, freed by Dmod_Deinitialize(), and if it cannot be allocated the built-in APIs are checked one by one as before. The comparison is the same as in Dmod_ApiSignature_IsModule(): the module name of an API starts with the given name. dmod-boot in Renode: Dmod_Mgr_IsSystemModule() takes ~560 instructions instead of ~105k (354 calls: 0.4 ms instead of ~75 ms), building the list takes 0.8 ms once; boot to the shell prompt 1892 ms -> 1788 ms. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V9XGvT9zLJVAagoEVhYh9T --- inc/private/dmod_mgr.h | 2 + src/system/dmod_system.c | 5 + src/system/private/dmod_mgr.c | 166 ++++++++++++++++++++++-- tests/system/private/tests_dmod_mgr.cpp | 57 ++++++++ 4 files changed, 222 insertions(+), 8 deletions(-) diff --git a/inc/private/dmod_mgr.h b/inc/private/dmod_mgr.h index e52b34fe..2c1590d4 100644 --- a/inc/private/dmod_mgr.h +++ b/inc/private/dmod_mgr.h @@ -9,6 +9,8 @@ extern "C" { # error "This is private DMOD header. Don't include this outside DMOD library" #endif +extern bool Dmod_Mgr_InitSystemModules( void ); +extern void Dmod_Mgr_DeinitSystemModules( void ); extern bool Dmod_Mgr_IsSystemModule( const char* ModuleName ); extern void Dmod_Mgr_PrintSystemModules( void ); extern bool Dmod_Mgr_IsLoaded( const char* ModuleName ); diff --git a/src/system/dmod_system.c b/src/system/dmod_system.c index 6820e59d..1478d366 100644 --- a/src/system/dmod_system.c +++ b/src/system/dmod_system.c @@ -147,6 +147,10 @@ bool Dmod_Initialize( size_t NumIrqs, size_t MaxHandlersPerIrq ) { return false; } + if( !Dmod_Mgr_InitSystemModules() ) + { + DMOD_LOG_WARN("Cannot build the list of system modules - built-in APIs will be checked one by one\n"); + } return Dmod_BuiltinInputApi.InputSection != NULL && Dmod_BuiltinOutputApi.OutputSection != NULL; } @@ -157,6 +161,7 @@ bool Dmod_Initialize( size_t NumIrqs, size_t MaxHandlersPerIrq ) */ bool Dmod_Deinitialize(void) { + Dmod_Mgr_DeinitSystemModules(); Dmod_Irq_Deinit(); return true; } diff --git a/src/system/private/dmod_mgr.c b/src/system/private/dmod_mgr.c index 925cc6dc..ec342d69 100644 --- a/src/system/private/dmod_mgr.c +++ b/src/system/private/dmod_mgr.c @@ -1,23 +1,152 @@ #define DMOD_PRIVATE +#include #include "dmod.h" #include "private/dmod_vars.h" #include "private/dmod_ctx.h" #include "private/dmod_mgr.h" /** - * @brief checks if the given module is a system module - * - * @param ModuleName Name of the module to check - * - * @return True if module is a system module, false otherwise + * @brief Names of the system modules - the modules the built-in input APIs belong to + * + * Packed one after another, each terminated with '\0', the list itself with an + * empty name. Built once (see Dmod_Mgr_InitSystemModules), so that + * Dmod_Mgr_IsSystemModule() does not have to check the signature of every + * built-in API on each call. */ -bool Dmod_Mgr_IsSystemModule( const char* ModuleName ) +static char* SystemModuleNames = NULL; + +/** + * @brief Read the module name of a built-in API signature that names a module + * + * @param Signature API signature + * @param outModuleName Buffer for the module name (DMOD_MAX_MODULE_NAME_LENGTH bytes) + * + * @return True if the signature is one that Dmod_ApiSignature_IsModule() accepts + */ +static bool ReadSystemModuleName( const char* Signature, char* outModuleName ) { - if(ModuleName == NULL || ModuleName[0] == 0) + if( + strncmp( Signature, DMOD_SIGNATURE_PREFIX, sizeof( DMOD_SIGNATURE_PREFIX ) - 1 ) != 0 + && strncmp( Signature, DMOD_BUILTIN_SIGNATURE_PREFIX, sizeof( DMOD_BUILTIN_SIGNATURE_PREFIX ) - 1 ) != 0 + && strncmp( Signature, DMOD_MAL_SIGNATURE_PREFIX, sizeof( DMOD_MAL_SIGNATURE_PREFIX ) - 1 ) != 0 + && strncmp( Signature, DMOD_DIF_SIGNATURE_PREFIX, sizeof( DMOD_DIF_SIGNATURE_PREFIX ) - 1 ) != 0 + ) { - DMOD_LOG_ERROR("Cannot check if module is system module - invalid module name (empty or NULL)\n"); return false; } + + memset( outModuleName, 0, DMOD_MAX_MODULE_NAME_LENGTH ); + return Dmod_ApiSignature_ReadModuleName( Signature, outModuleName, DMOD_MAX_MODULE_NAME_LENGTH - 1 ); +} + +/** + * @brief Find a system module name in the packed list + * + * @param Names Packed list of names (see SystemModuleNames) + * @param ModuleName Name to look for + * @param Length Number of characters of ModuleName to compare + * + * @return True if a name in the list starts with the first Length characters of ModuleName + */ +static bool FindSystemModuleName( const char* Names, const char* ModuleName, size_t Length ) +{ + for( const char* name = Names; *name != '\0'; name += strlen( name ) + 1 ) + { + if( strncmp( name, ModuleName, Length ) == 0 ) + { + return true; + } + } + return false; +} + +/** + * @brief Add a module name to the packed list of names, unless it is already there + * + * @param Names Packed list of names (NULL for an empty one) + * @param Size Size of the list in bytes, updated on return + * @param ModuleName Name to add + * + * @return The list (possibly reallocated), or NULL on allocation failure + */ +static char* AddSystemModuleName( char* Names, size_t* Size, const char* ModuleName ) +{ + size_t length = strlen( ModuleName ); + if( Names != NULL && FindSystemModuleName( Names, ModuleName, length + 1 ) ) + { + return Names; + } + + size_t used = ( Names == NULL ) ? 0 : *Size - 1; + char* names = Dmod_Realloc( Names, used + length + 2 ); + if( names == NULL ) + { + Dmod_Free( Names ); + return NULL; + } + + memcpy( &names[used], ModuleName, length + 1 ); + names[used + length + 1] = '\0'; + *Size = used + length + 2; + return names; +} + +/** + * @brief Build the list of the system module names, if it is not built yet + * + * @return True if the list is available + */ +bool Dmod_Mgr_InitSystemModules( void ) +{ + if( SystemModuleNames != NULL ) + { + return true; + } + + Dmod_BuiltinInputApi.SectionSize = (size_t)((void*)&__dmod_inputs_end - (void*)&__dmod_inputs_start); + size_t numberOfEntries = Dmod_Api_GetNumberOfEntries( &Dmod_BuiltinInputApi ); + char* names = NULL; + size_t size = 0; + char moduleName[DMOD_MAX_MODULE_NAME_LENGTH]; + for(size_t i = 0; i < numberOfEntries; i++) + { + const char* signature = Dmod_BuiltinInputApi.InputSection->Entries[i].Signature; + if( !Dmod_ApiSignature_IsValid( signature ) || !ReadSystemModuleName( signature, moduleName ) || moduleName[0] == '\0' ) + { + continue; + } + names = AddSystemModuleName( names, &size, moduleName ); + if( names == NULL ) + { + DMOD_LOG_ERROR("Cannot allocate memory for the list of system modules\n"); + return false; + } + } + + SystemModuleNames = names; + return SystemModuleNames != NULL; +} + +/** + * @brief Free the list of the system module names + */ +void Dmod_Mgr_DeinitSystemModules( void ) +{ + Dmod_Free( SystemModuleNames ); + SystemModuleNames = NULL; +} + +/** + * @brief Check if the given module is a system module by checking every built-in API + * + * Fallback for when the list of the system module names cannot be built. + * + * @param ModuleName Name of the module to check + * + * @return True if module is a system module, false otherwise + */ +static bool IsSystemModuleInApis( const char* ModuleName ) +{ Dmod_BuiltinInputApi.SectionSize = (size_t)((void*)&__dmod_inputs_end - (void*)&__dmod_inputs_start); size_t numberOfEntries = Dmod_Api_GetNumberOfEntries( &Dmod_BuiltinInputApi ); for(size_t i = 0; i < numberOfEntries; i++) @@ -30,6 +159,27 @@ bool Dmod_Mgr_IsSystemModule( const char* ModuleName ) return false; } +/** + * @brief checks if the given module is a system module + * + * @param ModuleName Name of the module to check + * + * @return True if module is a system module, false otherwise + */ +bool Dmod_Mgr_IsSystemModule( const char* ModuleName ) +{ + if(ModuleName == NULL || ModuleName[0] == 0) + { + DMOD_LOG_ERROR("Cannot check if module is system module - invalid module name (empty or NULL)\n"); + return false; + } + if( !Dmod_Mgr_InitSystemModules() ) + { + return IsSystemModuleInApis( ModuleName ); + } + return FindSystemModuleName( SystemModuleNames, ModuleName, strlen( ModuleName ) ); +} + /** * @brief Prints all system modules to the log */ diff --git a/tests/system/private/tests_dmod_mgr.cpp b/tests/system/private/tests_dmod_mgr.cpp index b70e8506..3c754244 100644 --- a/tests/system/private/tests_dmod_mgr.cpp +++ b/tests/system/private/tests_dmod_mgr.cpp @@ -64,6 +64,63 @@ TEST_F(DmodMgrTest, IsNullModule) ASSERT_FALSE(Dmod_Mgr_IsSystemModule(NULL)); } +/** + * @brief Test for Dmod_Mgr_InitSystemModules + * + * The test checks if the list of the system modules can be built more than + * once, and if the result of Dmod_Mgr_IsSystemModule does not change with it. + */ +TEST_F(DmodMgrTest, InitSystemModules) +{ + ASSERT_TRUE(Dmod_Mgr_InitSystemModules()); + ASSERT_TRUE(Dmod_Mgr_InitSystemModules()); + ASSERT_TRUE(Dmod_Mgr_IsSystemModule("Dmod")); + ASSERT_FALSE(Dmod_Mgr_IsSystemModule("Test")); +} + +/** + * @brief Test for Dmod_Mgr_DeinitSystemModules + * + * The test checks if Dmod_Mgr_IsSystemModule builds the list of the system + * modules again when it has been freed. + */ +TEST_F(DmodMgrTest, IsSystemModuleAfterDeinit) +{ + Dmod_Mgr_DeinitSystemModules(); + ASSERT_TRUE(Dmod_Mgr_IsSystemModule("Dmod")); + ASSERT_FALSE(Dmod_Mgr_IsSystemModule("Test")); +} + +/** + * @brief Test for Dmod_Mgr_IsSystemModule + * + * The test checks if every module named by a built-in API signature is + * reported as a system module. + */ +TEST_F(DmodMgrTest, IsSystemModuleForEveryBuiltinApi) +{ + Dmod_BuiltinInputApi.SectionSize = (size_t)((uint8_t*)&__dmod_inputs_end - (uint8_t*)&__dmod_inputs_start); + size_t numberOfEntries = Dmod_Api_GetNumberOfEntries(&Dmod_BuiltinInputApi); + ASSERT_GT(numberOfEntries, 0u); + size_t checked = 0; + for(size_t i = 0; i < numberOfEntries; i++) + { + const char* signature = Dmod_BuiltinInputApi.InputSection->Entries[i].Signature; + char moduleName[DMOD_MAX_MODULE_NAME_LENGTH] = {0}; + if(!Dmod_ApiSignature_IsValid(signature) + || Dmod_ApiSignature_IsTest(signature) + || strncmp(signature, DMOD_IRQ_SIGNATURE_PREFIX, sizeof(DMOD_IRQ_SIGNATURE_PREFIX) - 1) == 0 + || !Dmod_ApiSignature_ReadModuleName(signature, moduleName, sizeof(moduleName) - 1) + || moduleName[0] == '\0') + { + continue; + } + EXPECT_TRUE(Dmod_Mgr_IsSystemModule(moduleName)) << moduleName; + checked++; + } + ASSERT_GT(checked, 0u); +} + // =============================================================== // Tests for Dmod_Mgr_IsLoaded // =============================================================== From 12c6d3e38ec4892706b7a7fcf5aa3902d36faf41 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 10:01:44 +0000 Subject: [PATCH 3/3] Rename the system module list functions after what they do Dmod_Mgr_InitSystemModules/DeinitSystemModules suggested that the modules themselves are initialized; they only build and free the list of the system module names, so they are now Dmod_Mgr_BuildSystemModuleList/FreeSystemModuleList. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01V9XGvT9zLJVAagoEVhYh9T --- inc/private/dmod_mgr.h | 4 ++-- src/system/dmod_system.c | 4 ++-- src/system/private/dmod_mgr.c | 8 ++++---- tests/system/private/tests_dmod_mgr.cpp | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/inc/private/dmod_mgr.h b/inc/private/dmod_mgr.h index 2c1590d4..2f656f5d 100644 --- a/inc/private/dmod_mgr.h +++ b/inc/private/dmod_mgr.h @@ -9,8 +9,8 @@ extern "C" { # error "This is private DMOD header. Don't include this outside DMOD library" #endif -extern bool Dmod_Mgr_InitSystemModules( void ); -extern void Dmod_Mgr_DeinitSystemModules( void ); +extern bool Dmod_Mgr_BuildSystemModuleList( void ); +extern void Dmod_Mgr_FreeSystemModuleList( void ); extern bool Dmod_Mgr_IsSystemModule( const char* ModuleName ); extern void Dmod_Mgr_PrintSystemModules( void ); extern bool Dmod_Mgr_IsLoaded( const char* ModuleName ); diff --git a/src/system/dmod_system.c b/src/system/dmod_system.c index 1478d366..4a83a52d 100644 --- a/src/system/dmod_system.c +++ b/src/system/dmod_system.c @@ -147,7 +147,7 @@ bool Dmod_Initialize( size_t NumIrqs, size_t MaxHandlersPerIrq ) { return false; } - if( !Dmod_Mgr_InitSystemModules() ) + if( !Dmod_Mgr_BuildSystemModuleList() ) { DMOD_LOG_WARN("Cannot build the list of system modules - built-in APIs will be checked one by one\n"); } @@ -161,7 +161,7 @@ bool Dmod_Initialize( size_t NumIrqs, size_t MaxHandlersPerIrq ) */ bool Dmod_Deinitialize(void) { - Dmod_Mgr_DeinitSystemModules(); + Dmod_Mgr_FreeSystemModuleList(); Dmod_Irq_Deinit(); return true; } diff --git a/src/system/private/dmod_mgr.c b/src/system/private/dmod_mgr.c index ec342d69..7288f813 100644 --- a/src/system/private/dmod_mgr.c +++ b/src/system/private/dmod_mgr.c @@ -9,7 +9,7 @@ * @brief Names of the system modules - the modules the built-in input APIs belong to * * Packed one after another, each terminated with '\0', the list itself with an - * empty name. Built once (see Dmod_Mgr_InitSystemModules), so that + * empty name. Built once (see Dmod_Mgr_BuildSystemModuleList), so that * Dmod_Mgr_IsSystemModule() does not have to check the signature of every * built-in API on each call. */ @@ -96,7 +96,7 @@ static char* AddSystemModuleName( char* Names, size_t* Size, const char* ModuleN * * @return True if the list is available */ -bool Dmod_Mgr_InitSystemModules( void ) +bool Dmod_Mgr_BuildSystemModuleList( void ) { if( SystemModuleNames != NULL ) { @@ -130,7 +130,7 @@ bool Dmod_Mgr_InitSystemModules( void ) /** * @brief Free the list of the system module names */ -void Dmod_Mgr_DeinitSystemModules( void ) +void Dmod_Mgr_FreeSystemModuleList( void ) { Dmod_Free( SystemModuleNames ); SystemModuleNames = NULL; @@ -173,7 +173,7 @@ bool Dmod_Mgr_IsSystemModule( const char* ModuleName ) DMOD_LOG_ERROR("Cannot check if module is system module - invalid module name (empty or NULL)\n"); return false; } - if( !Dmod_Mgr_InitSystemModules() ) + if( !Dmod_Mgr_BuildSystemModuleList() ) { return IsSystemModuleInApis( ModuleName ); } diff --git a/tests/system/private/tests_dmod_mgr.cpp b/tests/system/private/tests_dmod_mgr.cpp index 3c754244..2f7f6384 100644 --- a/tests/system/private/tests_dmod_mgr.cpp +++ b/tests/system/private/tests_dmod_mgr.cpp @@ -65,28 +65,28 @@ TEST_F(DmodMgrTest, IsNullModule) } /** - * @brief Test for Dmod_Mgr_InitSystemModules + * @brief Test for Dmod_Mgr_BuildSystemModuleList * * The test checks if the list of the system modules can be built more than * once, and if the result of Dmod_Mgr_IsSystemModule does not change with it. */ -TEST_F(DmodMgrTest, InitSystemModules) +TEST_F(DmodMgrTest, BuildSystemModuleList) { - ASSERT_TRUE(Dmod_Mgr_InitSystemModules()); - ASSERT_TRUE(Dmod_Mgr_InitSystemModules()); + ASSERT_TRUE(Dmod_Mgr_BuildSystemModuleList()); + ASSERT_TRUE(Dmod_Mgr_BuildSystemModuleList()); ASSERT_TRUE(Dmod_Mgr_IsSystemModule("Dmod")); ASSERT_FALSE(Dmod_Mgr_IsSystemModule("Test")); } /** - * @brief Test for Dmod_Mgr_DeinitSystemModules + * @brief Test for Dmod_Mgr_FreeSystemModuleList * * The test checks if Dmod_Mgr_IsSystemModule builds the list of the system * modules again when it has been freed. */ -TEST_F(DmodMgrTest, IsSystemModuleAfterDeinit) +TEST_F(DmodMgrTest, IsSystemModuleAfterFreeingList) { - Dmod_Mgr_DeinitSystemModules(); + Dmod_Mgr_FreeSystemModuleList(); ASSERT_TRUE(Dmod_Mgr_IsSystemModule("Dmod")); ASSERT_FALSE(Dmod_Mgr_IsSystemModule("Test")); }