diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 0d38fc55ce..51cf50c031 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -109,6 +109,7 @@ basestring bashcompinit Bassic batchmode +bavail bbd bc bcm @@ -497,6 +498,7 @@ FADV fadvise FAKELOGGER fallocate +fallthrough fcheck fclose fcntl @@ -555,6 +557,7 @@ fprintf fprofile fptr fputil +Fregoso frontend frox frsize @@ -777,6 +780,7 @@ isfxmlwriter isgenerator ishii isinstance +isnan isoschematron isr ISREG @@ -936,6 +940,7 @@ membername memcheck memcmp memcpy +meminfo memname memoizing memset @@ -1330,6 +1335,7 @@ saxutils sbb SBF sbin +scanf sched schem schematron @@ -1382,6 +1388,7 @@ setval setw sev sface +sfregoso sgl SGN SHELLCOMMAND @@ -1453,6 +1460,7 @@ startswith startuml startword staticmethod +statfs statvfs stdarg STDC @@ -1719,6 +1727,7 @@ VERSIONED versioning vfd VFILE +vfs vhd vhdl viewforum @@ -1729,6 +1738,8 @@ virtualization virtualized vlist vm +vmstat +vmsize VMIN vsnprintf VTIME diff --git a/Os/Baremetal/SystemResources.cpp b/Os/Baremetal/SystemResources.cpp new file mode 100644 index 0000000000..c8d3126376 --- /dev/null +++ b/Os/Baremetal/SystemResources.cpp @@ -0,0 +1,41 @@ +// ====================================================================== +// \title Baremetal/SystemResources.cpp +// \author mstarch +// \brief hpp file for SystemResources component implementation class +// +// \copyright +// Copyright 2021, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== + +namespace Os { + +SystemResources::SystemResourcesStatus SystemResources::getCpuCount(U32& cpuCount) { + // Assumes 1 CPU + cpuCount = 1; + return SYSTEM_RESOURCES_OK; +} + +SystemResources::SystemResourcesStatus SystemResources::getCpuTicks(CpuTicks& cpu_ticks, U32 cpu_index) { + // Always 100 percent + cpu_ticks.used = 1; + cpu_ticks.total = 1; + return SYSTEM_RESOURCES_OK; +} + + +SystemResources::SystemResourcesStatus SystemResources::getMemUtil(MemUtil& memory_util) { + U8 stack_allocation = 0; + U8* heap_allocation = new U8; + if (heap_allocation != NULL) { + // Crude way to estimate memory usage: stack grows down, heap grows up. Stack - Heap = FREE bytes before collision + U64 free = static_cast(&stack_allocation - heap_allocation); + memory_util.total = free; + memory_util.util = 0; + delete heap_allocation; + } + return SYSTEM_RESOURCES_OK; +} +} // namespace Os diff --git a/Os/CMakeLists.txt b/Os/CMakeLists.txt index 4cb2129aaa..d459eb0375 100644 --- a/Os/CMakeLists.txt +++ b/Os/CMakeLists.txt @@ -57,12 +57,14 @@ endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/MacOs/IPCQueueStub.cpp" + "${CMAKE_CURRENT_LIST_DIR}/MacOs/SystemResources.cpp" ) # Linux IPC queues implementation elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Posix/IPCQueue.cpp" "${CMAKE_CURRENT_LIST_DIR}/Posix/LocklessQueue.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Linux/SystemResources.cpp" ) # Shared libraries need an -rt dependency for mq libs if (BUILD_SHARED_LIBS) @@ -75,12 +77,13 @@ endif() if (FPRIME_USE_BAREMETAL_SCHEDULER) add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Baremetal/TaskRunner/") foreach (ITER_ITEM IN LISTS SOURCE_FILES) - if (ITER_ITEM MATCHES "Task\.cpp$") + if (ITER_ITEM MATCHES "Task\\.cpp$") list(REMOVE_ITEM SOURCE_FILES "${ITER_ITEM}") endif() endforeach() list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/Task.cpp") list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/Mutex.cpp") + list(APPEND SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/Baremetal/SystemResources.cpp") endif() register_fprime_module() @@ -97,6 +100,7 @@ set(UT_SOURCE_FILES "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsValidateFileTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsTaskTest.cpp" "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsFileSystemTest.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/OsSystemResourcesTest.cpp" ) register_fprime_ut() diff --git a/Os/Linux/SystemResources.cpp b/Os/Linux/SystemResources.cpp new file mode 100644 index 0000000000..6fc1bd56d9 --- /dev/null +++ b/Os/Linux/SystemResources.cpp @@ -0,0 +1,137 @@ +// ====================================================================== +// \title Linux/SystemResources.cpp +// \author sfregoso +// \brief hpp file for SystemResources component implementation class +// +// \copyright +// Copyright 2021, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== +#include /* fopen() */ +#include /* scanf */ +#include /* statfs() */ +#include +#include +#include + +namespace Os { + static const U32 LINUX_CPU_LINE_LIMIT = 1024; // Maximum lines to read before bailing + + SystemResources::SystemResourcesStatus SystemResources::getCpuCount(U32 &cpuCount) { + char line[512] = {0}; + FILE *fp = NULL; + U32 cpu_count = 0; + + if ((fp = fopen("/proc/stat", "r")) == NULL) { + return SYSTEM_RESOURCES_ERROR; + } + + if (fgets(line, sizeof(line), fp) == NULL) { //1st line. Aggregate cpu line. Skip + fclose(fp); + return SYSTEM_RESOURCES_ERROR; + } + + for (U32 i = 0; i < LINUX_CPU_LINE_LIMIT; i++) { + if (fgets(line, sizeof(line), fp) == NULL) { //cpu# line + break; + } + + if (!(line[0] == 'c' && line[1] == 'p' && line[2] == 'u')) { + break; + } + cpu_count++; + } + fclose(fp); + cpuCount = cpu_count; + + return SYSTEM_RESOURCES_OK; + } + + SystemResources::SystemResourcesStatus SystemResources::getCpuTicks(CpuTicks &cpu_ticks, U32 cpu_index) { + char line[512] = {0}; + FILE *fp = NULL; + U32 cpu_data[4] = {0}; + U32 cpuCount = 0; + SystemResources::SystemResourcesStatus status = SYSTEM_RESOURCES_ERROR; + U64 cpuUsed = 0; + U64 cpuTotal = 0; + + if ((status = getCpuCount(cpuCount)) != SYSTEM_RESOURCES_OK) { + return status; + } + + if (cpu_index >= cpuCount) { + return SYSTEM_RESOURCES_ERROR; + } + + if ((fp = fopen("/proc/stat", "r")) == NULL) { + + return SYSTEM_RESOURCES_ERROR; + + } + + if (fgets(line, sizeof(line), fp) == NULL) { //1st line. Aggregate cpu line. + fclose(fp); + return SYSTEM_RESOURCES_ERROR; + } + + for (U32 i = 0; i < cpu_index + 1; i++) { + if (fgets(line, sizeof(line), fp) == NULL) { //cpu# line + fclose(fp); + return SYSTEM_RESOURCES_ERROR; + } + if (i != cpu_index) { continue; } + + if (!(line[0] == 'c' && line[1] == 'p' && line[2] == 'u')) { + fclose(fp); + return SYSTEM_RESOURCES_ERROR; + } + + sscanf(line, "%*s %d %d %d %d", &cpu_data[0], + &cpu_data[1], + &cpu_data[2], + &cpu_data[3]); //cpu#: 4 numbers: usr, nice, sys, idle + + cpuUsed = cpu_data[0] + cpu_data[1] + cpu_data[2]; + cpuTotal = cpu_data[0] + cpu_data[1] + cpu_data[2] + cpu_data[3]; + + break; + } + fclose(fp); + + cpu_ticks.used = cpuUsed; + cpu_ticks.total = cpuTotal; + return SYSTEM_RESOURCES_OK; + } + + SystemResources::SystemResourcesStatus SystemResources::getMemUtil(MemUtil &memory_util) { + FILE *fp = NULL; + NATIVE_INT_TYPE total = 0; + NATIVE_INT_TYPE free = 0; + // Fallbacks + memory_util.total = 1; + memory_util.used = 1; + + fp = fopen("/proc/meminfo", "r"); + if (fp == NULL) { + return SYSTEM_RESOURCES_ERROR; + } + + if (fscanf(fp, "%*s %d %*s", &total) != 1 || /* 1st line is MemTotal */ + fscanf(fp, "%*s %d", &free) != 1) { /* 2nd line is MemFree */ + fclose(fp); + return SYSTEM_RESOURCES_ERROR; + } + fclose(fp); + + // Check results + if (total < 0 or free < 0 or total < free) { + return SYSTEM_RESOURCES_ERROR; + } + memory_util.total = static_cast(total) * 1024; // KB to Bytes + memory_util.used = static_cast(total - free) * 1024; + return SYSTEM_RESOURCES_OK; + } +} diff --git a/Os/MacOs/SystemResources.cpp b/Os/MacOs/SystemResources.cpp new file mode 100644 index 0000000000..60f1efe301 --- /dev/null +++ b/Os/MacOs/SystemResources.cpp @@ -0,0 +1,132 @@ +// ====================================================================== +// \title MacOs/SystemResources.cpp +// \author mstarch +// \brief hpp file for SystemResources component implementation class +// +// \copyright +// Copyright 2021, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== +#include +#include +#include +#include +#include +#include +#include + +namespace Os { +/** + * \brief reads macOS virtual memory statistics for memory calculation + * + * Queries the macOS kernel for virtual memory information. These items are returned in units of page-size and are + * then converted back into bytes. + * + * Thanks to: https://stackoverflow.com/questions/8782228/retrieve-ram-info-on-a-mac + * + * \param used: used memory in bytes + * \param total: total memory in bytes + * \return: kern_return_t with success/failure straight from the kernel + */ +kern_return_t vm_stat_helper(U64& used, U64& total) { + mach_msg_type_number_t count = HOST_VM_INFO_COUNT; + vm_statistics_data_t vmstat; + vm_size_t vmsize; + + kern_return_t stat1 = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count); + kern_return_t stat2 = host_page_size(mach_host_self(), &vmsize); + + if (KERN_SUCCESS == stat1 and KERN_SUCCESS == stat2) { + // Wired (permanently in RAM), active (recently used), and inactive (not recently used) pages + used = vmstat.wire_count + vmstat.active_count + vmstat.inactive_count; + total = used + vmstat.free_count; + + // Pages to totals + used *= vmsize; + total *= vmsize; + } + return (stat1 == KERN_SUCCESS) ? stat2 : stat1; +} + +/** + * \brief helper around raw CPU capture API + * + * Calls for the CPU information from the machine, improving readability in cpu_by_index + * + * \param cpu_load_info: filled with CPU data + * \param cpu_count: filled with CPU count + * + * \return success/failure using kern_return_t + */ +kern_return_t cpu_data_helper(processor_cpu_load_info_t& cpu_load_info, U32& cpu_count) { + mach_msg_type_number_t processor_msg_count; + kern_return_t stat = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, + (processor_info_array_t*)&cpu_load_info, &processor_msg_count); + return stat; +} +/** + * \brief Query for a single CPU's ticks information + * + * Queries all CPU information but only deals with a single CPU's output. This is done because the load average is + * tracked sample to sample and the call pattern is cpu0, cpu1, ..., cpu last, wait for sample window, cpu0, ... and + * thus each call should update one CPU's sample or only the last cpu will have the benefit of the sampling window. + * + * \param cpu_index: index of current CPU being queried + * \param used: filled with CPU's used ticks count + * \param total: filled with CPU's total ticks + * + * \return success/failure using kern_return_t + */ +kern_return_t cpu_by_index(U32 cpu_index, U64& used, U64& total) { + processor_cpu_load_info_t cpu_load_info; + U32 cpu_count = 0; + kern_return_t stat = cpu_data_helper(cpu_load_info, cpu_count); + + // Failure for CPU index + if (cpu_count <= cpu_index) { + stat = KERN_FAILURE; + } else if (KERN_SUCCESS == stat) { + FW_ASSERT(cpu_count > cpu_index, cpu_count, cpu_index); // Will fail if the CPU count changes while running + processor_cpu_load_info per_cpu_info = cpu_load_info[(cpu_count > cpu_index) ? cpu_index : 0]; + + // Total the ticks across the different states: idle, system, user, etc... + total = 0; + for (U32 i = 0; i < CPU_STATE_MAX; i++) { + total += per_cpu_info.cpu_ticks[i]; + } + used = total - per_cpu_info.cpu_ticks[CPU_STATE_IDLE]; + } + return stat; +} + +SystemResources::SystemResourcesStatus SystemResources::getCpuCount(U32& cpuCount) { + processor_cpu_load_info_t cpu_load_info; + if (KERN_SUCCESS == cpu_data_helper(cpu_load_info, cpuCount)) { + return SYSTEM_RESOURCES_OK; + } + cpuCount = 0; + return SYSTEM_RESOURCES_ERROR; +} + +SystemResources::SystemResourcesStatus SystemResources::getCpuTicks(CpuTicks& cpu_ticks, U32 cpu_index) { + // Fallbacks in case of error + cpu_ticks.used = 1; + cpu_ticks.total = 1; + kern_return_t stat = cpu_by_index(cpu_index, cpu_ticks.used, cpu_ticks.total); + return (stat == KERN_SUCCESS) ? SYSTEM_RESOURCES_OK : SYSTEM_RESOURCES_ERROR; +} + + +SystemResources::SystemResourcesStatus SystemResources::getMemUtil(MemUtil& memory_util) { + // Call out VM helper + if (KERN_SUCCESS == vm_stat_helper(memory_util.used, memory_util.total)) { + return SYSTEM_RESOURCES_OK; + } + // Force something sensible, while preventing divide by zero + memory_util.total = 1; + memory_util.used = 1; + return SYSTEM_RESOURCES_ERROR; +} +} // namespace Os diff --git a/Os/SystemResources.hpp b/Os/SystemResources.hpp new file mode 100644 index 0000000000..36f7bc7527 --- /dev/null +++ b/Os/SystemResources.hpp @@ -0,0 +1,69 @@ +// ====================================================================== +// \title SystemResources.hpp +// \author mstarch +// \brief hpp file for SystemResources component implementation class +// +// \copyright +// Copyright 2021, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== +#ifndef _SystemResources_hpp_ +#define _SystemResources_hpp_ + +#include + +namespace Os { +namespace SystemResources { + +enum SystemResourcesStatus { + SYSTEM_RESOURCES_OK, //!< Call was successful + SYSTEM_RESOURCES_ERROR, //!< Call failed +}; + +struct CpuTicks { + U64 used; //!< Filled with non-idle (system, user) CPU ticks + U64 total; //!< Filled with total CPU ticks +}; + +struct MemUtil { + U64 used; //!< Filled with used bytes of volatile memory (permanent, paged-in) + U64 total; //!< Filled with total non-volatile memory +}; + +/** + * \brief Request the count of the CPUs detected by the system + * + * \param cpu_count: (output) filled with CPU count on system + * \return: SYSTEM_RESOURCES_OK with valid CPU count, SYSTEM_RESOURCES_ERROR when error occurs + */ +SystemResourcesStatus getCpuCount(U32& cpu_count); + +/** + * \brief Get the CPU tick information for a given CPU + * + * CPU ticks represent a small time slice of processor time. This will retrieve the used CPU ticks and total + * ticks for a given CPU. This information in a running accumulation and thus a sample-to-sample + * differencing is needed to see the 'realtime' changing load. This shall be done by the caller. + * + * \param ticks: (output) filled with the tick information for the given CPU + * \param cpu_index: index for CPU to read + * \return: SYSTEM_RESOURCES_OK with valid CPU count, SYSTEM_RESOURCES_ERROR when error occurs + */ +SystemResourcesStatus getCpuTicks(CpuTicks& ticks, U32 cpu_index = 0); + +/** + * \brief Calculate the across-all-cpu average tick information + * + * See `getCpuTicks`. Operates in a similar capacity, but the aggregation is done across all CPUs not for + * a specific CPU. + * + * \param ticks: (output) filled with the tick information for the given CPU + * \return: SYSTEM_RESOURCES_OK with valid CPU count, SYSTEM_RESOURCES_ERROR when error occurs + */ +SystemResourcesStatus getMemUtil(MemUtil& memory_util); +} // namespace SystemResources +} // namespace Os + +#endif diff --git a/Os/test/ut/OsSystemResourcesTest.cpp b/Os/test/ut/OsSystemResourcesTest.cpp new file mode 100644 index 0000000000..1d6219cded --- /dev/null +++ b/Os/test/ut/OsSystemResourcesTest.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +void testTestSystemResources() { + + Os::SystemResources::SystemResourcesStatus sys_res_status; + Os::SystemResources::CpuTicks cpuUtil; + Os::SystemResources::MemUtil memUtil; + + U32 cpuCount; + U32 cpuIndex; + + sys_res_status = Os::SystemResources::getCpuCount(cpuCount); + ASSERT_EQ(sys_res_status, Os::SystemResources::SystemResourcesStatus::SYSTEM_RESOURCES_OK); + + cpuIndex = 0; + sys_res_status = Os::SystemResources::getCpuTicks(cpuUtil, cpuIndex); + ASSERT_EQ(sys_res_status, Os::SystemResources::SystemResourcesStatus::SYSTEM_RESOURCES_OK); + + cpuIndex = 1000; + sys_res_status = Os::SystemResources::getCpuTicks(cpuUtil, cpuIndex); + ASSERT_EQ(sys_res_status, Os::SystemResources::SystemResourcesStatus::SYSTEM_RESOURCES_ERROR); + + sys_res_status = Os::SystemResources::getMemUtil(memUtil); + ASSERT_EQ(sys_res_status, Os::SystemResources::SystemResourcesStatus::SYSTEM_RESOURCES_OK); +} + +extern "C" { + void systemResourcesTest(void); +} + +void systemResourcesTest(void) { + testTestSystemResources(); +} diff --git a/Os/test/ut/TestMain.cpp b/Os/test/ut/TestMain.cpp index 0af1d5a537..d68b1c10b3 100644 --- a/Os/test/ut/TestMain.cpp +++ b/Os/test/ut/TestMain.cpp @@ -14,6 +14,7 @@ extern "C" { void intervalTimerTest(void); void fileSystemTest(void); void validateFileTest(const char* filename); + void systemResourcesTest(void); } const char* filename; TEST(Nominal, StartTestTask) { @@ -44,6 +45,9 @@ TEST(Nominal, FileSystemTest) { TEST(Nominal, ValidateFileTest) { validateFileTest(filename); } +TEST(Nominal, SystemResourcesTest) { + systemResourcesTest(); +} int main(int argc, char* argv[]) { filename = argv[0]; diff --git a/Ref/Top/Components.hpp b/Ref/Top/Components.hpp index 6fc0ade424..2b35f3a7a7 100644 --- a/Ref/Top/Components.hpp +++ b/Ref/Top/Components.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -65,4 +66,5 @@ extern Svc::StaticMemoryComponentImpl staticMemory; extern Drv::TcpClientComponentImpl comm; extern Svc::FramerComponentImpl downlink; extern Svc::DeframerComponentImpl uplink; +extern Svc::SystemResources resources; #endif diff --git a/Ref/Top/RefTopologyAppAi.xml b/Ref/Top/RefTopologyAppAi.xml index f73a022d2e..5d5830bfcc 100644 --- a/Ref/Top/RefTopologyAppAi.xml +++ b/Ref/Top/RefTopologyAppAi.xml @@ -31,6 +31,7 @@ Svc/StaticMemory/StaticMemoryComponentAi.xml Svc/Framer/FramerComponentAi.xml Svc/Deframer/DeframerComponentAi.xml + Svc/SystemResources/SystemResourcesComponentAi.xml @@ -76,6 +77,7 @@ + @@ -187,7 +189,7 @@ - + @@ -221,6 +223,10 @@ + + + + @@ -292,6 +298,10 @@ + + + + @@ -360,6 +370,10 @@ + + + + @@ -462,6 +476,10 @@ + + + + @@ -541,6 +559,10 @@ + + + + @@ -625,6 +647,10 @@ + + + + @@ -738,7 +764,11 @@ - + + + + + @@ -797,7 +827,7 @@ - + @@ -821,6 +851,10 @@ + + + + diff --git a/Ref/Top/Topology.cpp b/Ref/Top/Topology.cpp index 5fe2e79516..72ed49632e 100644 --- a/Ref/Top/Topology.cpp +++ b/Ref/Top/Topology.cpp @@ -104,6 +104,8 @@ Svc::FramerComponentImpl downlink(FW_OPTIONAL_NAME("downlink")); Svc::DeframerComponentImpl uplink(FW_OPTIONAL_NAME("uplink")); +Svc::SystemResources resources(FW_OPTIONAL_NAME("resources")); + const char* getHealthName(Fw::ObjBase& comp) { #if FW_OBJECT_NAMES == 1 return comp.getObjName(); @@ -170,6 +172,7 @@ bool constructApp(bool dump, U32 port_number, char* hostname) { fatalHandler.init(0); health.init(25,0); pingRcvr.init(10); + resources.init(0); downlink.setup(framing); uplink.setup(deframing); @@ -202,6 +205,7 @@ bool constructApp(bool dump, U32 port_number, char* hostname) { health.regCommands(); pingRcvr.regCommands(); pktTlm.regCommands(); + resources.regCommands(); // read parameters prmDb.readParamFile(); diff --git a/Svc/CMakeLists.txt b/Svc/CMakeLists.txt index 28ac758cd3..810a153d0c 100644 --- a/Svc/CMakeLists.txt +++ b/Svc/CMakeLists.txt @@ -37,6 +37,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StaticMemory/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Time/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmChan/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/TlmPacketizer/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/SystemResources/") # Text logger components included by default, # but can be disabled if FW_ENABLE_TEXT_LOGGING=0 is desired. diff --git a/Svc/SystemResources/CMakeLists.txt b/Svc/SystemResources/CMakeLists.txt new file mode 100644 index 0000000000..b807ba2b67 --- /dev/null +++ b/Svc/SystemResources/CMakeLists.txt @@ -0,0 +1,32 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding diles +# MOD_DEPS: (optional) module dependencies +# +# Note: using PROJECT_NAME as EXECUTABLE_NAME +#### +set(SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/SystemResourcesComponentAi.xml" + "${CMAKE_CURRENT_LIST_DIR}/SystemResources.cpp" +) +set(MOD_DEPS + Os +) +register_fprime_module() +get_module_name(${CMAKE_CURRENT_LIST_DIR}) +add_dependencies(${MODULE_NAME} version) + +### UTs ### +set(UT_SOURCE_FILES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/Tester.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/TestMain.cpp" + "${CMAKE_CURRENT_LIST_DIR}/SystemResources.cpp" + "${CMAKE_CURRENT_LIST_DIR}/SystemResourcesComponentAi.xml" +) +register_fprime_ut() +# Implementation requires switch cascade +if (TARGET ${MODULE_NAME}_ut_exe) + target_compile_options(${MODULE_NAME}_ut_exe PRIVATE -Wno-implicit-fallthrough) +endif() + diff --git a/Svc/SystemResources/SystemResources.cpp b/Svc/SystemResources/SystemResources.cpp new file mode 100644 index 0000000000..176e6f974a --- /dev/null +++ b/Svc/SystemResources/SystemResources.cpp @@ -0,0 +1,158 @@ +// ====================================================================== +// \title SystemResourcesComponentImpl.cpp +// \author sfregoso +// \brief cpp file for SystemResources component implementation class +// +// \copyright +// Copyright 2021, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== + +#include //isnan() +#include +#include +#include "Fw/Types/BasicTypes.hpp" + +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction, initialization, and destruction +// ---------------------------------------------------------------------- + +SystemResources ::SystemResources(const char* const compName) + : SystemResourcesComponentBase(compName), m_cpu_count(0), m_enable(true) { + + // Structure initializations + m_mem.used = 0; + m_mem.total = 0; + for (U32 i = 0; i < CPU_COUNT; i++) { + m_cpu[i].used = 0; + m_cpu[i].total = 0; + m_cpu_prev[i].used = 0; + m_cpu_prev[i].total = 0; + } + + if (Os::SystemResources::getCpuCount(m_cpu_count) == Os::SystemResources::SYSTEM_RESOURCES_ERROR) { + m_cpu_count = 0; + } + + m_cpu_count = (m_cpu_count >= CPU_COUNT) ? CPU_COUNT : m_cpu_count; +} + +void SystemResources ::init(const NATIVE_INT_TYPE instance) { + SystemResourcesComponentBase::init(instance); + + m_cpu_tlm_functions[0] = &Svc::SystemResources::tlmWrite_CPU_00; + m_cpu_tlm_functions[1] = &Svc::SystemResources::tlmWrite_CPU_01; + m_cpu_tlm_functions[2] = &Svc::SystemResources::tlmWrite_CPU_02; + m_cpu_tlm_functions[3] = &Svc::SystemResources::tlmWrite_CPU_03; + m_cpu_tlm_functions[4] = &Svc::SystemResources::tlmWrite_CPU_04; + m_cpu_tlm_functions[5] = &Svc::SystemResources::tlmWrite_CPU_05; + m_cpu_tlm_functions[6] = &Svc::SystemResources::tlmWrite_CPU_06; + m_cpu_tlm_functions[7] = &Svc::SystemResources::tlmWrite_CPU_07; + m_cpu_tlm_functions[8] = &Svc::SystemResources::tlmWrite_CPU_08; + m_cpu_tlm_functions[9] = &Svc::SystemResources::tlmWrite_CPU_09; + m_cpu_tlm_functions[10] = &Svc::SystemResources::tlmWrite_CPU_10; + m_cpu_tlm_functions[11] = &Svc::SystemResources::tlmWrite_CPU_11; + m_cpu_tlm_functions[12] = &Svc::SystemResources::tlmWrite_CPU_12; + m_cpu_tlm_functions[13] = &Svc::SystemResources::tlmWrite_CPU_13; + m_cpu_tlm_functions[14] = &Svc::SystemResources::tlmWrite_CPU_14; + m_cpu_tlm_functions[15] = &Svc::SystemResources::tlmWrite_CPU_15; +} + +SystemResources ::~SystemResources() {} + +// ---------------------------------------------------------------------- +// Handler implementations for user-defined typed input ports +// ---------------------------------------------------------------------- + +void SystemResources ::run_handler(const NATIVE_INT_TYPE portNum, NATIVE_UINT_TYPE tick_time_hz) { + if (m_enable) { + Cpu(); + Mem(); + PhysMem(); + Version(); + } +} + +// ---------------------------------------------------------------------- +// Command handler implementations +// ---------------------------------------------------------------------- + +void SystemResources ::ENABLE_cmdHandler(const FwOpcodeType opCode, + const U32 cmdSeq, + SystemResourceEnabled enable) { + m_enable = (enable == SYS_RES_ENABLED); + this->cmdResponse_out(opCode, cmdSeq, Fw::COMMAND_OK); +} + +void SystemResources ::VERSION_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq) { + Fw::LogStringArg version_string(VERSION); + + this->log_ACTIVITY_LO_VERSION(version_string); + this->cmdResponse_out(opCode, cmdSeq, Fw::COMMAND_OK); +} + +F32 SystemResources::compCpuUtil(Os::SystemResources::CpuTicks current, Os::SystemResources::CpuTicks previous) { + F32 util = 100.0f; + // Prevent divide by zero on fast-sample + if ((current.total - previous.total) != 0) { + // Compute CPU % Utilization + util = (static_cast(current.used - previous.used) / static_cast(current.total - previous.total)) * + 100.0f; + util = isnan(util) ? 100.0f : util; + } + return util; +} + +void SystemResources::Cpu() { + U32 count = 0; + F32 cpuAvg = 0; + + for (U32 i = 0; i < m_cpu_count && i < CPU_COUNT; i++) { + Os::SystemResources::SystemResourcesStatus status = Os::SystemResources::getCpuTicks(m_cpu[i], i); + // Best-effort calculations and telemetry + if (status == Os::SystemResources::SYSTEM_RESOURCES_OK) { + F32 cpuUtil = compCpuUtil(m_cpu[i], m_cpu_prev[i]); + cpuAvg += cpuUtil; + + // Send telemetry using telemetry output table + FW_ASSERT(this->m_cpu_tlm_functions[i]); + (this->*m_cpu_tlm_functions[i])(cpuUtil, Fw::Time()); + + // Store cpu used and total + m_cpu_prev[i] = m_cpu[i]; + count++; + } + } + + cpuAvg = (count == 0) ? 0.0f : (cpuAvg / count); + this->tlmWrite_CPU(cpuAvg); +} + +void SystemResources::Mem() { + Os::SystemResources::SystemResourcesStatus status; + + if ((status = Os::SystemResources::getMemUtil(m_mem)) == Os::SystemResources::SYSTEM_RESOURCES_OK) { + this->tlmWrite_MEMORY_TOTAL(m_mem.total / 1024); + this->tlmWrite_MEMORY_USED(m_mem.used / 1024); + } +} + +void SystemResources::PhysMem() { + U64 total = 0; + U64 free = 0; + + if (Os::FileSystem::getFreeSpace("/", total, free) == Os::FileSystem::OP_OK) { + this->tlmWrite_NON_VOLATILE_FREE(free / 1024); + this->tlmWrite_NON_VOLATILE_TOTAL(total / 1024); + } +} + +void SystemResources::Version() { + Fw::TlmString version_string(VERSION); + this->tlmWrite_VERSION(version_string); +} +} // end namespace Svc diff --git a/Svc/SystemResources/SystemResources.hpp b/Svc/SystemResources/SystemResources.hpp new file mode 100644 index 0000000000..e8c2f2dab1 --- /dev/null +++ b/Svc/SystemResources/SystemResources.hpp @@ -0,0 +1,96 @@ +// ====================================================================== +// \title SystemResourcesComponentImpl.hpp +// \author Santos F. Fregoso +// \brief hpp file for SystemResources component implementation class +// +// \copyright +// Copyright 2021, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== + +#ifndef SystemResources_HPP +#define SystemResources_HPP + +#include "Svc/SystemResources/SystemResourcesComponentAc.hpp" +#include "Os/SystemResources.hpp" +#include "Os/FileSystem.hpp" + +namespace Svc { + +class SystemResources : public SystemResourcesComponentBase { + public: + // ---------------------------------------------------------------------- + // Construction, initialization, and destruction + // ---------------------------------------------------------------------- + + //! Construct object SystemResources + //! + SystemResources(const char* const compName /*!< The component name*/ + ); + + //! Initialize object SystemResources + //! + void init(const NATIVE_INT_TYPE instance = 0 /*!< The instance number*/ + ); + + //! Destroy object SystemResources + //! + ~SystemResources(void); + + typedef void (SystemResourcesComponentBase::*cpuTlmFunc)(F32, Fw::Time); + + PRIVATE : + + // ---------------------------------------------------------------------- + // Handler implementations for user-defined typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for run + //! + void + run_handler(const NATIVE_INT_TYPE portNum, /*!< The port number*/ + NATIVE_UINT_TYPE context /*!< The call order*/ + ); + + private: + // ---------------------------------------------------------------------- + // Command handler implementations + // ---------------------------------------------------------------------- + + //! Implementation for SYS_RES_ENABLE command handler + //! A command to enable or disable system resource telemetry + void ENABLE_cmdHandler( + const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq, /*!< The command sequence number*/ + SystemResourceEnabled enable /*!< whether or not system resource telemetry is enabled*/ + ); + + //! Implementation for VERSION command handler + //! Report version as EVR + void VERSION_cmdHandler(const FwOpcodeType opCode, /*!< The opcode*/ + const U32 cmdSeq /*!< The command sequence number*/ + ); + + private: + void Cpu(); + void Mem(); + void PhysMem(); + void Version(); + F32 compCpuUtil(Os::SystemResources::CpuTicks current, Os::SystemResources::CpuTicks previous); + + + static const U32 CPU_COUNT = 16; /*!< Maximum number of CPUs to report as telemetry */ + + cpuTlmFunc m_cpu_tlm_functions[CPU_COUNT]; /*!< Function pointer to specific CPU telemetry */ + U32 m_cpu_count; /*!< Number of CPUs used by the system */ + Os::SystemResources::MemUtil m_mem; /*!< RAM memory information */ + Os::SystemResources::CpuTicks m_cpu[CPU_COUNT]; /*!< CPU information for each CPU on the system */ + Os::SystemResources::CpuTicks m_cpu_prev[CPU_COUNT]; /*!< Previous iteration CPU information */ + bool m_enable; /*!< Send telemetry when TRUE. Don't send when FALSE */ +}; + +} // end namespace Svc + +#endif diff --git a/Svc/SystemResources/SystemResourcesComponentAi.xml b/Svc/SystemResources/SystemResourcesComponentAi.xml new file mode 100644 index 0000000000..26a2fc9f78 --- /dev/null +++ b/Svc/SystemResources/SystemResourcesComponentAi.xml @@ -0,0 +1,158 @@ + + + + + Os/SystemResources.hpp + + Svc/Sched/SchedPortAi.xml + + + Run port + + + + + + A command to enable or disable system resource telemetry + + + + + + + + whether or not system resource telemetry is enabled + + + + + + Report version as EVR + + + + + + + Total system memory in KB + + + + + System memory used in KB + + + + + System non-volatile available in KB + + + + + System non-volatile available in KB + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + System's CPU Percentage + + + + + + + Version of the git repository. + + + + version string + + + + + diff --git a/Svc/SystemResources/test/ut/TestMain.cpp b/Svc/SystemResources/test/ut/TestMain.cpp new file mode 100644 index 0000000000..e0b11146c2 --- /dev/null +++ b/Svc/SystemResources/test/ut/TestMain.cpp @@ -0,0 +1,25 @@ +// ---------------------------------------------------------------------- +// TestMain.cpp +// ---------------------------------------------------------------------- + +#include "Tester.hpp" + +TEST(Nominal, Telemetry) { + Svc::Tester tester; + tester.test_tlm(); +} + +TEST(OffNominal, Disabled) { + Svc::Tester tester; + tester.test_disable_enable(); +} + +TEST(Nominal, Events) { + Svc::Tester tester; + tester.test_version_evr(); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/SystemResources/test/ut/Tester.cpp b/Svc/SystemResources/test/ut/Tester.cpp new file mode 100644 index 0000000000..84e6178436 --- /dev/null +++ b/Svc/SystemResources/test/ut/Tester.cpp @@ -0,0 +1,154 @@ +// ====================================================================== +// \title SystemResources.hpp +// \author mstarch +// \brief cpp file for SystemResources test harness implementation class +// +// \copyright +// Copyright 2009-2015, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== + +#include "Tester.hpp" +#include "version.hpp" +#define INSTANCE 0 +#define MAX_HISTORY_SIZE 100 + +namespace Svc { + +// ---------------------------------------------------------------------- +// Construction and destruction +// ---------------------------------------------------------------------- + +Tester ::Tester(void) : SystemResourcesGTestBase("Tester", MAX_HISTORY_SIZE), component("SystemResources") { + this->initComponents(); + this->connectPorts(); +} + +Tester ::~Tester(void) {} + +// ---------------------------------------------------------------------- +// Tests +// ---------------------------------------------------------------------- + +void Tester ::test_tlm(bool enabled) { + U32 count = 0; + if (Os::SystemResources::getCpuCount(count) == Os::SystemResources::SYSTEM_RESOURCES_OK) { + this->invoke_to_run(0, 0); + // All cascades expected + switch (count) { + case 16: + ASSERT_TLM_CPU_15_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 15: + ASSERT_TLM_CPU_14_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 14: + ASSERT_TLM_CPU_13_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 13: + ASSERT_TLM_CPU_12_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 12: + ASSERT_TLM_CPU_11_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 11: + ASSERT_TLM_CPU_10_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 10: + ASSERT_TLM_CPU_09_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 9: + ASSERT_TLM_CPU_08_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 8: + ASSERT_TLM_CPU_07_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 7: + ASSERT_TLM_CPU_06_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 6: + ASSERT_TLM_CPU_05_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 5: + ASSERT_TLM_CPU_04_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 4: + ASSERT_TLM_CPU_03_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 3: + ASSERT_TLM_CPU_02_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 2: + ASSERT_TLM_CPU_01_SIZE((enabled) ? 1 : 0); + // Cascade expected + case 1: + ASSERT_TLM_CPU_00_SIZE((enabled) ? 1 : 0); + // Cascade expected + default: + ASSERT_TLM_CPU_SIZE((enabled) ? 1 : 0); + + ASSERT_TLM_MEMORY_USED_SIZE((enabled) ? 1 : 0); + ASSERT_TLM_MEMORY_TOTAL_SIZE((enabled) ? 1 : 0); + ASSERT_TLM_NON_VOLATILE_FREE_SIZE((enabled) ? 1 : 0); + ASSERT_TLM_NON_VOLATILE_TOTAL_SIZE((enabled) ? 1 : 0); + ASSERT_TLM_VERSION_SIZE((enabled) ? 1 : 0); + if (enabled) { + ASSERT_TLM_VERSION(0, VERSION); + } + ASSERT_TLM_SIZE((enabled) ? (count + 6) : 0); // CPU count channels + avg + 2 mem + 2 non-volatile + ver + break; + } + } +} + +void Tester ::test_disable_enable() { + this->sendCmd_ENABLE(0, 0, SystemResourcesComponentBase::SystemResourceEnabled::SYS_RES_DISABLED); + this->test_tlm(false); + this->sendCmd_ENABLE(0, 0, SystemResourcesComponentBase::SystemResourceEnabled::SYS_RES_ENABLED); + this->test_tlm(true); +} + +void Tester ::test_version_evr() { + this->sendCmd_VERSION(0, 0); + ASSERT_EVENTS_VERSION_SIZE(1); + ASSERT_EVENTS_VERSION(0, VERSION); +} + +// ---------------------------------------------------------------------- +// Helper methods +// ---------------------------------------------------------------------- + +void Tester ::connectPorts(void) { + // run + this->connect_to_run(0, this->component.get_run_InputPort(0)); + + // CmdDisp + this->connect_to_CmdDisp(0, this->component.get_CmdDisp_InputPort(0)); + + // CmdStatus + this->component.set_CmdStatus_OutputPort(0, this->get_from_CmdStatus(0)); + + // CmdReg + this->component.set_CmdReg_OutputPort(0, this->get_from_CmdReg(0)); + + // Tlm + this->component.set_Tlm_OutputPort(0, this->get_from_Tlm(0)); + + // Time + this->component.set_Time_OutputPort(0, this->get_from_Time(0)); + + // Log + this->component.set_Log_OutputPort(0, this->get_from_Log(0)); + + // LogText + this->component.set_LogText_OutputPort(0, this->get_from_LogText(0)); +} + +void Tester ::initComponents(void) { + this->init(); + this->component.init(INSTANCE); +} + +} // end namespace Svc diff --git a/Svc/SystemResources/test/ut/Tester.hpp b/Svc/SystemResources/test/ut/Tester.hpp new file mode 100644 index 0000000000..96d91f8eaa --- /dev/null +++ b/Svc/SystemResources/test/ut/Tester.hpp @@ -0,0 +1,77 @@ +// ====================================================================== +// \title SystemResources/test/ut/Tester.hpp +// \author sfregoso +// \brief hpp file for SystemResources test harness implementation class +// +// \copyright +// Copyright 2009-2015, by the California Institute of Technology. +// ALL RIGHTS RESERVED. United States Government Sponsorship +// acknowledged. +// +// ====================================================================== + +#ifndef TESTER_HPP +#define TESTER_HPP + +#include "GTestBase.hpp" +#include "Svc/SystemResources/SystemResources.hpp" + +namespace Svc { + +class Tester : public SystemResourcesGTestBase { + // ---------------------------------------------------------------------- + // Construction and destruction + // ---------------------------------------------------------------------- + + public: + //! Construct object Tester + //! + Tester(void); + + //! Destroy object Tester + //! + ~Tester(void); + + public: + // ---------------------------------------------------------------------- + // Tests + // ---------------------------------------------------------------------- + + //! Test the telemetry output + //! + void test_tlm(bool enabled = true); + + //! Test the telemetry enable/disable + //! + void test_disable_enable(); + + //! Test version EVR + //! + void test_version_evr(); + + private: + // ---------------------------------------------------------------------- + // Helper methods + // ---------------------------------------------------------------------- + + //! Connect ports + //! + void connectPorts(void); + + //! Initialize components + //! + void initComponents(void); + + private: + // ---------------------------------------------------------------------- + // Variables + // ---------------------------------------------------------------------- + + //! The component under test + //! + SystemResources component; +}; + +} // end namespace Svc + +#endif diff --git a/cmake/FPrime.cmake b/cmake/FPrime.cmake index ed8dc36dd6..3dfc5b67e3 100644 --- a/cmake/FPrime.cmake +++ b/cmake/FPrime.cmake @@ -49,6 +49,7 @@ endif() include_directories("${CMAKE_BINARY_DIR}") include_directories("${CMAKE_BINARY_DIR}/F-Prime") +register_fprime_target("${CMAKE_CURRENT_LIST_DIR}/target/version.cmake") register_fprime_target("${CMAKE_CURRENT_LIST_DIR}/target/dict.cmake") register_fprime_target("${CMAKE_CURRENT_LIST_DIR}/target/impl.cmake") register_fprime_target("${CMAKE_CURRENT_LIST_DIR}/target/testimpl.cmake") diff --git a/cmake/target/version.cmake b/cmake/target/version.cmake new file mode 100644 index 0000000000..b664106726 --- /dev/null +++ b/cmake/target/version.cmake @@ -0,0 +1,29 @@ + +set (GEN_VERSION_FILE_CMD ${CMAKE_CURRENT_LIST_DIR}/version/generate_version_header.py "${CMAKE_BINARY_DIR}/version.hpp") + +function(add_global_target TARGET_NAME) + + add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/version.hpp" __PHONY__ COMMAND ${CMAKE_COMMAND} -E chdir ${FPRIME_PROJECT_ROOT} ${GEN_VERSION_FILE_CMD}) + add_custom_target(${TARGET_NAME} ALL DEPENDS "${CMAKE_BINARY_DIR}/version.hpp" __PHONY__) + +endfunction(add_global_target) + +#### +# Dict function `add_module_target`: +# +# Adds a module-by-module target for producing dictionaries. These dictionaries take the outputs +# from the autocoder and copies them into the correct directory. These outputs are then handled as +# part of the global `dict` target above. +# +# +# - **MODULE_NAME:** name of the module +# - **TARGET_NAME:** name of target to produce +# - **GLOBAL_TARGET_NAME:** name of produced global target +# - **AC_INPUTS:** list of autocoder inputs +# - **SOURCE_FILES:** list of source file inputs +# - **AC_OUTPUTS:** list of autocoder outputs +# - **MOD_DEPS:** module dependencies of the target +#### +function(add_module_target MODULE_NAME TARGET_NAME GLOBAL_TARGET_NAME AC_INPUTS SOURCE_FILES AC_OUTPUTS MOD_DEPS) + +endfunction(add_module_target) diff --git a/cmake/target/version/generate_version_header.py b/cmake/target/version/generate_version_header.py new file mode 100755 index 0000000000..31d302b4e4 --- /dev/null +++ b/cmake/target/version/generate_version_header.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +""" +# =============================================================================== +# NAME: generate_version_header.py +# +# DESCRIPTION: Creates a version header file of specified name. +# It takes as input a filename and creates a header file +# with a constant string which is the git hash of the current version +# +# USAGE: ./generate_version_header.py /path/tofile/version.hpp +# +# AUTHOR: sfregoso +# EMAIL: sfregoso@jpl.nasa.gov +# DATE CREATED : Oct. 15, 2021 +# +# Copyright 2021, California Institute of Technology. +# ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged. +# =============================================================================== +""" +import sys +import os +import subprocess + + +def get_version_str(): + """ + System call to get the git hash + + Return: String with git hash + """ + output = subprocess.check_output(["git", "describe", "--tags", "--always"]) + return output.strip().decode("ascii") + + +def create_version_file(filename): + """ + Create the version file using the provided name and path. + """ + + # Check if file exists, if it does, overwrite it + if os.path.isfile(filename): + + print("WARNING: File [%s] exists and will be overwritten." % (filename)) + + # Open file for writing + with open(filename, "w") as fid: + + fid.write("/*\n") + fid.write( + " This file has been autogenerated using [%s].\n" + % (os.path.basename(__file__)) + ) + fid.write(" This file may be overwritten.\n") + fid.write("*/\n") + fid.write("#ifndef _VERSION_HPP_\n") + fid.write("#define _VERSION_HPP_\n") + fid.write("\n") + fid.write('static const char* VERSION = "%s";\n' % (get_version_str())) + fid.write("\n") + fid.write("#endif\n") + fid.write("\n\r") + + +if __name__ == "__main__": + + FNAME = sys.argv[1] + + create_version_file(FNAME)