diff --git a/inc/private/dmod_mgr.h b/inc/private/dmod_mgr.h index e52b34f..2f656f5 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_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 6820e59..4a83a52 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_BuildSystemModuleList() ) + { + 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_FreeSystemModuleList(); Dmod_Irq_Deinit(); return true; } diff --git a/src/system/private/dmod_mgr.c b/src/system/private/dmod_mgr.c index 173b417..7288f81 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_BuildSystemModuleList), 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_BuildSystemModuleList( 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_FreeSystemModuleList( 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_BuildSystemModuleList() ) + { + return IsSystemModuleInApis( ModuleName ); + } + return FindSystemModuleName( SystemModuleNames, ModuleName, strlen( ModuleName ) ); +} + /** * @brief Prints all system modules to the log */ @@ -53,12 +203,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 +222,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 ); } diff --git a/tests/system/private/tests_dmod_mgr.cpp b/tests/system/private/tests_dmod_mgr.cpp index b70e850..2f7f638 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_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, BuildSystemModuleList) +{ + 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_FreeSystemModuleList + * + * The test checks if Dmod_Mgr_IsSystemModule builds the list of the system + * modules again when it has been freed. + */ +TEST_F(DmodMgrTest, IsSystemModuleAfterFreeingList) +{ + Dmod_Mgr_FreeSystemModuleList(); + 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 // ===============================================================