From 30487c600885ff6f08de2699a19d1497eb00fcb6 Mon Sep 17 00:00:00 2001 From: Adam Dobrowolski Date: Fri, 11 Feb 2022 15:14:28 +0100 Subject: [PATCH] [MOS-110] Statistics api for system Gets data from freertos and prints on frequency change depending if it's important. Gathering is not costly, printing is though. For less intrusive checks I would rather disable names gathering as in worst case scenario it hangs rtos context switching till thread id is found. --- .gdbinit-1051 | 4 + .gitmodules | 3 + cmake/modules/ProjectConfig.cmake | 8 ++ .../Bluetooth/interface/profiles/SCO/SCO.cpp | 4 +- module-bsp/bsp/common.cpp | 21 ++++ module-bsp/bsp/common.hpp | 3 + module-os/CMakeLists.txt | 7 ++ module-os/FreeRTOS/include/task.h | 2 + module-os/FreeRTOS/prof.c | 98 +++++++++++++++ module-os/FreeRTOS/prof.h | 57 +++++++++ module-os/FreeRTOS/tasks.c | 112 +++++++++++++++++- module-os/LockGuard.cpp | 9 ++ module-os/board/linux/fsl_runtimestat_gpt.c | 48 +++++++- module-os/board/linux/macros.h | 8 ++ module-os/board/linux/port.c | 5 + module-os/board/rt1051/include/macros.h | 8 ++ module-os/test/CMakeLists.txt | 9 ++ module-os/test/performance-monitor.cpp | 49 ++++++++ module-sys/Service/Service.cpp | 6 + .../Service/include/Service/Service.hpp | 3 + module-sys/SystemManager/CMakeLists.txt | 3 + module-sys/SystemManager/CpuGovernor.cpp | 30 +++-- module-sys/SystemManager/CpuLogPrinter.cpp | 47 ++++++++ module-sys/SystemManager/CpuPackPrinter.cpp | 46 +++++++ module-sys/SystemManager/CpuSentinel.cpp | 21 +++- module-sys/SystemManager/CpuStatistics.cpp | 60 ++++++++-- module-sys/SystemManager/PowerManager.cpp | 33 ++++-- .../SystemManager/SystemManagerCommon.cpp | 51 ++++++-- module-sys/SystemManager/doc/CpuStatistics.md | 65 ++++++++++ .../SystemManager/doc/PowerManagement.md | 3 + .../include/SystemManager/CpuGovernor.hpp | 22 +++- .../include/SystemManager/CpuPrinter.hpp | 44 +++++++ .../include/SystemManager/CpuSentinel.hpp | 5 +- .../include/SystemManager/CpuStatistics.hpp | 24 +++- .../include/SystemManager/PowerManager.hpp | 3 +- .../SystemManager/SysCpuUpdateResult.hpp | 15 +++ .../SystemManager/SystemManagerCommon.hpp | 7 +- .../tests/unittest_CpuSentinelsGovernor.cpp | 18 +-- module-utils/log/api/log/log.hpp | 17 ++- module-utils/log/log.cpp | 10 +- products/PurePhone/CMakeLists.txt | 1 + products/PurePhone/EinkSentinelPure.cpp | 4 +- products/PurePhone/PurePhoneMain.cpp | 3 + products/PurePhone/init_prof.cpp | 26 ++++ products/PurePhone/init_prof.hpp | 9 ++ third-party/CMakeLists.txt | 2 + third-party/msgpack11/CMakeLists.txt | 6 + third-party/msgpack11/msgpack11 | 1 + 48 files changed, 973 insertions(+), 67 deletions(-) create mode 100644 module-os/FreeRTOS/prof.c create mode 100644 module-os/FreeRTOS/prof.h create mode 100644 module-os/test/CMakeLists.txt create mode 100644 module-os/test/performance-monitor.cpp create mode 100644 module-sys/SystemManager/CpuLogPrinter.cpp create mode 100644 module-sys/SystemManager/CpuPackPrinter.cpp create mode 100644 module-sys/SystemManager/doc/CpuStatistics.md create mode 100644 module-sys/SystemManager/include/SystemManager/CpuPrinter.hpp create mode 100644 module-sys/SystemManager/include/SystemManager/SysCpuUpdateResult.hpp create mode 100644 products/PurePhone/init_prof.cpp create mode 100644 products/PurePhone/init_prof.hpp create mode 100644 third-party/msgpack11/CMakeLists.txt create mode 160000 third-party/msgpack11/msgpack11 diff --git a/.gdbinit-1051 b/.gdbinit-1051 index 1271dc7a76d4ef243657de343cda539755890f79..8c9cce775e50e48b0ad210a3dd29857449d6da10 100644 --- a/.gdbinit-1051 +++ b/.gdbinit-1051 @@ -15,3 +15,7 @@ thread 2 tb main b HardFault_Handler b _exit +b abort +b WDOG1_IRQHandler +b RTWDOG_IRQHandler +b IntDefaultHandler diff --git a/.gitmodules b/.gitmodules index 2f45c8b1363ba009b7fa8bdbbfbc8733933489ec..c32ea71f4df5077896ab6da86e1f4148b07f21f4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -106,3 +106,6 @@ path = third-party/reedgefs/src url = ../reliance-edge.git branch = mudita +[submodule "third-party/msgpack11/msgpack11"] + path = third-party/msgpack11/msgpack11 + url = https://github.com/ar90n/msgpack11.git diff --git a/cmake/modules/ProjectConfig.cmake b/cmake/modules/ProjectConfig.cmake index 09ef8e3e7c02b203c6b31dceb6929dd293f26934..641b357a9831b3a2e0aa98094a9a54fb02b2b31b 100644 --- a/cmake/modules/ProjectConfig.cmake +++ b/cmake/modules/ProjectConfig.cmake @@ -24,6 +24,13 @@ else() set (LOG_REDIRECT "RTT_JLINK" CACHE INTERNAL "") endif() +option(SYSTEM_PROFILE "SYSTEM_PROFILE" OFF) +if(${SYSTEM_PROFILE} STREQUAL "ON") + set(PROF_ON 1 CACHE INTERNAL "") +else() + set(PROF_ON 0 CACHE INTERNAL "") +endif() + # add CurrentMeasurement enable option option(CURRENT_MEASUREMENT "CURRENT_MEASUREMENT" OFF) @@ -72,6 +79,7 @@ set(PROJECT_CONFIG_DEFINITIONS USBCDC_ECHO_ENABLED=${USBCDC_ECHO_ENABLED} LOG_LUART_ENABLED=${LOG_LUART_ENABLED} MAGIC_ENUM_RANGE_MAX=256 + PROF_ON=${PROF_ON} CACHE INTERNAL "" ) diff --git a/module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp b/module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp index 45c13fd76b948f8fefce66102ea0fd7a6347c265..4c8815fd14ede249f9b2e653b2003fbb71e291aa 100644 --- a/module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp +++ b/module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "SCO.hpp" @@ -153,7 +153,7 @@ void SCO::SCOImpl::writeToHostEndian(int16_t *buffer, uint8_t *packet, int lengt void SCO::SCOImpl::receiveCvsd(uint8_t *packet, uint16_t size) { - std::array audioFrameOut; + std::array audioFrameOut{}; if (size > audioFrameOut.size()) { LOG_WARN("SCO packet larger than local output buffer - dropping data."); diff --git a/module-bsp/bsp/common.cpp b/module-bsp/bsp/common.cpp index 9351fa6e2ad48a75d83ac82baecd7417f3150345..f4ff516f29b4560cc0c94f4e0a47e23217db9258 100644 --- a/module-bsp/bsp/common.cpp +++ b/module-bsp/bsp/common.cpp @@ -16,5 +16,26 @@ namespace bsp{ break; } } + + uint8_t CpuMHZToLevel(enum CpuFrequencyMHz val) + { + switch (val) { + case CpuFrequencyMHz::Level_0: + return 0; + case CpuFrequencyMHz::Level_1: + return 1; + case CpuFrequencyMHz::Level_2: + return 2; + case CpuFrequencyMHz::Level_3: + return 3; + case CpuFrequencyMHz::Level_4: + return 4; + case CpuFrequencyMHz::Level_5: + return 5; + case CpuFrequencyMHz::Level_6: + return 6; + } + return -1; + } }; diff --git a/module-bsp/bsp/common.hpp b/module-bsp/bsp/common.hpp index 267f2a2517f80ec257e192d44f41ffa33dea151d..d69116359e0d9f08af62ca5dd4a134ef17bb5424 100644 --- a/module-bsp/bsp/common.hpp +++ b/module-bsp/bsp/common.hpp @@ -3,6 +3,7 @@ #pragma once +#include namespace bsp { enum class RetCode{ @@ -24,6 +25,8 @@ namespace bsp Level_6 = 528 }; + uint8_t CpuMHZToLevel(enum CpuFrequencyMHz val); + constexpr auto MHz_frequency_multiplier = 1000000U; enum class Board{ diff --git a/module-os/CMakeLists.txt b/module-os/CMakeLists.txt index 03f2794e70096ab3d5fef187b4ec06e777133f43..d17af3d8f9b4028f63a59b9314dc8ed3fc1347a6 100644 --- a/module-os/CMakeLists.txt +++ b/module-os/CMakeLists.txt @@ -34,6 +34,9 @@ target_sources(module-os PRIVATE ${SOURCES}) if(NOT ${SYSTEM_VIEW_ENABLED}) target_sources(module-os PRIVATE FreeRTOS/tasks.c) endif() +if(${PROF_ON}) + target_sources(module-os PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/FreeRTOS/prof.c) +endif() add_board_subdirectory(board) @@ -80,3 +83,7 @@ if((${PROJECT_TARGET} STREQUAL "TARGET_RT1051") AND (${SYSTEM_VIEW_ENABLED})) endif() target_link_libraries(${PROJECT_NAME} PUBLIC log-api board-config) + +if (${ENABLE_TESTS}) + add_subdirectory(test) +endif () diff --git a/module-os/FreeRTOS/include/task.h b/module-os/FreeRTOS/include/task.h index 8c8653a458a58585319fd20869e4ab9e25a753f6..5c5b001a5cc9f25705458cdc69fc5d2a56878745 100644 --- a/module-os/FreeRTOS/include/task.h +++ b/module-os/FreeRTOS/include/task.h @@ -1408,6 +1408,7 @@ char *pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e9 * \ingroup TaskUtils */ TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +TaskHandle_t xTaskGetByTCBNumber(UBaseType_t uxTCBNumber ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ /** * task.h @@ -2366,6 +2367,7 @@ void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBas * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. */ UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; +UBaseType_t uxTaskGetTCBNumber(TaskHandle_t xTask) PRIVILEGED_FUNCTION; /* * Set the uxTaskNumber of the task referenced by the xTask parameter to diff --git a/module-os/FreeRTOS/prof.c b/module-os/FreeRTOS/prof.c new file mode 100644 index 0000000000000000000000000000000000000000..8541a9e057430e3f97ad39cfdd822a7185c803a6 --- /dev/null +++ b/module-os/FreeRTOS/prof.c @@ -0,0 +1,98 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "prof.h" +#include +#include + +struct prof_pool +{ + struct task_prof_data *pool; + unsigned int _overflow_counter; + unsigned int _pos; + + struct prof_pool_init_data data; + void (*clean)(); + void (*handle_overflow)(uint32_t id); + struct task_prof_data* (*get)(uint32_t id); +}; + + +static struct prof_pool pool; + +static void _pool_clean() +{ + memset(pool.pool, 0, sizeof(*pool.pool)*pool.data.size); + pool._overflow_counter =0; + pool._pos = 0; +} + +static void _pool_overflow(uint32_t id) +{ + pool._overflow_counter = id; +} + +/// just meant to be fast get of element +static struct task_prof_data* _pool_get(uint32_t id) +{ + if ( pool._pos == pool.data.size ) { + pool.handle_overflow(id); + } + for ( size_t i =0; i < pool.data.size && i != pool._pos; ++i ) { + if (id == pool.pool[i].task_TCB_id) { + return &pool.pool[i]; + } + } + struct task_prof_data* p = &pool.pool[pool._pos]; + pool._pos++; + return p; +} + + + +void prof_pool_init(struct prof_pool_init_data init) +{ + pool.data = init; + pool.clean = _pool_clean; + pool.handle_overflow = _pool_overflow; + pool.get = _pool_get; + + pool.pool = (struct task_prof_data *)(malloc(sizeof(struct task_prof_data)*pool.data.size)); + pool.clean(); +} + +void prof_pool_deinit() +{ + free(pool.pool); + pool.pool = NULL; +} + + +struct prof_pool_init_data prof_pool_get_data() +{ + return pool.data; +} + +void prof_pool_data_set(uint8_t ts, uint32_t id) +{ + struct task_prof_data *what = pool.get(id); + if ( what == NULL) { + return; + } + what->task_TCB_id=id; + what->exec_time += ts; + ++(what->switches); +} + +unsigned int prof_pool_overflow() +{ + return pool._overflow_counter; +} + +unsigned int prof_pool_flush(struct task_prof_data *mem, size_t cap) +{ + unsigned int to_ret = pool._overflow_counter; + memcpy(mem, pool.pool, cap * (sizeof(struct task_prof_data))); + pool.clean(); + return to_ret; +} diff --git a/module-os/FreeRTOS/prof.h b/module-os/FreeRTOS/prof.h new file mode 100644 index 0000000000000000000000000000000000000000..a9d4f71897e36e61f8a0eaf98d0d141a0964a07c --- /dev/null +++ b/module-os/FreeRTOS/prof.h @@ -0,0 +1,57 @@ +// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/// just store what's interesting +/// 1. do not store TaskHandle_t, id is better - we normally have up to 32 id's as we do not +/// as we tend to keep up to ~30 threads alive +/// 2. execution time, the better granulation, the better result +struct task_prof_data { + uint32_t task_TCB_id; /// task TCB id + uint32_t exec_time; /// single task switch execution time summed up in TS + uint32_t switches; /// count how many times it was switched out +}; + +/// initialization structure +struct prof_pool_init_data +{ + size_t size; /// size of the pool, use should have linear eficiency +}; + +#if PROF_ON + +/// initialization of pool to store switch data +void prof_pool_init(struct prof_pool_init_data init); +void prof_pool_deinit(); +struct prof_pool_init_data prof_pool_get_data(); + +/// get next available slot from the pool +/// struct task_prof_data* prof_pool_get_next(); +/// set the element +void prof_pool_data_set(uint8_t ts, uint32_t id); + +/// mark if overflow happened +unsigned int prof_pool_overflow(); + +/// to `mem` flush up to `cap` data - then clean +/// if passed: +/// - set used count: how much data was used +/// - returns overflow count +/// requires sched lock before running +unsigned int prof_pool_flush(struct task_prof_data *mem, size_t cap); + +#else +#define prof_pool_data_set(...) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/module-os/FreeRTOS/tasks.c b/module-os/FreeRTOS/tasks.c index 2c091ca0553c841e5422c2c2307259fd6a36be96..a73fa4fb4bfe98faf32a4a630ab04978875c16b0 100644 --- a/module-os/FreeRTOS/tasks.c +++ b/module-os/FreeRTOS/tasks.c @@ -26,6 +26,7 @@ */ /* Standard includes. */ +#include "prof.h" #include #include @@ -2353,8 +2354,6 @@ TCB_t *pxTCB; TCB_t *pxNextTCB, *pxFirstTCB, *pxReturn = NULL; UBaseType_t x; char cNextChar; - BaseType_t xBreakLoop; - /* This function is called with the scheduler suspended. */ if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) @@ -2412,6 +2411,32 @@ TCB_t *pxTCB; } #endif /* INCLUDE_xTaskGetHandle */ + + static TCB_t *prvSearchForTCBNumberWithinSingleList( List_t *pxList, UBaseType_t TCBNumber) + { + TCB_t *pxNextTCB, *pxFirstTCB, *pxReturn = NULL; + /* This function is called with the scheduler suspended. */ + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + if (pxNextTCB->uxTCBNumber == TCBNumber) { + pxReturn = pxNextTCB; + break; + } + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return pxReturn; + } + + /*-----------------------------------------------------------*/ #if ( INCLUDE_xTaskGetHandle == 1 ) @@ -2475,8 +2500,64 @@ TCB_t *pxTCB; return pxTCB; } - #endif /* INCLUDE_xTaskGetHandle */ + + TaskHandle_t xTaskGetByTCBNumber(UBaseType_t TCBNumber ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t uxQueue = configMAX_PRIORITIES; + TCB_t* pxTCB; + + vTaskSuspendAll(); + { + /* Search the ready lists. */ + do + { + uxQueue--; + pxTCB = prvSearchForTCBNumberWithinSingleList( ( List_t * ) &( pxReadyTasksLists[ uxQueue ] ), TCBNumber); + if( pxTCB != NULL ) + { + /* Found the handle. */ + break; + } + + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Search the delayed lists. */ + if( pxTCB == NULL ) + { + pxTCB = prvSearchForTCBNumberWithinSingleList( ( List_t * ) pxDelayedTaskList, TCBNumber ); + } + + if( pxTCB == NULL ) + { + pxTCB = prvSearchForTCBNumberWithinSingleList( ( List_t * ) pxOverflowDelayedTaskList, TCBNumber ); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the suspended list. */ + pxTCB = prvSearchForTCBNumberWithinSingleList( &xSuspendedTaskList, TCBNumber ); + } + } + #endif + + #if( INCLUDE_vTaskDelete == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the deleted list. */ + pxTCB = prvSearchForTCBNumberWithinSingleList( &xTasksWaitingTermination, TCBNumber ); + } + } + #endif + } + ( void ) xTaskResumeAll(); + + return pxTCB; + } + /*-----------------------------------------------------------*/ #if ( configUSE_TRACE_FACILITY == 1 ) @@ -2978,6 +3059,7 @@ void vTaskSwitchContext( void ) if( ulTotalRunTime > ulTaskSwitchedInTime ) { pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); + prof_pool_data_set(ulTotalRunTime - ulTaskSwitchedInTime, pxCurrentTCB->uxTCBNumber); } else { @@ -3305,6 +3387,25 @@ void vTaskMissedYield( void ) return uxReturn; } + UBaseType_t uxTaskGetTCBNumber( TaskHandle_t xTask ) + { + UBaseType_t uxReturn; + TCB_t const *pxTCB; + + if( xTask != NULL ) + { + pxTCB = xTask; + uxReturn = pxTCB->uxTCBNumber; + } + else + { + uxReturn = 0U; + } + + return uxReturn; + } + + #endif /* configUSE_TRACE_FACILITY */ /*-----------------------------------------------------------*/ @@ -5215,3 +5316,8 @@ when performing module tests). */ #endif + +int isOSRunning() +{ + return pdTRUE == xSchedulerRunning; +} diff --git a/module-os/LockGuard.cpp b/module-os/LockGuard.cpp index 6aaa55802ffb8c8d7d074f1106e8e924c4950e3f..8ca3722ff19fdec8ead3c0477d6a65b8fcfd5351 100644 --- a/module-os/LockGuard.cpp +++ b/module-os/LockGuard.cpp @@ -8,6 +8,11 @@ LockGuard::LockGuard(cpp_freertos::MutexStandard& mutex) : mutex(mutex) { + + if (isOSRunning() == 0) + { + return; + } if (isIRQ()) { savedInterruptStatus = cpp_freertos::CriticalSection::EnterFromISR(); } @@ -18,6 +23,10 @@ LockGuard::LockGuard(cpp_freertos::MutexStandard& mutex) : mutex(mutex) LockGuard::~LockGuard() { + if (isOSRunning() == 0) + { + return; + } if (isIRQ()) { cpp_freertos::CriticalSection::ExitFromISR(savedInterruptStatus); } diff --git a/module-os/board/linux/fsl_runtimestat_gpt.c b/module-os/board/linux/fsl_runtimestat_gpt.c index 1d854cac920b25d18587e8d6541aba77505020a7..05ae94ab62187b4f495979abeb34baff7bc8107c 100644 --- a/module-os/board/linux/fsl_runtimestat_gpt.c +++ b/module-os/board/linux/fsl_runtimestat_gpt.c @@ -2,12 +2,58 @@ // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint32_t prof_tick = 0; + +static void TickSignalHandler( int sig ) +{ + ++prof_tick; +} void vConfigureTimerForRunTimeStats(void) { + struct sigaction tick; + sigemptyset(&tick.sa_mask); + tick.sa_flags = 0; + tick.sa_handler = TickSignalHandler; + + if (sigaction( SIGPROF, &tick, NULL )!=0) + //if (sigaction( SIGVTALRM, &tick, NULL )!=0) + { + printf("cant profile\n"); + exit(1); + } + + struct itimerval itimer; + if( getitimer(ITIMER_PROF , &itimer) != 0 ){ + printf("cant get profile\n"); + exit(1); + } + /// timer set to 1kHz, not 1MHz like timer in the other gpt + /// freq is the same as freq of rtos + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = 1000; + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = 1000; + + /* Set-up the timer interrupt. */ + if (setitimer( ITIMER_PROF, &itimer, NULL) != 0 ) + { + printf("cant set profile\n"); + exit(1); + } } uint32_t ulHighFrequencyTimerTicks(void) { - return 0; + return prof_tick; } diff --git a/module-os/board/linux/macros.h b/module-os/board/linux/macros.h index d3e688d8f7d4b2ca4ad9f0c26eb0959dbb48d918..c0ed20f401dc35fd835277d8759fdafd9116bb83 100644 --- a/module-os/board/linux/macros.h +++ b/module-os/board/linux/macros.h @@ -73,4 +73,12 @@ static inline bool isIRQ() } +#ifdef __cplusplus +extern "C" { +#endif + int isOSRunning(); +#ifdef __cplusplus +}; +#endif + #endif /* MACROS_H_ */ diff --git a/module-os/board/linux/port.c b/module-os/board/linux/port.c index ec2177e3db991c26ddbc8ab208ded66e2c635e60..57b47b8287e57bdc45d1771b9ae83352adc2be52 100644 --- a/module-os/board/linux/port.c +++ b/module-os/board/linux/port.c @@ -921,3 +921,8 @@ BaseType_t xPortIsInsideInterrupt(void) { return pdFALSE; } + +uint32_t CLOCK_GetFreq(int clock) { + (void)clock; + return 0; +} diff --git a/module-os/board/rt1051/include/macros.h b/module-os/board/rt1051/include/macros.h index 2c08ad12d03411dbd4ead89b963bad6a2fd6819f..1de34ba5da54f5f2f063f8d66fb1516128471039 100644 --- a/module-os/board/rt1051/include/macros.h +++ b/module-os/board/rt1051/include/macros.h @@ -71,3 +71,11 @@ static inline void haltIfDebugging() __asm("bkpt 1"); } } + +#ifdef __cplusplus +extern "C" { +#endif + int isOSRunning(); +#ifdef __cplusplus +}; +#endif diff --git a/module-os/test/CMakeLists.txt b/module-os/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d9375e2ceda63f21aa2324e6d68d6a4549c2797b --- /dev/null +++ b/module-os/test/CMakeLists.txt @@ -0,0 +1,9 @@ +if(${PROF_ON}) +add_catch2_executable( + NAME performance + SRCS + performance-monitor.cpp + LIBS + module-os +) +endif() diff --git a/module-os/test/performance-monitor.cpp b/module-os/test/performance-monitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32c1d7608b90688b8218d44771897257de20408b --- /dev/null +++ b/module-os/test/performance-monitor.cpp @@ -0,0 +1,49 @@ +#include +#define CATCH_CONFIG_MAIN + +#include +#include "prof.h" + +TEST_CASE("prof api test") +{ + struct prof_pool_init_data init{0}; + + prof_pool_init(init); + auto pp = prof_pool_get_data(); + REQUIRE(pp.size == 0); + prof_pool_deinit(); +} + +TEST_CASE("overflow") +{ + struct prof_pool_init_data init + { + 0 + }; + prof_pool_init(init); + prof_pool_data_set(0,-1); + REQUIRE(prof_pool_overflow() == 1); + prof_pool_deinit(); +} + +TEST_CASE("prof api sum") +{ + struct prof_pool_init_data init{1}; + + prof_pool_init(init); + auto pp = prof_pool_get_data(); + REQUIRE(pp.size == 1); + const auto switches = 10; + const auto ts = 10; + for (auto i =0; i < switches ; ++i) + { + prof_pool_data_set(0,ts); + } + + task_prof_data mem[1]; + prof_pool_flush(mem, 1); + REQUIRE(mem->switches == switches); + REQUIRE(mem->exec_time == switches*ts); + + prof_pool_deinit(); +} diff --git a/module-sys/Service/Service.cpp b/module-sys/Service/Service.cpp index 98d167a0bf7c4f5e986e5e97968b535b30c642f4..12831d3fa8711a622061a243270da0e19796f9d0 100644 --- a/module-sys/Service/Service.cpp +++ b/module-sys/Service/Service.cpp @@ -112,6 +112,7 @@ namespace sys staleUniqueMsg.end()); const bool respond = msg->type != Message::Type::Response && GetName() != msg->sender; + currentlyProcessing = msg; auto response = msg->Execute(this); if (response == nullptr || !respond) { continue; @@ -268,6 +269,11 @@ namespace sys service->bus.sendUnicast(std::move(msg), service::name::system_manager); } + std::string Service::getCurrentProcessing() + { + return currentlyProcessing ? std::string(typeid(*currentlyProcessing).name()) : "nothing in progress"; + } + auto Proxy::handleMessage(Service *service, Message *message, ResponseMessage *response) -> MessagePointer { if (service->isReady) { diff --git a/module-sys/Service/include/Service/Service.hpp b/module-sys/Service/include/Service/Service.hpp index 3823791685f83c6a901957468f5062d35442f173..5126bc302a19923eadb6f07a7550d751d1d388c7 100644 --- a/module-sys/Service/include/Service/Service.hpp +++ b/module-sys/Service/include/Service/Service.hpp @@ -100,6 +100,7 @@ namespace sys bool disconnect(const std::type_info &type); void sendCloseReadyMessage(Service *service); + std::string getCurrentProcessing(); protected: bool enableRunLoop; @@ -138,6 +139,8 @@ namespace sys [[nodiscard]] auto get(timer::SystemTimer *timer) noexcept -> timer::SystemTimer *; } timers; + MessagePointer currentlyProcessing = nullptr; + public: auto getTimers() -> auto & { diff --git a/module-sys/SystemManager/CMakeLists.txt b/module-sys/SystemManager/CMakeLists.txt index 99f417662574082e45fcc90025e492502a332502..4c7a5b59b94a34546f962fceb61a6e03cc5c29c1 100644 --- a/module-sys/SystemManager/CMakeLists.txt +++ b/module-sys/SystemManager/CMakeLists.txt @@ -14,6 +14,8 @@ target_sources(sys-manager CpuGovernor.cpp CpuSentinel.cpp CpuStatistics.cpp + CpuLogPrinter.cpp + CpuPackPrinter.cpp data/SystemManagerActionsParams.hpp DependencyGraph.cpp DeviceManager.cpp @@ -37,6 +39,7 @@ target_link_libraries(sys-manager sys-common PRIVATE service-desktop + msgpack11 ) if (${ENABLE_TESTS}) diff --git a/module-sys/SystemManager/CpuGovernor.cpp b/module-sys/SystemManager/CpuGovernor.cpp index f842bc940c72800b87142756aedc1ab988a4252d..b2911dd7c4466a4961c47012c45d3751f3c3cb5f 100644 --- a/module-sys/SystemManager/CpuGovernor.cpp +++ b/module-sys/SystemManager/CpuGovernor.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include @@ -113,19 +113,33 @@ namespace sys SetCpuFrequencyRequest(sentinelName, bsp::CpuFrequencyMHz::Level_0); } - [[nodiscard]] auto CpuGovernor::GetMinimumFrequencyRequested() const noexcept -> bsp::CpuFrequencyMHz + [[nodiscard]] auto CpuGovernor::GetMinimumFrequencyRequested() const noexcept -> sentinel::Data { - bsp::CpuFrequencyMHz minFrequency = bsp::CpuFrequencyMHz::Level_0; + sentinel::Data d; + if (sentinels.empty()) { + d.reason = "empty"; + return d; + } - for (auto &sentinel : sentinels) { - const auto sentinelFrequency = sentinel->GetRequestedFrequency(); + auto minSentinel = sentinels.begin(); + for (auto iter = sentinels.begin(); iter != std::end(sentinels); ++iter) { + const auto sentinelFrequency = (*iter)->GetRequestedFrequency(); - if (sentinelFrequency > minFrequency) { - minFrequency = sentinelFrequency; + if (sentinelFrequency > (*minSentinel)->GetRequestedFrequency()) { + minSentinel = iter; } } - return minFrequency; + d.frequency = (*minSentinel)->GetRequestedFrequency(); + if (auto p = (*minSentinel)->GetSentinel().lock()) { + d.name = p->GetName(); + d.task = p->getTask(); + d.reason = p->getReason(); + } + else { + d.reason = "cant lock"; + } + return d; } void CpuGovernor::InformSentinelsAboutCpuFrequencyChange(bsp::CpuFrequencyMHz newFrequency) const noexcept diff --git a/module-sys/SystemManager/CpuLogPrinter.cpp b/module-sys/SystemManager/CpuLogPrinter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df40d5a5b3e27e712c7477dfb934e805e129081f --- /dev/null +++ b/module-sys/SystemManager/CpuLogPrinter.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "SystemManager/CpuPrinter.hpp" +#include "SystemManager/SysCpuUpdateResult.hpp" +#include + +extern "C" +{ + uint32_t CLOCK_GetFreq(int); +} + +namespace sys::cpu::stats +{ + + void LogPrinter::printSysUsage(struct task_prof_data *data, size_t size) + { + vTaskSuspendAll(); + { + for (size_t i = 0; i < size; ++i) { + if (data[i].exec_time == 0 && data[i].switches == 0) { + continue; + } + + LOG_PRINTF("%s,%" PRIu32 ",%" PRIu32 ",%" PRIu32 "\n", + SystemManagerCommon::ServiceProcessor(i).c_str(), + data[i].task_TCB_id, + data[i].exec_time, + data[i].switches); + /// NOTE: version below is much lighter and doesn't need system suspend, it requires GDB to show what + /// were the + // task names LOG_PRINTF("%d,%" PRIu32 "\n", data[i].task_TCB_id, data[i].exec_time); + } + } + xTaskResumeAll(); + } + + void LogPrinter::printCPUChange(const cpu::UpdateResult &ret) + { + LOG_PRINTF("CPU freq changed to: %d by: %s reason: %s for freq: %d curent: %" PRIu32 "\n", + int(ret.frequencySet), + ret.data.name.c_str(), + ret.data.reason.c_str(), + int(ret.data.frequency), + CLOCK_GetFreq(0)); + } +} // namespace sys::cpu::stats diff --git a/module-sys/SystemManager/CpuPackPrinter.cpp b/module-sys/SystemManager/CpuPackPrinter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..27456989524b0e32ad31cbff6aafd6f2697ede7c --- /dev/null +++ b/module-sys/SystemManager/CpuPackPrinter.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "SystemManager/CpuPrinter.hpp" +#include "SystemManager/SysCpuUpdateResult.hpp" +#include +#include "third-party/msgpack11/msgpack11/msgpack11.hpp" + +namespace sys::cpu::stats +{ + + using namespace msgpack11; + enum class PackID + { + Proc, + Usage, + }; + + void PackPrinter::printSysUsage(struct task_prof_data *data, size_t size) + { + vTaskSuspendAll(); + { + for (size_t i = 0; i < size; ++i) { + if (data[i].exec_time == 0 && data[i].switches == 0) { + continue; + } + MsgPack obj = MsgPack::object{{"id", uint32_t(PackID::Proc)}, + {"name", SystemManagerCommon::ServiceProcessor(i)}, + {"tcb", uint32_t(data[i].task_TCB_id)}, + {"t", data[i].exec_time}}; + LOG_PRINTF("%c%s\n", 2, obj.dump().c_str()); + } + } + xTaskResumeAll(); + } + + void PackPrinter::printCPUChange(const cpu::UpdateResult &ret) + { + MsgPack obj = MsgPack::object{{"id", uint32_t(PackID::Usage)}, + {"freq", uint32_t(ret.frequencySet)}, + {"name", ret.data.name}, + {"reason", ret.data.reason}, + {"requested", uint32_t(ret.data.frequency)}}; + LOG_PRINTF("%c%s\n", 2, obj.dump().c_str()); + } +} // namespace sys::cpu::stats diff --git a/module-sys/SystemManager/CpuSentinel.cpp b/module-sys/SystemManager/CpuSentinel.cpp index 0a971854570f8d49deed5ef4dedae0f8e2bc3d66..df74944956c4771d15c8827c6512f9c35654b56f 100644 --- a/module-sys/SystemManager/CpuSentinel.cpp +++ b/module-sys/SystemManager/CpuSentinel.cpp @@ -31,6 +31,8 @@ namespace sys auto msg = std::make_shared(GetName(), frequencyToHold); owner->bus.sendUnicast(std::move(msg), service::name::system_manager); currentFrequencyToHold = frequencyToHold; + currentReason = std::string("up: ") + owner->getCurrentProcessing() + std::string(" req: ") + + std::to_string(int(frequencyToHold)); } } @@ -40,6 +42,7 @@ namespace sys auto msg = std::make_shared(GetName()); owner->bus.sendUnicast(std::move(msg), service::name::system_manager); currentFrequencyToHold = bsp::CpuFrequencyMHz::Level_0; + currentReason = std::string("down: ") + owner->getCurrentProcessing(); } } @@ -82,9 +85,9 @@ namespace sys if (callback) { callback(newFrequency); } - if (taskHandle != nullptr && newFrequency >= currentFrequencyToHold) { - xTaskNotifyGive(taskHandle); - taskHandle = nullptr; + if (taskWaitingForFrequency != nullptr && newFrequency >= currentFrequencyToHold) { + xTaskNotifyGive(taskWaitingForFrequency); + taskWaitingForFrequency = nullptr; } } @@ -92,10 +95,11 @@ namespace sys TaskHandle_t taskToNotify, uint32_t timeout) { + currentReason = std::string("h+w: ") + owner->getCurrentProcessing(); HoldMinimumFrequency(frequencyToHold); if (currentFrequencyToHold < frequencyToHold) { - taskHandle = taskToNotify; + taskWaitingForFrequency = taskToNotify; return ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(timeout)) == 0; } @@ -135,4 +139,13 @@ namespace sys } } + TaskHandle_t CpuSentinel::getTask() + { + return owner->GetHandle(); + } + + std::string CpuSentinel::getReason() + { + return currentReason; + } } // namespace sys diff --git a/module-sys/SystemManager/CpuStatistics.cpp b/module-sys/SystemManager/CpuStatistics.cpp index 1d2795a280919bdcfcafae22dc3b199c8fd7a88d..bdcc90a156bffd3adad65a3d3b80534fc65a628e 100644 --- a/module-sys/SystemManager/CpuStatistics.cpp +++ b/module-sys/SystemManager/CpuStatistics.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include @@ -7,25 +7,69 @@ #include #include +extern "C" +{ +#include "prof.h" +} + namespace sys { - void CpuStatistics::Update() + + CpuStatistics::CpuStatistics() + { + +#if PROF_ON + data_size = prof_pool_get_data().size; + data = new task_prof_data[data_size]; +#endif + printer = std::make_unique(); + } + + CpuStatistics::~CpuStatistics() + { +#if PROF_ON + delete[] data; +#endif + } + + void CpuStatistics::StoreSysUsage() + { +#if PROF_ON + vTaskSuspendAll(); + { + if (auto ovf = prof_pool_flush(data, data_size); ovf != 0) { + LOG_FATAL("prof pool flush overflow: %d", int(ovf)); + } + } + xTaskResumeAll(); +#endif + } + + void CpuStatistics::TrackChange(const cpu::UpdateResult &ret) + { + if (ret.changed) { + printer->printCPUChange(ret); +#if PROF_ON + printer->printSysUsage(data, data_size); +#endif + } + StoreSysUsage(); + } + + void CpuStatistics::UpdatePercentageCpuLoad() { uint32_t idleTickCount = xTaskGetIdleRunTimeCounter(); uint32_t totalTickCount = ulHighFrequencyTimerTicks(); - uint32_t idleTickIncrease = ComputeIncrease(idleTickCount, lastIdleTickCount); uint32_t totalTickIncrease = ComputeIncrease(totalTickCount, lastTotalTickCount); - - if (totalTickIncrease) { + lastIdleTickCount = idleTickCount; + lastTotalTickCount = totalTickCount; + if (totalTickIncrease != 0u) { cpuLoad = 100 - ((idleTickIncrease * 100) / totalTickIncrease); } else { cpuLoad = 0; } - - lastIdleTickCount = idleTickCount; - lastTotalTickCount = totalTickCount; } uint32_t CpuStatistics::GetPercentageCpuLoad() const noexcept diff --git a/module-sys/SystemManager/PowerManager.cpp b/module-sys/SystemManager/PowerManager.cpp index d08362ad647f56a3f338c7ffe07c4c7708220c96..488f5b28c598a0be4d76ae21751e75273676ccb3 100644 --- a/module-sys/SystemManager/PowerManager.cpp +++ b/module-sys/SystemManager/PowerManager.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md +#include #include #include @@ -76,14 +77,27 @@ namespace sys } } - void PowerManager::UpdateCpuFrequency(uint32_t cpuLoad) + [[nodiscard]] cpu::UpdateResult PowerManager::UpdateCpuFrequency(uint32_t cpuLoad) { + cpu::UpdateResult result; + const auto currentCpuFreq = lowPowerControl->GetCurrentFrequencyLevel(); - const auto minFrequencyRequested = cpuGovernor->GetMinimumFrequencyRequested(); - const auto permanentFrequencyToHold = cpuGovernor->GetPermanentFrequencyRequested(); + const auto min = cpuGovernor->GetMinimumFrequencyRequested(); + const auto permanent = cpuGovernor->GetPermanentFrequencyRequested(); + + auto _ = gsl::finally([&result, this, ¤tCpuFreq, min, permanent] { + result.frequencySet = lowPowerControl->GetCurrentFrequencyLevel(); + result.changed = result.frequencySet != currentCpuFreq; + if (not permanent.isActive) { + result.data = min; + } + else { + result.data.reason = "perm"; + } + }); - if (permanentFrequencyToHold.isActive) { - auto frequencyToHold = std::max(permanentFrequencyToHold.frequencyToHold, powerProfile.minimalFrequency); + if (permanent.isActive) { + auto frequencyToHold = std::max(permanent.frequencyToHold, powerProfile.minimalFrequency); if (currentCpuFreq < frequencyToHold) { IncreaseCpuFrequency(frequencyToHold); @@ -94,7 +108,7 @@ namespace sys } while (lowPowerControl->GetCurrentFrequencyLevel() > frequencyToHold); } ResetFrequencyShiftCounter(); - return; + return result; } if (cpuLoad > powerProfile.frequencyShiftUpperThreshold && currentCpuFreq < bsp::CpuFrequencyMHz::Level_6) { @@ -114,9 +128,9 @@ namespace sys isFrequencyLoweringInProgress = false; } - if (minFrequencyRequested > currentCpuFreq) { + if (min.frequency > currentCpuFreq) { ResetFrequencyShiftCounter(); - IncreaseCpuFrequency(minFrequencyRequested); + IncreaseCpuFrequency(min.frequency); } else if (aboveThresholdCounter >= powerProfile.maxAboveThresholdCount) { if (powerProfile.frequencyIncreaseIntermediateStep && currentCpuFreq < bsp::CpuFrequencyMHz::Level_4) { @@ -131,11 +145,12 @@ namespace sys else { if (belowThresholdCounter >= (isFrequencyLoweringInProgress ? powerProfile.maxBelowThresholdInRowCount : powerProfile.maxBelowThresholdCount) && - currentCpuFreq > minFrequencyRequested) { + currentCpuFreq > min.frequency) { ResetFrequencyShiftCounter(); DecreaseCpuFrequency(); } } + return result; } void PowerManager::IncreaseCpuFrequency(bsp::CpuFrequencyMHz newFrequency) diff --git a/module-sys/SystemManager/SystemManagerCommon.cpp b/module-sys/SystemManager/SystemManagerCommon.cpp index d6e691861de14bd072e3de07732142b927d23608..cca78014d5e3aaa96e69be6cc4d961c72031e5dd 100644 --- a/module-sys/SystemManager/SystemManagerCommon.cpp +++ b/module-sys/SystemManager/SystemManagerCommon.cpp @@ -256,12 +256,12 @@ namespace sys // Start System manager StartService(); - cpuStatisticsTimer = sys::TimerFactory::createPeriodicTimer( - this, "cpuStatistics", constants::timerInitInterval, [this](sys::Timer &) { CpuStatisticsTimerHandler(); }); - cpuStatisticsTimer.start(); + freqTimer = sys::TimerFactory::createPeriodicTimer( + this, "cpuTick", constants::timerInitInterval, [this](sys::Timer &) { FreqUpdateTick(); }); + freqTimer.start(); powerManagerEfficiencyTimer = sys::TimerFactory::createPeriodicTimer( - this, "logPowerManagerEfficiency", constants::powerManagerLogsTimerInterval, [this](sys::Timer &) { + this, "logPowerManagerEfficiency", constants::powerManagerLogsTimerInterval, [](sys::Timer &) { powerManager->LogPowerManagerEfficiency(); }); powerManagerEfficiencyTimer.start(); @@ -437,6 +437,36 @@ namespace sys return false; } + std::string SystemManagerCommon::ServiceProcessor(const uint32_t &t) + { + if (t == 0) { + return "Idle"; + } + + auto foo = [](auto &l, const uint32_t &t) { + auto found = std::find_if(l.begin(), l.end(), [&t](auto &r) { + auto right = uxTaskGetTCBNumber(r->GetHandle()); + auto left = t; + return left == right; + }); + return found; + }; + + if (auto found = foo(applicationsList, t); found != std::end(applicationsList)) { + return (*found)->GetName() + "::" + (*found)->getCurrentProcessing(); + } + if (auto found = foo(servicesList, t); found != std::end(servicesList)) { + return (*found)->GetName() + "::" + (*found)->getCurrentProcessing(); + } + + auto handle = xTaskGetByTCBNumber(t); + if (handle != nullptr) { + return pcTaskGetTaskName(handle); + } + + return "none"; + } + void SystemManagerCommon::preCloseRoutine(CloseReason closeReason) { for (const auto &service : servicesList) { @@ -678,9 +708,7 @@ namespace sys // In case if other power down request arrive in the meantime lowBatteryShutdownDelay.stop(); - - // We stop the timers - cpuStatisticsTimer.stop(); + freqTimer.stop(); powerManagerEfficiencyTimer.stop(); // We are going to remove services in reversed order of creation @@ -744,15 +772,16 @@ namespace sys set(newState); } - void SystemManagerCommon::CpuStatisticsTimerHandler() + void SystemManagerCommon::FreqUpdateTick() { if (!cpuStatisticsTimerInit) { cpuStatisticsTimerInit = true; - cpuStatisticsTimer.restart(constants::timerPeriodInterval); + freqTimer.restart(constants::timerPeriodInterval); } - cpuStatistics->Update(); - powerManager->UpdateCpuFrequency(cpuStatistics->GetPercentageCpuLoad()); + cpuStatistics->UpdatePercentageCpuLoad(); + auto ret = powerManager->UpdateCpuFrequency(cpuStatistics->GetPercentageCpuLoad()); + cpuStatistics->TrackChange(ret); } void SystemManagerCommon::UpdateResourcesAfterCpuFrequencyChange(bsp::CpuFrequencyMHz newFrequency) diff --git a/module-sys/SystemManager/doc/CpuStatistics.md b/module-sys/SystemManager/doc/CpuStatistics.md new file mode 100644 index 0000000000000000000000000000000000000000..a230fbde740b4bb589ef48861ecc45fc61f7cc34 --- /dev/null +++ b/module-sys/SystemManager/doc/CpuStatistics.md @@ -0,0 +1,65 @@ +CPU statistics +============== + +We are able to gather CPU statistics and dump them wherever we want via printer. +Current CPU statistics gathering: +- measures each task in profiling pool data +- prints last 100ms CPU usage data after frequency was changed + +# preface + +Our processor usage is limited to: +- Applications +- Services +- Workers +- Threads +- IRQ's + +Applications and Services are registered on bus and are processing data on demand. +Workers should also process data on demand, but do not have bus - but communicate via custom bindings. Workers are bound to Services. +Threads are naked FreeRTOS threads, for our needs its: Idle task, Timer task - these do not have any rule how should be implemented. +IRQ's are very processing specific. + +For Applications and Services we can measure: +- What request was approximately was handled and how long +For Workers and threads we can measure: +- How long was it processing. +For IRQs we do not have measuring capabilities right now. + +# data gathering + +defined in: `module-os/FreeRTOS/perf.h`, on each context switch saves data in memory pool for each task. +used in: +- CpuStatistics.cpp +- tasks.c + +Two approaches were tested: +1. approach where profiling data was stored separately on each context switch +2. approach where profiling data is a sum of time for all context switches +profiling gathering is meant to have O0 complexity to not add load to the core processing + +The first approach resulted in quite a lot of data, especially in the moments when the processor was fully loaded. +With filtering out useless information it resulted in ~60 samples per 100ms when in idle, while having thousands when +actual processing was happening (i.e. rendering). This resulted in pretty graphing capabilities, but was not useable +for storage, as we could easily gather thousands kB per 100ms +The second approach sums time and context switches per task from the last request. +This way we do not know what exact tasks fight for the CPU attention, but have overall **smoothened** characteristics. + +The second approach is more convenient to both: +- storage: with samples limiting we can save data only when frequency is changed, which depending on time span should take up to hundreds of bytes. normally tens right now. +- ram usage: we can limit buffer to predicted count od threads, therefore whole performance gathering buffer shall take around 600 bytes. + +# data printing + +There is `sys::cpu::Printer` class in CpuStatistics for use. Depending of needs curently it can either: +- not print at all +- print CPU usage as formatted csv string to std::out +- print CPU usage as message pack csv to std::out +Formatted message pack can be modified to store data in file to minimize it's size. This way we will have small footprint, easily accessible statistics without logging. +Any other printing capabilities could be added. + +# IRQ time + +Currently we do not measure time spent in IRQs, this could be easily achieved by: +- funneling all IVT calls to single IRQ receptor function +- calling IVT calls on demand form them, while saving GPT time before and after the call diff --git a/module-sys/SystemManager/doc/PowerManagement.md b/module-sys/SystemManager/doc/PowerManagement.md index 1cee0d635c5a9a659607b0039db83f3620333a3b..e1d423bffc26fec73bd567ce11f3cae22cba0a19 100644 --- a/module-sys/SystemManager/doc/PowerManagement.md +++ b/module-sys/SystemManager/doc/PowerManagement.md @@ -75,3 +75,6 @@ and requesting resources from `service_cellular` to make a phone call: ![](./data/cellularResourceRequest.svg) +# CpuStatistics + +CpuStatistics measurement is described in: [link](./module-sys/SystemManager/doc/CpuStatistics.md) diff --git a/module-sys/SystemManager/include/SystemManager/CpuGovernor.hpp b/module-sys/SystemManager/include/SystemManager/CpuGovernor.hpp index 18911e2a6374b43c5999fe90913ad77e80158bee..bbc75defb46bf0fc3d03aaf0b43347715a385d94 100644 --- a/module-sys/SystemManager/include/SystemManager/CpuGovernor.hpp +++ b/module-sys/SystemManager/include/SystemManager/CpuGovernor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once @@ -9,6 +9,23 @@ namespace sys { + + namespace sentinel + { + struct Data + { + UBaseType_t ownerTCBNumber = 0; + /// name of sentinel thread responsible for curent minimum load + std::string name; + /// curent minimum frequency set in sentinel + bsp::CpuFrequencyMHz frequency = bsp::CpuFrequencyMHz::Level_0; + /// please do not use this task handle to perform actions, it's just for reference sake + TaskHandle_t task; + /// textual information on what actually happens + std::string reason; + }; + }; // namespace sentinel + using SentinelPointer = std::weak_ptr; class GovernorSentinel @@ -43,7 +60,7 @@ namespace sys bool permanentBlock = false); void ResetCpuFrequencyRequest(std::string sentinelName, bool permanentBlock = false); - [[nodiscard]] auto GetMinimumFrequencyRequested() const noexcept -> bsp::CpuFrequencyMHz; + [[nodiscard]] auto GetMinimumFrequencyRequested() const noexcept -> sentinel::Data; void InformSentinelsAboutCpuFrequencyChange(bsp::CpuFrequencyMHz newFrequency) const noexcept; [[nodiscard]] auto GetPermanentFrequencyRequested() const noexcept -> PermanentFrequencyToHold; @@ -51,6 +68,7 @@ namespace sys private: static void PrintName(const GovernorSentinelPointer &element); + /// this could be set - set is sorted :) GovernorSentinelsVector sentinels; PermanentFrequencyToHold permanentFrequencyToHold{false, bsp::CpuFrequencyMHz::Level_0}; }; diff --git a/module-sys/SystemManager/include/SystemManager/CpuPrinter.hpp b/module-sys/SystemManager/include/SystemManager/CpuPrinter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dedfffe1a4d81b14c23e93065d62d31b084e5c1e --- /dev/null +++ b/module-sys/SystemManager/include/SystemManager/CpuPrinter.hpp @@ -0,0 +1,44 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include +struct task_prof_data; +namespace sys::cpu +{ + struct UpdateResult; + + namespace stats + { + + class Printer + { + public: + virtual void printSysUsage(struct task_prof_data *data, size_t size) = 0; + virtual void printCPUChange(const cpu::UpdateResult &ret) = 0; + }; + + class NullPrinter : public Printer + { + void printSysUsage(struct task_prof_data *data, size_t size) + {} + void printCPUChange(const cpu::UpdateResult &ret) + {} + }; + + class LogPrinter : public Printer + { + public: + void printSysUsage(struct task_prof_data *data, size_t size) override; + void printCPUChange(const cpu::UpdateResult &ret) override; + }; + + class PackPrinter : public Printer + { + public: + void printSysUsage(struct task_prof_data *data, size_t size) override; + void printCPUChange(const cpu::UpdateResult &ret) override; + }; + }; // namespace stats +}; // namespace sys::cpu diff --git a/module-sys/SystemManager/include/SystemManager/CpuSentinel.hpp b/module-sys/SystemManager/include/SystemManager/CpuSentinel.hpp index cdd14e9b7e0c006a523958dd674a9301ca4ef76a..1e436c005773e325b708bd0c2f2cd231ddc3b629 100644 --- a/module-sys/SystemManager/include/SystemManager/CpuSentinel.hpp +++ b/module-sys/SystemManager/include/SystemManager/CpuSentinel.hpp @@ -46,6 +46,8 @@ namespace sys void CpuFrequencyHasChanged(bsp::CpuFrequencyMHz newFrequency); void ReadRegistrationData(bsp::CpuFrequencyMHz frequencyHz, bool permanentFrequency); + TaskHandle_t getTask(); + std::string getReason(); protected: const std::string name; @@ -59,7 +61,8 @@ namespace sys /// critical section or mutex support necessary std::function callback; - TaskHandle_t taskHandle = nullptr; + TaskHandle_t taskWaitingForFrequency = nullptr; + std::string currentReason; }; /// Sentinel releases the frequency lock automatically after the time specified in the parameter - timeout diff --git a/module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp b/module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp index ad102319be509eadb5b05cfceda0dce32a83cbc9..0babb6343fbd3cf312d226114b9b168b9f4761dd 100644 --- a/module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp +++ b/module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp @@ -1,10 +1,17 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #pragma once +#include "SystemManager/PowerManager.hpp" +#include "SystemManager/CpuPrinter.hpp" #include +extern "C" +{ +#include "prof.h" +} + namespace sys { @@ -12,15 +19,28 @@ namespace sys { public: - void Update(); + CpuStatistics(); + ~CpuStatistics(); + /// stores system usage, should be called before any CPU frequency change + /// this way we know what services were in use and for how long before it happened + void StoreSysUsage(); [[nodiscard]] uint32_t GetPercentageCpuLoad() const noexcept; + void UpdatePercentageCpuLoad(); + void TrackChange(const cpu::UpdateResult &ret); private: + /// used to print stored data in CpuStatistics on change + std::unique_ptr printer; uint32_t ComputeIncrease(uint32_t currentCount, uint32_t lastCount) const; uint32_t lastIdleTickCount{0}; uint32_t lastTotalTickCount{0}; uint32_t cpuLoad{0}; + +#if PROF_ON + size_t data_size{0}; + struct task_prof_data *data = nullptr; +#endif }; } // namespace sys diff --git a/module-sys/SystemManager/include/SystemManager/PowerManager.hpp b/module-sys/SystemManager/include/SystemManager/PowerManager.hpp index a1211afa74456e0466225b7917e51a839cb179ad..c57a062cab42d51ea136bad1dff16986c87b34a6 100644 --- a/module-sys/SystemManager/include/SystemManager/PowerManager.hpp +++ b/module-sys/SystemManager/include/SystemManager/PowerManager.hpp @@ -8,6 +8,7 @@ #include "bsp/lpm/bsp_lpm.hpp" #include "drivers/semc/DriverSEMC.hpp" +#include "SysCpuUpdateResult.hpp" #include "CpuGovernor.hpp" #include #include @@ -47,7 +48,7 @@ namespace sys /// periods the current CPU usage was below the lower limit (frequencyShiftLowerThreshold), CPU frequency is /// reduced frequency /// @param current cpu load - void UpdateCpuFrequency(uint32_t cpuLoad); + [[nodiscard]] cpu::UpdateResult UpdateCpuFrequency(uint32_t cpuLoad); [[nodiscard]] auto getExternalRamDevice() const noexcept -> std::shared_ptr; diff --git a/module-sys/SystemManager/include/SystemManager/SysCpuUpdateResult.hpp b/module-sys/SystemManager/include/SystemManager/SysCpuUpdateResult.hpp new file mode 100644 index 0000000000000000000000000000000000000000..53f8f28badf26a1f469fbcbdb60e00052bc94a11 --- /dev/null +++ b/module-sys/SystemManager/include/SystemManager/SysCpuUpdateResult.hpp @@ -0,0 +1,15 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +#include "SystemManager/CpuGovernor.hpp" +namespace sys::cpu +{ + struct UpdateResult + { + bool changed = false; + bsp::CpuFrequencyMHz frequencySet = bsp::CpuFrequencyMHz::Level_0; + sentinel::Data data{}; + }; +}; // namespace sys::cpu diff --git a/module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp b/module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp index 86c663b3540e44392d30646663a912a2ecf50080..8f52612fc0e927c1a56d75e033bfefdbf0a28011 100644 --- a/module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp +++ b/module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp @@ -121,6 +121,8 @@ namespace sys /// Destroy existing application static bool DestroyApplication(const std::string &name, Service *caller); + static std::string ServiceProcessor(const uint32_t &t); + /// Kill service /// @note - this is final, it straight takes service, calls it's close callback and it's gone /// please mind that services & apps not registered in SystemManager cant be killed - these should be handled by @@ -185,8 +187,7 @@ namespace sys void RebootToUsbMscModeHandler(State newState); - /// periodic update of cpu statistics - void CpuStatisticsTimerHandler(); + void FreqUpdateTick(); /// used for power management control for the filesystem void UpdateResourcesAfterCpuFrequencyChange(bsp::CpuFrequencyMHz newFrequency); @@ -195,7 +196,7 @@ namespace sys UpdateReason updateReason{UpdateReason::Update}; std::vector> systemServiceCreators; - sys::TimerHandle cpuStatisticsTimer; + sys::TimerHandle freqTimer; sys::TimerHandle servicesPreShutdownRoutineTimeout; sys::TimerHandle lowBatteryShutdownDelay; sys::TimerHandle powerManagerEfficiencyTimer; diff --git a/module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp b/module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp index 35d3db39a894a26af56bf90abfe4b4e08c48c011..8a50ed61babdb1db424ad380dbebf62704a77722 100644 --- a/module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp +++ b/module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp @@ -68,27 +68,27 @@ TEST_CASE("Power Manager CPU sentinels governor test") governor->RegisterNewSentinel(testSentinel_1); governor->RegisterNewSentinel(testSentinel_2); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_0); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_0); governor->SetCpuFrequencyRequest("testSentinel_1", bsp::CpuFrequencyMHz::Level_4); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_4); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_4); governor->SetCpuFrequencyRequest("testSentinel_2", bsp::CpuFrequencyMHz::Level_6); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_6); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_6); governor->SetCpuFrequencyRequest("testSentinel_1", bsp::CpuFrequencyMHz::Level_2); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_6); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_6); governor->ResetCpuFrequencyRequest("testSentinel_2"); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_2); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_2); - governor->SetCpuFrequencyRequest("bedNameSentinel", bsp::CpuFrequencyMHz::Level_6); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_2); + governor->SetCpuFrequencyRequest("badNameSentinel", bsp::CpuFrequencyMHz::Level_6); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_2); governor->SetCpuFrequencyRequest("testSentinel_1", bsp::CpuFrequencyMHz::Level_1); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_1); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_1); governor->ResetCpuFrequencyRequest("testSentinel_1"); - REQUIRE(governor->GetMinimumFrequencyRequested() == bsp::CpuFrequencyMHz::Level_0); + REQUIRE(governor->GetMinimumFrequencyRequested().frequency == bsp::CpuFrequencyMHz::Level_0); } } diff --git a/module-utils/log/api/log/log.hpp b/module-utils/log/api/log/log.hpp index 6dbf09c05b5559eab3c15d857cdffe119fdeff48..d641b9235ac4ff5589715f2564bc2c0bc27c5466 100644 --- a/module-utils/log/api/log/log.hpp +++ b/module-utils/log/api/log/log.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md /* @@ -62,6 +62,10 @@ extern "C" */ int log_Log(logger_level level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__((format(printf, 5, 6))); +#ifdef LOG_IGNORE_ALL + int log_ignore(logger_level level, const char *file, int line, const char *function, const char *fmt, ...) + __attribute__((format(printf, 5, 6))); +#endif int log_Printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void log_WriteToDevice(const uint8_t *pBuffer, unsigned NumBytes); @@ -69,6 +73,7 @@ extern "C" * Log functions (one per level). */ #define LOG_PRINTF(...) log_Printf(__VA_ARGS__) +#ifndef LOG_IGNORE_ALL #define LOG_TRACE(...) log_Log(LOGTRACE, __FILENAME__, __LINE__, __func__, __VA_ARGS__) #define LOG_DEBUG(...) log_Log(LOGDEBUG, __FILENAME__, __LINE__, __func__, __VA_ARGS__) #define LOG_INFO(...) log_Log(LOGINFO, __FILENAME__, __LINE__, __func__, __VA_ARGS__) @@ -76,6 +81,16 @@ extern "C" #define LOG_ERROR(...) log_Log(LOGERROR, __FILENAME__, __LINE__, __func__, __VA_ARGS__) #define LOG_FATAL(...) log_Log(LOGFATAL, __FILENAME__, __LINE__, __func__, __VA_ARGS__) #define LOG_CUSTOM(loggerLevel, ...) log_Log(loggerLevel, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#else +#define LOG_TRACE(...) log_ignore(LOGTRACE, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#define LOG_DEBUG(...) log_ignore(LOGDEBUG, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#define LOG_INFO(...) log_ignore(LOGINFO, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#define LOG_WARN(...) log_ignore(LOGWARN, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#define LOG_ERROR(...) log_ignore(LOGERROR, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#define LOG_FATAL(...) log_ignore(LOGFATAL, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#define LOG_CUSTOM(loggerLevel, ...) log_ignore(loggerLevel, __FILENAME__, __LINE__, __func__, __VA_ARGS__) +#endif + #if LOG_SENSITIVE_DATA_ENABLED #define LOG_SENSITIVE(loggerLevel, ...) log_Log(loggerLevel, __FILENAME__, __LINE__, __func__, __VA_ARGS__) #else diff --git a/module-utils/log/log.cpp b/module-utils/log/log.cpp index 32961def98025c8d48893fe735e2207cdb9628fb..7cff1be747f967c6d798e1cc9abd27b839986bd9 100644 --- a/module-utils/log/log.cpp +++ b/module-utils/log/log.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include @@ -23,6 +23,14 @@ int log_Printf(const char *fmt, ...) return result; } +int log_ignore(logger_level level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + va_end(args); + return 0; +} + int log_Log(logger_level level, const char *file, int line, const char *function, const char *fmt, ...) { va_list args; diff --git a/products/PurePhone/CMakeLists.txt b/products/PurePhone/CMakeLists.txt index befc79f645fdc938befe08fff77da5b1e42f1507..b6334e662c686ca85ba4f1934ba7f404c0f333bf 100644 --- a/products/PurePhone/CMakeLists.txt +++ b/products/PurePhone/CMakeLists.txt @@ -28,6 +28,7 @@ target_sources(PurePhone PurePhoneMain.cpp PlatformFactory.cpp EinkSentinelPure.cpp + init_prof.cpp ) target_include_directories(PurePhone diff --git a/products/PurePhone/EinkSentinelPure.cpp b/products/PurePhone/EinkSentinelPure.cpp index d1c1165d6e9625831ac22679a0f05ad2d1f91888..0e41942f6d6b0d9862b09bcefd52fbb64842c1f1 100644 --- a/products/PurePhone/EinkSentinelPure.cpp +++ b/products/PurePhone/EinkSentinelPure.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "EinkSentinel.hpp" @@ -27,6 +27,8 @@ namespace service::eink void EinkSentinel::HoldMinimumFrequency() { + currentReason = std::string("up: ") + owner->getCurrentProcessing() + std::string(" req: ") + + std::to_string(static_cast((GetFrequency()))); CpuSentinel::HoldMinimumFrequency(isScreenLocked ? RedrawLockedEinkCpuFrequency : RedrawUnlockedEinkCpuFrequency); } diff --git a/products/PurePhone/PurePhoneMain.cpp b/products/PurePhone/PurePhoneMain.cpp index 362438594f52d448c904da1283cd958f5bf60d67..8d8494253b206e292310cb595be91b544011e156 100644 --- a/products/PurePhone/PurePhoneMain.cpp +++ b/products/PurePhone/PurePhoneMain.cpp @@ -71,6 +71,7 @@ #include #include #include +#include "init_prof.hpp" void atexit_cleanup_handler() { @@ -95,6 +96,8 @@ int main() const std::vector fileIndexerAudioPaths = {{purefs::dir::getUserDiskPath() / "music"}}; + prof::init(); + #if SYSTEM_VIEW_ENABLED SEGGER_SYSVIEW_Conf(); SEGGER_SYSVIEW_DisableEvents(SYSVIEW_EVTMASK_ISR_ENTER); diff --git a/products/PurePhone/init_prof.cpp b/products/PurePhone/init_prof.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71c17c8dc1660360d8c9b97eff7941c85170d2b2 --- /dev/null +++ b/products/PurePhone/init_prof.cpp @@ -0,0 +1,26 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#include "init_prof.hpp" +#include "FreeRTOS.h" +#include +#include + +extern "C" +{ +#include "prof.h" +} + +namespace prof +{ + void init() + { +#if PROF_ON + struct prof_pool_init_data init + { + 32 + }; + prof_pool_init(init); +#endif + } +} // namespace prof diff --git a/products/PurePhone/init_prof.hpp b/products/PurePhone/init_prof.hpp new file mode 100644 index 0000000000000000000000000000000000000000..23f173e5ee6fc3ff1c95cc279ee4c2eced588dd7 --- /dev/null +++ b/products/PurePhone/init_prof.hpp @@ -0,0 +1,9 @@ +// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. +// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md + +#pragma once + +namespace prof +{ + void init(); +} diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index bd5769acd9043eec123147255235cc1c93afb91a..e4ea1c4914699c34691bdc7ce799bdad32fe3ff5 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -25,6 +25,8 @@ add_subdirectory(taglib) add_subdirectory(tinyexpr) add_subdirectory(utz) add_subdirectory(dr_libs) +add_subdirectory(msgpack11) + if (${PROJECT_TARGET} STREQUAL "TARGET_RT1051") add_subdirectory(CrashDebug) diff --git a/third-party/msgpack11/CMakeLists.txt b/third-party/msgpack11/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..dd1330dfba50eb56c300a36d2e9710b26fa3779a --- /dev/null +++ b/third-party/msgpack11/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(msgpack11 msgpack11/msgpack11.cpp) + +target_include_directories(msgpack11 + INTERFACE + $