~aleteoryx/muditaos

30487c600885ff6f08de2699a19d1497eb00fcb6 — Adam Dobrowolski 4 years ago 082966f
[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.
48 files changed, 973 insertions(+), 67 deletions(-)

M .gdbinit-1051
M .gitmodules
M cmake/modules/ProjectConfig.cmake
M module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp
M module-bsp/bsp/common.cpp
M module-bsp/bsp/common.hpp
M module-os/CMakeLists.txt
M module-os/FreeRTOS/include/task.h
A module-os/FreeRTOS/prof.c
A module-os/FreeRTOS/prof.h
M module-os/FreeRTOS/tasks.c
M module-os/LockGuard.cpp
M module-os/board/linux/fsl_runtimestat_gpt.c
M module-os/board/linux/macros.h
M module-os/board/linux/port.c
M module-os/board/rt1051/include/macros.h
A module-os/test/CMakeLists.txt
A module-os/test/performance-monitor.cpp
M module-sys/Service/Service.cpp
M module-sys/Service/include/Service/Service.hpp
M module-sys/SystemManager/CMakeLists.txt
M module-sys/SystemManager/CpuGovernor.cpp
A module-sys/SystemManager/CpuLogPrinter.cpp
A module-sys/SystemManager/CpuPackPrinter.cpp
M module-sys/SystemManager/CpuSentinel.cpp
M module-sys/SystemManager/CpuStatistics.cpp
M module-sys/SystemManager/PowerManager.cpp
M module-sys/SystemManager/SystemManagerCommon.cpp
A module-sys/SystemManager/doc/CpuStatistics.md
M module-sys/SystemManager/doc/PowerManagement.md
M module-sys/SystemManager/include/SystemManager/CpuGovernor.hpp
A module-sys/SystemManager/include/SystemManager/CpuPrinter.hpp
M module-sys/SystemManager/include/SystemManager/CpuSentinel.hpp
M module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp
M module-sys/SystemManager/include/SystemManager/PowerManager.hpp
A module-sys/SystemManager/include/SystemManager/SysCpuUpdateResult.hpp
M module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp
M module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp
M module-utils/log/api/log/log.hpp
M module-utils/log/log.cpp
M products/PurePhone/CMakeLists.txt
M products/PurePhone/EinkSentinelPure.cpp
M products/PurePhone/PurePhoneMain.cpp
A products/PurePhone/init_prof.cpp
A products/PurePhone/init_prof.hpp
M third-party/CMakeLists.txt
A third-party/msgpack11/CMakeLists.txt
A third-party/msgpack11/msgpack11
M .gdbinit-1051 => .gdbinit-1051 +4 -0
@@ 15,3 15,7 @@ thread 2
tb main
b HardFault_Handler
b _exit
b abort
b WDOG1_IRQHandler
b RTWDOG_IRQHandler
b IntDefaultHandler

M .gitmodules => .gitmodules +3 -0
@@ 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

M cmake/modules/ProjectConfig.cmake => cmake/modules/ProjectConfig.cmake +8 -0
@@ 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 ""
        )


M module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp => module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp +2 -2
@@ 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<int16_t, AUDIO_BUFFER_LENGTH> audioFrameOut;
    std::array<int16_t, AUDIO_BUFFER_LENGTH> audioFrameOut{};

    if (size > audioFrameOut.size()) {
        LOG_WARN("SCO packet larger than local output buffer - dropping data.");

M module-bsp/bsp/common.cpp => module-bsp/bsp/common.cpp +21 -0
@@ 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;
   }
};


M module-bsp/bsp/common.hpp => module-bsp/bsp/common.hpp +3 -0
@@ 3,6 3,7 @@

#pragma once

#include <cstdint>
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{

M module-os/CMakeLists.txt => module-os/CMakeLists.txt +7 -0
@@ 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 ()

M module-os/FreeRTOS/include/task.h => module-os/FreeRTOS/include/task.h +2 -0
@@ 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

A module-os/FreeRTOS/prof.c => module-os/FreeRTOS/prof.c +98 -0
@@ 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 <stdlib.h>
#include <string.h>

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;
}

A module-os/FreeRTOS/prof.h => module-os/FreeRTOS/prof.h +57 -0
@@ 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 <stddef.h>
#include <stdint.h>

/// 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

M module-os/FreeRTOS/tasks.c => module-os/FreeRTOS/tasks.c +109 -3
@@ 26,6 26,7 @@
 */

/* Standard includes. */
#include "prof.h"
#include <stdlib.h>
#include <string.h>



