From 47174e775f101c283adffd4d93956c0a7ac9cffc Mon Sep 17 00:00:00 2001 From: Sam Price Date: Wed, 21 Jan 2026 09:34:41 -0500 Subject: [PATCH] Add resource stats API Details: - add OS_resource_stats_t and OS_GetResourceStats for totals/used counts - count active objects via ID map iteration - add unit and coverage tests for stats and null handling --- src/os/inc/osapi-idmap.h | 45 ++++++++ src/os/shared/src/osapi-idmap.c | 67 ++++++++++++ .../shared/src/coveragetest-idmap.c | 31 ++++++ .../shared/src/coveragetest-module.c | 1 + src/unit-tests/oscore-test/CMakeLists.txt | 2 +- .../ut_oscore_resource_stats_test.c | 100 ++++++++++++++++++ .../ut_oscore_resource_stats_test.h | 43 ++++++++ src/unit-tests/oscore-test/ut_oscore_test.c | 1 + src/unit-tests/oscore-test/ut_oscore_test.h | 1 + 9 files changed, 290 insertions(+), 1 deletion(-) create mode 100644 src/unit-tests/oscore-test/ut_oscore_resource_stats_test.c create mode 100644 src/unit-tests/oscore-test/ut_oscore_resource_stats_test.h diff --git a/src/os/inc/osapi-idmap.h b/src/os/inc/osapi-idmap.h index 3f1fb8e14..6ccf754b1 100644 --- a/src/os/inc/osapi-idmap.h +++ b/src/os/inc/osapi-idmap.h @@ -273,6 +273,51 @@ void OS_ForEachObject(osal_id_t creator_id, OS_ArgCallback_t callback_ptr, void void OS_ForEachObjectOfType(osal_objtype_t objtype, osal_id_t creator_id, OS_ArgCallback_t callback_ptr, void *callback_arg); +/*-------------------------------------------------------------------------------------*/ +/** + * @brief Tracks total and in-use counts for a resource type. + */ +typedef struct +{ + uint32 total; + uint32 used; +} OS_resource_count_t; + +/*-------------------------------------------------------------------------------------*/ +/** + * @brief Global counts for all OSAL resource types. + * + * Streams include files, sockets, pipes, and other stream-based objects. + */ +typedef struct +{ + OS_resource_count_t tasks; + OS_resource_count_t queues; + OS_resource_count_t bin_semaphores; + OS_resource_count_t count_semaphores; + OS_resource_count_t mutexes; + OS_resource_count_t streams; + OS_resource_count_t dirs; + OS_resource_count_t timebases; + OS_resource_count_t timers; + OS_resource_count_t modules; + OS_resource_count_t filesystems; + OS_resource_count_t consoles; + OS_resource_count_t condvars; +} OS_resource_stats_t; + +/*-------------------------------------------------------------------------------------*/ +/** + * @brief Returns global totals and in-use counts for OSAL resources. + * + * @param[out] stats Output structure to populate @nonnull + * + * @returns Execution status, see @ref OSReturnCodes + * @retval #OS_SUCCESS @copybrief OS_SUCCESS + * @retval #OS_INVALID_POINTER if stats is NULL + */ +int32 OS_GetResourceStats(OS_resource_stats_t *stats); + /**@}*/ #endif /* OSAPI_IDMAP_H */ diff --git a/src/os/shared/src/osapi-idmap.c b/src/os/shared/src/osapi-idmap.c index 13a3d336a..d1ec0e58c 100644 --- a/src/os/shared/src/osapi-idmap.c +++ b/src/os/shared/src/osapi-idmap.c @@ -162,6 +162,33 @@ uint32 OS_GetMaxForObjectType(osal_objtype_t idtype) } } +static uint32 OS_CountActiveForObjectType(osal_objtype_t idtype) +{ + OS_object_iter_t iter; + uint32 count = 0; + uint32 max_count; + int32 status; + + status = OS_ObjectIdIterateActive(idtype, &iter); + max_count = OS_GetMaxForObjectType(idtype); + if (status == OS_SUCCESS) + { + for (uint32 i = 0; i < max_count; ++i) + { + bool has_next = OS_ObjectIdIteratorGetNext(&iter); + if (!has_next) + { + break; + } + ++count; + } + + OS_ObjectIdIteratorDestroy(&iter); + } + + return count; +} + /*---------------------------------------------------------------- * * Purpose: Local helper routine, not part of OSAL API. @@ -1379,6 +1406,46 @@ void OS_ForEachObjectOfType(osal_objtype_t objtype, osal_id_t creator_id, OS_Arg } } +int32 OS_GetResourceStats(OS_resource_stats_t *stats) +{ + if (stats == NULL) + { + return OS_INVALID_POINTER; + } + + memset(stats, 0, sizeof(*stats)); + + stats->tasks.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_TASK); + stats->queues.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_QUEUE); + stats->bin_semaphores.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_BINSEM); + stats->count_semaphores.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_COUNTSEM); + stats->mutexes.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_MUTEX); + stats->streams.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_STREAM); + stats->dirs.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_DIR); + stats->timebases.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_TIMEBASE); + stats->timers.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_TIMECB); + stats->modules.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_MODULE); + stats->filesystems.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_FILESYS); + stats->consoles.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_CONSOLE); + stats->condvars.total = OS_GetMaxForObjectType(OS_OBJECT_TYPE_OS_CONDVAR); + + stats->tasks.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_TASK); + stats->queues.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_QUEUE); + stats->bin_semaphores.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_BINSEM); + stats->count_semaphores.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_COUNTSEM); + stats->mutexes.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_MUTEX); + stats->streams.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_STREAM); + stats->dirs.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_DIR); + stats->timebases.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_TIMEBASE); + stats->timers.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_TIMECB); + stats->modules.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_MODULE); + stats->filesystems.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_FILESYS); + stats->consoles.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_CONSOLE); + stats->condvars.used = OS_CountActiveForObjectType(OS_OBJECT_TYPE_OS_CONDVAR); + + return OS_SUCCESS; +} + /*---------------------------------------------------------------- * * Purpose: Implemented per public OSAL API diff --git a/src/unit-test-coverage/shared/src/coveragetest-idmap.c b/src/unit-test-coverage/shared/src/coveragetest-idmap.c index 1b8f8ea8a..ff7d8ab8e 100644 --- a/src/unit-test-coverage/shared/src/coveragetest-idmap.c +++ b/src/unit-test-coverage/shared/src/coveragetest-idmap.c @@ -1143,6 +1143,36 @@ void Test_OS_ObjectIdIterator(void) UtAssert_STUB_COUNT(OS_Unlock_Global_Impl, 2); } +void Test_OS_GetResourceStats(void) +{ + /* + * Test Case For: + * int32 OS_GetResourceStats(OS_resource_stats_t *stats) + */ + OS_resource_stats_t stats; + OS_object_token_t token; + OS_common_record_t *record; + uint32 saved_state; + + OSAPI_TEST_FUNCTION_RC(OS_GetResourceStats(NULL), OS_INVALID_POINTER); + + memset(&token, 0, sizeof(token)); + token.obj_type = OS_OBJECT_TYPE_OS_MUTEX; + OS_ObjectIdFindNextFree(&token); + record = OS_OBJECT_TABLE_GET(OS_global_mutex_table, token); + record->active_id = token.obj_id; + + OSAPI_TEST_FUNCTION_RC(OS_GetResourceStats(&stats), OS_SUCCESS); + UtAssert_True(stats.mutexes.used > 0, "mutexes used > 0"); + UtAssert_True(stats.mutexes.total == OS_MAX_MUTEXES, "mutexes total == OS_MAX_MUTEXES"); + + saved_state = OS_SharedGlobalVars.GlobalState; + OS_SharedGlobalVars.GlobalState = 0; + OSAPI_TEST_FUNCTION_RC(OS_GetResourceStats(&stats), OS_SUCCESS); + UtAssert_True(stats.mutexes.used == 0, "mutexes used == 0 with invalid state"); + OS_SharedGlobalVars.GlobalState = saved_state; +} + void Test_OS_ObjectIDInteger(void) { /* @@ -1267,6 +1297,7 @@ void UtTest_Setup(void) ADD_TEST(OS_GetBaseForObjectType); ADD_TEST(OS_GetResourceName); ADD_TEST(OS_ObjectIdIterator); + ADD_TEST(OS_GetResourceStats); ADD_TEST(OS_ObjectIDInteger); ADD_TEST(OS_ObjectIDUndefined); } diff --git a/src/unit-test-coverage/shared/src/coveragetest-module.c b/src/unit-test-coverage/shared/src/coveragetest-module.c index da0a52570..45a34cd4a 100644 --- a/src/unit-test-coverage/shared/src/coveragetest-module.c +++ b/src/unit-test-coverage/shared/src/coveragetest-module.c @@ -283,6 +283,7 @@ void Test_OS_ModuleGetInfo(void) OSAPI_TEST_FUNCTION_RC(OS_ModuleInfo(OS_OBJECT_ID_UNDEFINED, &module_prop), OS_ERROR); } + /* Osapi_Test_Setup * * Purpose: diff --git a/src/unit-tests/oscore-test/CMakeLists.txt b/src/unit-tests/oscore-test/CMakeLists.txt index b3e560f05..4d3eb93f8 100644 --- a/src/unit-tests/oscore-test/CMakeLists.txt +++ b/src/unit-tests/oscore-test/CMakeLists.txt @@ -8,8 +8,8 @@ set(TEST_MODULE_FILES ut_oscore_countsem_test.c ut_oscore_mutex_test.c ut_oscore_task_test.c + ut_oscore_resource_stats_test.c ut_oscore_test.c ) add_osal_ut_exe(osal_core_UT ${TEST_MODULE_FILES}) - diff --git a/src/unit-tests/oscore-test/ut_oscore_resource_stats_test.c b/src/unit-tests/oscore-test/ut_oscore_resource_stats_test.c new file mode 100644 index 000000000..e9db870ed --- /dev/null +++ b/src/unit-tests/oscore-test/ut_oscore_resource_stats_test.c @@ -0,0 +1,100 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/*================================================================================* +** File: ut_oscore_resource_stats_test.c +** Owner: Codex +** Date: September 2025 +**================================================================================*/ + +/*--------------------------------------------------------------------------------* +** Includes +**--------------------------------------------------------------------------------*/ + +#include "ut_oscore_resource_stats_test.h" + +/*--------------------------------------------------------------------------------* +** Local function definitions +**--------------------------------------------------------------------------------*/ + +void UT_os_resource_stats_test(void) +{ + OS_resource_stats_t stats_before; + OS_resource_stats_t stats_after; + osal_id_t queue_id = OS_OBJECT_ID_UNDEFINED; + osal_id_t binsem_id = OS_OBJECT_ID_UNDEFINED; + osal_id_t countsem_id = OS_OBJECT_ID_UNDEFINED; + osal_id_t mutex_id = OS_OBJECT_ID_UNDEFINED; + osal_blockcount_t queue_depth = OSAL_BLOCKCOUNT_C(4); + size_t queue_size = OSAL_SIZE_C(4); + uint32 queue_flags = 0; + uint32 sem_options = 0; + uint32 sem_init_val = 1; + + UT_RETVAL(OS_GetResourceStats(NULL), OS_INVALID_POINTER); + UT_RETVAL(OS_GetResourceStats(&stats_before), OS_SUCCESS); + + UtAssert_True(stats_before.tasks.total == OS_MAX_TASKS, "tasks total == OS_MAX_TASKS"); + UtAssert_True(stats_before.queues.total == OS_MAX_QUEUES, "queues total == OS_MAX_QUEUES"); + UtAssert_True(stats_before.bin_semaphores.total == OS_MAX_BIN_SEMAPHORES, + "bin semaphores total == OS_MAX_BIN_SEMAPHORES"); + UtAssert_True(stats_before.count_semaphores.total == OS_MAX_COUNT_SEMAPHORES, + "count semaphores total == OS_MAX_COUNT_SEMAPHORES"); + UtAssert_True(stats_before.mutexes.total == OS_MAX_MUTEXES, "mutexes total == OS_MAX_MUTEXES"); + UtAssert_True(stats_before.streams.total == OS_MAX_NUM_OPEN_FILES, "streams total == OS_MAX_NUM_OPEN_FILES"); + UtAssert_True(stats_before.dirs.total == OS_MAX_NUM_OPEN_DIRS, "dirs total == OS_MAX_NUM_OPEN_DIRS"); + UtAssert_True(stats_before.timebases.total == OS_MAX_TIMEBASES, "timebases total == OS_MAX_TIMEBASES"); + UtAssert_True(stats_before.timers.total == OS_MAX_TIMERS, "timers total == OS_MAX_TIMERS"); + UtAssert_True(stats_before.modules.total == OS_MAX_MODULES, "modules total == OS_MAX_MODULES"); + UtAssert_True(stats_before.filesystems.total == OS_MAX_FILE_SYSTEMS, "filesystems total == OS_MAX_FILE_SYSTEMS"); + UtAssert_True(stats_before.consoles.total == OS_MAX_CONSOLES, "consoles total == OS_MAX_CONSOLES"); + UtAssert_True(stats_before.condvars.total == OS_MAX_CONDVARS, "condvars total == OS_MAX_CONDVARS"); + + UT_RETVAL(OS_BinSemCreate(&binsem_id, "ResStat_BinSem", sem_init_val, sem_options), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.bin_semaphores.used == (stats_before.bin_semaphores.used + 1), + "bin semaphores used incremented"); + UT_RETVAL(OS_BinSemDelete(binsem_id), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.bin_semaphores.used == stats_before.bin_semaphores.used, + "bin semaphores used restored"); + + UT_RETVAL(OS_CountSemCreate(&countsem_id, "ResStat_CountSem", sem_init_val, sem_options), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.count_semaphores.used == (stats_before.count_semaphores.used + 1), + "count semaphores used incremented"); + UT_RETVAL(OS_CountSemDelete(countsem_id), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.count_semaphores.used == stats_before.count_semaphores.used, + "count semaphores used restored"); + + UT_RETVAL(OS_MutSemCreate(&mutex_id, "ResStat_Mutex", sem_options), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.mutexes.used == (stats_before.mutexes.used + 1), "mutexes used incremented"); + UT_RETVAL(OS_MutSemDelete(mutex_id), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.mutexes.used == stats_before.mutexes.used, "mutexes used restored"); + + UT_RETVAL(OS_QueueCreate(&queue_id, "ResStat_Queue", queue_depth, queue_size, queue_flags), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.queues.used == (stats_before.queues.used + 1), "queues used incremented"); + UT_RETVAL(OS_QueueDelete(queue_id), OS_SUCCESS); + UT_RETVAL(OS_GetResourceStats(&stats_after), OS_SUCCESS); + UtAssert_True(stats_after.queues.used == stats_before.queues.used, "queues used restored"); +} + diff --git a/src/unit-tests/oscore-test/ut_oscore_resource_stats_test.h b/src/unit-tests/oscore-test/ut_oscore_resource_stats_test.h new file mode 100644 index 000000000..d8996d3cd --- /dev/null +++ b/src/unit-tests/oscore-test/ut_oscore_resource_stats_test.h @@ -0,0 +1,43 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * \file + * + * Owner: Codex + * Date: September 2025 + */ + +#ifndef UT_OSCORE_RESOURCE_STATS_TEST_H +#define UT_OSCORE_RESOURCE_STATS_TEST_H + +/*--------------------------------------------------------------------------------* +** Includes +**--------------------------------------------------------------------------------*/ + +#include "ut_os_support.h" + +/*--------------------------------------------------------------------------------* +** Function prototypes +**--------------------------------------------------------------------------------*/ + +void UT_os_resource_stats_test(void); + +/*--------------------------------------------------------------------------------*/ + +#endif /* UT_OSCORE_RESOURCE_STATS_TEST_H */ diff --git a/src/unit-tests/oscore-test/ut_oscore_test.c b/src/unit-tests/oscore-test/ut_oscore_test.c index 8fda28de3..d1e09adbc 100644 --- a/src/unit-tests/oscore-test/ut_oscore_test.c +++ b/src/unit-tests/oscore-test/ut_oscore_test.c @@ -195,6 +195,7 @@ void UtTest_Setup(void) UtTest_Add(UT_os_printf_test, NULL, NULL, "OS_printf"); UtTest_Add(UT_os_printfenable_test, NULL, NULL, "OS_printf_enable"); UtTest_Add(UT_os_printfdisable_test, NULL, NULL, "OS_printf_disable"); + UtTest_Add(UT_os_resource_stats_test, NULL, NULL, "OS_GetResourceStats"); UtTest_Add(UT_os_bin_sem_create_test, NULL, NULL, "OS_BinSemCreate"); UtTest_Add(UT_os_bin_sem_delete_test, NULL, NULL, "OS_BinSemDelete"); diff --git a/src/unit-tests/oscore-test/ut_oscore_test.h b/src/unit-tests/oscore-test/ut_oscore_test.h index d51abe175..a734d70c5 100644 --- a/src/unit-tests/oscore-test/ut_oscore_test.h +++ b/src/unit-tests/oscore-test/ut_oscore_test.h @@ -36,6 +36,7 @@ #include "ut_oscore_countsem_test.h" #include "ut_oscore_mutex_test.h" #include "ut_oscore_queue_test.h" +#include "ut_oscore_resource_stats_test.h" #include "ut_oscore_select_test.h" #include "ut_oscore_task_test.h"