@@ 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;
}

M module-os/LockGuard.cpp => module-os/LockGuard.cpp +9 -0
@@ 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);
    }

M module-os/board/linux/fsl_runtimestat_gpt.c => module-os/board/linux/fsl_runtimestat_gpt.c +47 -1
@@ 2,12 2,58 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include <sys/times.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

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;
}

M module-os/board/linux/macros.h => module-os/board/linux/macros.h +8 -0
@@ 73,4 73,12 @@ static inline bool isIRQ()
}


#ifdef __cplusplus
extern "C" {
#endif
    int isOSRunning();
#ifdef __cplusplus
};
#endif

#endif /* MACROS_H_ */

M module-os/board/linux/port.c => module-os/board/linux/port.c +5 -0
@@ 921,3 921,8 @@ BaseType_t xPortIsInsideInterrupt(void)
{
    return pdFALSE;
}

uint32_t CLOCK_GetFreq(int clock) {
    (void)clock;
    return 0;
}

M module-os/board/rt1051/include/macros.h => module-os/board/rt1051/include/macros.h +8 -0
@@ 71,3 71,11 @@ static inline void haltIfDebugging()
        __asm("bkpt 1");
    }
}

#ifdef __cplusplus
extern "C" {
#endif
    int isOSRunning();
#ifdef __cplusplus
};
#endif

A module-os/test/CMakeLists.txt => module-os/test/CMakeLists.txt +9 -0
@@ 0,0 1,9 @@
if(${PROF_ON})
add_catch2_executable(
    NAME performance
    SRCS
        performance-monitor.cpp
    LIBS
        module-os
)
endif()

A module-os/test/performance-monitor.cpp => module-os/test/performance-monitor.cpp +49 -0
@@ 0,0 1,49 @@
#include <limits>
#define CATCH_CONFIG_MAIN

#include <catch2/catch.hpp>
#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();
}

M module-sys/Service/Service.cpp => module-sys/Service/Service.cpp +6 -0
@@ 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) {

M module-sys/Service/include/Service/Service.hpp => module-sys/Service/include/Service/Service.hpp +3 -0
@@ 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 &
        {

M module-sys/SystemManager/CMakeLists.txt => module-sys/SystemManager/CMakeLists.txt +3 -0
@@ 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})

M module-sys/SystemManager/CpuGovernor.cpp => module-sys/SystemManager/CpuGovernor.cpp +22 -8
@@ 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 <SystemManager/CpuGovernor.hpp>


@@ 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

A module-sys/SystemManager/CpuLogPrinter.cpp => module-sys/SystemManager/CpuLogPrinter.cpp +47 -0
@@ 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 <SystemManager/SystemManagerCommon.hpp>

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

A module-sys/SystemManager/CpuPackPrinter.cpp => module-sys/SystemManager/CpuPackPrinter.cpp +46 -0
@@ 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 <SystemManager/SystemManagerCommon.hpp>
#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

M module-sys/SystemManager/CpuSentinel.cpp => module-sys/SystemManager/CpuSentinel.cpp +17 -4
@@ 31,6 31,8 @@ namespace sys
            auto msg = std::make_shared<sys::HoldCpuFrequencyMessage>(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<sys::ReleaseCpuFrequencyMessage>(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

M module-sys/SystemManager/CpuStatistics.cpp => module-sys/SystemManager/CpuStatistics.cpp +52 -8
@@ 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 <SystemManager/CpuStatistics.hpp>


@@ 7,25 7,69 @@
#include <task.h>
#include <limits>

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<cpu::stats::LogPrinter>();
    }

    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

M module-sys/SystemManager/PowerManager.cpp => module-sys/SystemManager/PowerManager.cpp +24 -9
@@ 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 <gsl/util>
#include <log/log.hpp>

#include <SystemManager/PowerManager.hpp>


@@ 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, &currentCpuFreq, 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)

M module-sys/SystemManager/SystemManagerCommon.cpp => module-sys/SystemManager/SystemManagerCommon.cpp +40 -11
@@ 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)

A module-sys/SystemManager/doc/CpuStatistics.md => module-sys/SystemManager/doc/CpuStatistics.md +65 -0
@@ 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

M module-sys/SystemManager/doc/PowerManagement.md => module-sys/SystemManager/doc/PowerManagement.md +3 -0
@@ 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)

M module-sys/SystemManager/include/SystemManager/CpuGovernor.hpp => module-sys/SystemManager/include/SystemManager/CpuGovernor.hpp +20 -2
@@ 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<CpuSentinel>;

    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};
    };

A module-sys/SystemManager/include/SystemManager/CpuPrinter.hpp => module-sys/SystemManager/include/SystemManager/CpuPrinter.hpp +44 -0
@@ 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 <cstddef>
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

M module-sys/SystemManager/include/SystemManager/CpuSentinel.hpp => module-sys/SystemManager/include/SystemManager/CpuSentinel.hpp +4 -1
@@ 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<void(bsp::CpuFrequencyMHz)> 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

M module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp => module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp +22 -2
@@ 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 <cstdint>

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<cpu::stats::Printer> 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

M module-sys/SystemManager/include/SystemManager/PowerManager.hpp => module-sys/SystemManager/include/SystemManager/PowerManager.hpp +2 -1
@@ 8,6 8,7 @@

#include "bsp/lpm/bsp_lpm.hpp"
#include "drivers/semc/DriverSEMC.hpp"
#include "SysCpuUpdateResult.hpp"
#include "CpuGovernor.hpp"
#include <bsp/lpm/PowerProfile.hpp>
#include <vector>


@@ 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<devices::Device>;


A module-sys/SystemManager/include/SystemManager/SysCpuUpdateResult.hpp => module-sys/SystemManager/include/SystemManager/SysCpuUpdateResult.hpp +15 -0
@@ 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

M module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp => module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp +4 -3
@@ 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<std::unique_ptr<BaseServiceCreator>> systemServiceCreators;
        sys::TimerHandle cpuStatisticsTimer;
        sys::TimerHandle freqTimer;
        sys::TimerHandle servicesPreShutdownRoutineTimeout;
        sys::TimerHandle lowBatteryShutdownDelay;
        sys::TimerHandle powerManagerEfficiencyTimer;

M module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp => module-sys/SystemManager/tests/unittest_CpuSentinelsGovernor.cpp +9 -9
@@ 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);
    }
}

M module-utils/log/api/log/log.hpp => module-utils/log/api/log/log.hpp +16 -1
@@ 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

M module-utils/log/log.cpp => module-utils/log/log.cpp +9 -1
@@ 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 <log/log.hpp>


@@ 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;

M products/PurePhone/CMakeLists.txt => products/PurePhone/CMakeLists.txt +1 -0
@@ 28,6 28,7 @@ target_sources(PurePhone
        PurePhoneMain.cpp
        PlatformFactory.cpp
        EinkSentinelPure.cpp
        init_prof.cpp
    )

target_include_directories(PurePhone

M products/PurePhone/EinkSentinelPure.cpp => products/PurePhone/EinkSentinelPure.cpp +3 -1
@@ 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<int>((GetFrequency())));
        CpuSentinel::HoldMinimumFrequency(isScreenLocked ? RedrawLockedEinkCpuFrequency
                                                         : RedrawUnlockedEinkCpuFrequency);
    }

M products/PurePhone/PurePhoneMain.cpp => products/PurePhone/PurePhoneMain.cpp +3 -0
@@ 71,6 71,7 @@
#include <memory>
#include <vector>
#include <cstdlib>
#include "init_prof.hpp"

void atexit_cleanup_handler()
{


@@ 95,6 96,8 @@ int main()

    const std::vector<std::string> fileIndexerAudioPaths = {{purefs::dir::getUserDiskPath() / "music"}};

    prof::init();

#if SYSTEM_VIEW_ENABLED
    SEGGER_SYSVIEW_Conf();
    SEGGER_SYSVIEW_DisableEvents(SYSVIEW_EVTMASK_ISR_ENTER);

A products/PurePhone/init_prof.cpp => products/PurePhone/init_prof.cpp +26 -0
@@ 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 <cstdint>
#include <bsp/common.hpp>

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

A products/PurePhone/init_prof.hpp => products/PurePhone/init_prof.hpp +9 -0
@@ 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();
}

M third-party/CMakeLists.txt => third-party/CMakeLists.txt +2 -0
@@ 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)

A third-party/msgpack11/CMakeLists.txt => third-party/msgpack11/CMakeLists.txt +6 -0
@@ 0,0 1,6 @@
add_library(msgpack11 msgpack11/msgpack11.cpp)

target_include_directories(msgpack11
    INTERFACE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}
)

A third-party/msgpack11/msgpack11 => third-party/msgpack11/msgpack11 +1 -0
@@ 0,0 1,1 @@
Subproject commit e15f1d33f3b06b9552f4e8b24d83f8c89fb31c35