~aleteoryx/muditaos

f4aaf4e30276066aa4b8a32c73950b16d4dba0aa — Maciej-Mudita 3 years ago d63d460
[MOS-775] Create run-time statistics for tasks

Every hour, statistics of tasks that put a heavy load
on the CPU will be logged
M module-bsp/board/linux/os/FreeRTOSConfig.h => module-bsp/board/linux/os/FreeRTOSConfig.h +7 -1
@@ 1,3 1,6 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*
 * FreeRTOS Kernel V10.0.1
 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.


@@ 122,7 125,7 @@ extern "C"
#define INCLUDE_eTaskGetState                0
#define INCLUDE_xTimerPendFunctionCall       1
#define INCLUDE_xTaskAbortDelay              1
#define INCLUDE_xTaskGetHandle               0
#define INCLUDE_xTaskGetHandle               1
#define INCLUDE_xTaskResumeFromISR           1

#ifdef __NVIC_PRIO_BITS


@@ 172,4 175,7 @@ standard names. */
#define PROJECT_CONFIG_HEAP_INTEGRITY_CHECKS (1)
#endif

extern void trace_deleteTask(const char *name);
#define traceTASK_DELETE(pxTaskToDelete) trace_deleteTask(pxTaskToDelete->pcTaskName)

#endif /* FREERTOS_CONFIG_H */

M module-bsp/board/rt1051/os/include/FreeRTOSConfig.h => module-bsp/board/rt1051/os/include/FreeRTOSConfig.h +7 -1
@@ 1,3 1,6 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*
 * FreeRTOS Kernel V10.0.1
 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.


@@ 122,7 125,7 @@ extern "C"
#define INCLUDE_eTaskGetState                0
#define INCLUDE_xTimerPendFunctionCall       1
#define INCLUDE_xTaskAbortDelay              1
#define INCLUDE_xTaskGetHandle               0
#define INCLUDE_xTaskGetHandle               1
#define INCLUDE_xTaskResumeFromISR           1

#ifdef __NVIC_PRIO_BITS


@@ 177,4 180,7 @@ extern uint32_t ulHighFrequencyTimerTicks(void);
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vConfigureTimerForRunTimeStats()
#define portGET_RUN_TIME_COUNTER_VALUE()         ulHighFrequencyTimerTicks()

extern void trace_deleteTask(const char *name);
#define traceTASK_DELETE(pxTaskToDelete) trace_deleteTask(pxTaskToDelete->pcTaskName)

#endif /* FREERTOS_CONFIG_H */

M module-os/CMakeLists.txt => module-os/CMakeLists.txt +4 -1
@@ 22,6 22,8 @@ add_library(${PROJECT_NAME} STATIC
        CriticalSectionGuard.cpp

        $<$<BOOL:${PROF_ON}>:prof/prof.c>

        trace/DeletedTasks.cpp
        )

# Board specific compilation definitions,options,include directories and features


@@ 49,6 51,7 @@ target_include_directories(${PROJECT_NAME}

        RTOSWrapper/include
        prof
        trace

        )



@@ 60,7 63,7 @@ endif ()
target_link_libraries(${PROJECT_NAME} PUBLIC log-api board-config freertos_kernel)


add_library(freertos-app application.c)
add_library(freertos-app application.c trace/DeletedTasks.cpp)
target_link_libraries(freertos-app PRIVATE freertos_kernel log-api)

if (${ENABLE_TESTS})

A module-os/trace/DeletedTasks.cpp => module-os/trace/DeletedTasks.cpp +53 -0
@@ 0,0 1,53 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DeletedTasks.hpp"
#include <FreeRTOS.h>
#include <mutex.hpp>
#include <task.h>
#include <cstdint>
#include <algorithm>

namespace
{
    std::vector<sys::DeletedTasks::DeletedTaskDetails_t> deletedTasks;
    cpp_freertos::MutexStandard mutex;
} // namespace

// Function called from FreeRTOS when the task is deleted, works on static data therefore it is necessary to ensure
// mutex synchronization
void trace_deleteTask(const char *name)
{
    TaskHandle_t xHandle;
    TaskStatus_t xTaskDetails;

    cpp_freertos::LockGuard lock(mutex);

    xHandle = xTaskGetHandle(name);
    if (xHandle != NULL) {
        vTaskGetInfo(xHandle, &xTaskDetails, pdFALSE, eInvalid);

        auto taskIt = std::find_if(std::begin(deletedTasks), std::end(deletedTasks), [xTaskDetails](auto &el) {
            return el.name == xTaskDetails.pcTaskName;
        });

        if (taskIt != std::end(deletedTasks)) {
            taskIt->tickIncrements += xTaskDetails.ulRunTimeCounter;
        }
        else {
            deletedTasks.push_back(
                {.name = xTaskDetails.pcTaskName, .tickIncrements = xTaskDetails.ulRunTimeCounter});
        }
    }
}

namespace sys
{
    void DeletedTasks::MigrateDeletedTasks(std::vector<DeletedTaskDetails_t> &task)
    {
        cpp_freertos::LockGuard lock(mutex);

        task.assign(deletedTasks.begin(), deletedTasks.end()); 
        deletedTasks.clear();
    }
}

A module-os/trace/DeletedTasks.hpp => module-os/trace/DeletedTasks.hpp +27 -0
@@ 0,0 1,27 @@
// 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 <vector>
#include <cstdint>

namespace sys
{

    class DeletedTasks
    {

      public:
        struct DeletedTaskDetails_t
        {
            const char *name;             // A pointer to the task's name
            std::uint32_t tickIncrements; // The total run time allocated to the task so far, as defined by the run time
                                            // stats clock.
        };

        void MigrateDeletedTasks(std::vector<DeletedTaskDetails_t> &task);
    };

} // namespace sys


M module-sys/SystemManager/CMakeLists.txt => module-sys/SystemManager/CMakeLists.txt +2 -0
@@ 9,12 9,14 @@ target_sources(sys-manager
        include/SystemManager/CpuGovernor.hpp
        include/SystemManager/PowerManager.hpp
        include/SystemManager/DeviceManager.hpp
        include/SystemManager/TaskStatistics.hpp
    
    PRIVATE
        CpuGovernor.cpp
        CpuSentinel.cpp
        GovernorSentinelOperations.cpp
        CpuStatistics.cpp
        TaskStatistics.cpp
        CpuLogPrinter.cpp
        CpuPackPrinter.cpp
        data/SystemManagerActionsParams.hpp

M module-sys/SystemManager/CpuStatistics.cpp => module-sys/SystemManager/CpuStatistics.cpp +3 -12
@@ 5,7 5,7 @@
#include <log/log.hpp>
#include <FreeRTOS.h>
#include <task.h>
#include <limits>
#include <Utils.hpp>

extern "C"
{


@@ 65,8 65,8 @@ namespace sys
    {
        uint32_t idleTickCount     = xTaskGetIdleRunTimeCounter();
        uint32_t totalTickCount    = ulHighFrequencyTimerTicks();
        uint32_t idleTickIncrease  = ComputeIncrease(idleTickCount, lastIdleTickCount);
        uint32_t totalTickIncrease = ComputeIncrease(totalTickCount, lastTotalTickCount);
        uint32_t idleTickIncrease  = utils::computeIncrease(idleTickCount, lastIdleTickCount);
        uint32_t totalTickIncrease = utils::computeIncrease(totalTickCount, lastTotalTickCount);
        lastIdleTickCount          = idleTickCount;
        lastTotalTickCount         = totalTickCount;
        if (totalTickIncrease != 0u) {


@@ 83,13 83,4 @@ namespace sys
        return cpuLoad;
    }

    uint32_t CpuStatistics::ComputeIncrease(uint32_t currentCount, uint32_t lastCount) const
    {
        if (currentCount >= lastCount) {
            return currentCount - lastCount;
        }
        else {
            return std::numeric_limits<uint32_t>::max() - lastCount + currentCount;
        }
    }
} // namespace sys

M module-sys/SystemManager/PowerManager.cpp => module-sys/SystemManager/PowerManager.cpp +31 -8
@@ 10,6 10,7 @@
#include <SystemManager/PowerManager.hpp>
#include <gsl/util>
#include <log/log.hpp>
#include <Utils.hpp>

namespace sys
{


@@ 28,10 29,19 @@ namespace sys
        return levelName;
    }

    [[nodiscard]] auto CpuFrequencyMonitor::GetRuntimePercentage() const noexcept -> std::uint32_t
    [[nodiscard]] auto CpuFrequencyMonitor::GetTotalRuntimePercentage(
        const TickType_t totalTicksIncrease) const noexcept -> std::uint32_t
    {
        auto tickCount = xTaskGetTickCount();
        return tickCount == 0 ? 0 : ((totalTicksCount * 100) / tickCount);
        return totalTicksIncrease == 0 ? 0 : ((totalTicksCount * 100) / totalTicksIncrease);
    }

    [[nodiscard]] auto CpuFrequencyMonitor::GetPeriodRuntimePercentage(
        const TickType_t periodTicksIncrease) const noexcept -> std::uint32_t
    {
        return periodTicksIncrease == 0
                   ? 0
                   : ((static_cast<std::uint64_t>(utils::computeIncrease(totalTicksCount, lastTotalTicksCount)) * 100) /
                      periodTicksIncrease);
    }

    void CpuFrequencyMonitor::IncreaseTicks(TickType_t ticks)


@@ 39,7 49,13 @@ namespace sys
        totalTicksCount += ticks;
    }

    PowerManager::PowerManager(CpuStatistics &stats) : powerProfile{bsp::getPowerProfile()}, cpuStatistics(stats)
    void CpuFrequencyMonitor::SavePeriodTicks()
    {
        lastTotalTicksCount = totalTicksCount;
    }

    PowerManager::PowerManager(CpuStatistics &cpuStats, TaskStatistics &taskStats)
        : powerProfile{bsp::getPowerProfile()}, cpuStatistics(cpuStats), taskStatistics(taskStats)
    {
        driverSEMC      = drivers::DriverSEMC::Create(drivers::name::ExternalRAM);
        lowPowerControl = bsp::LowPowerMode::Create().value_or(nullptr);


@@ 184,16 200,23 @@ namespace sys
        lastCpuFrequencyChangeTimestamp = ticks;
    }

    void PowerManager::LogPowerManagerEfficiency()
    void PowerManager::LogPowerManagerStatistics()
    {
        std::string log{"PowerManager Efficiency: "};
        const TickType_t tickCount          = xTaskGetTickCount();
        const TickType_t periodTickIncrease = tickCount - lastLogStatisticsTimestamp;
        UpdateCpuFrequencyMonitor(lowPowerControl->GetCurrentFrequencyLevel());

        std::string log{"last period (total): "};
        for (auto &level : cpuFrequencyMonitor) {
            log.append(level.GetName() + ": " + std::to_string(level.GetRuntimePercentage()) + "% ");
            log.append(level.GetName() + ": " + std::to_string(level.GetPeriodRuntimePercentage(periodTickIncrease)) +
                       "% (" + std::to_string(level.GetTotalRuntimePercentage(tickCount)) + "%) ");
            level.SavePeriodTicks();
        }

        lastLogStatisticsTimestamp = tickCount;
        LOG_INFO("%s", log.c_str());

        taskStatistics.Update();
        taskStatistics.LogCpuUsage();
    }

    void PowerManager::SetBootSuccess()

M module-sys/SystemManager/SystemManagerCommon.cpp => module-sys/SystemManager/SystemManagerCommon.cpp +7 -6
@@ 252,7 252,8 @@ namespace sys
    void SystemManagerCommon::StartSystem(InitFunction sysInit, InitFunction appSpaceInit, DeinitFunction sysDeinit)
    {
        cpuStatistics = std::make_unique<CpuStatistics>();
        powerManager  = std::make_unique<PowerManager>(*cpuStatistics);
        taskStatistics = std::make_unique<TaskStatistics>();
        powerManager   = std::make_unique<PowerManager>(*cpuStatistics, *taskStatistics);
        deviceManager = std::make_unique<DeviceManager>();

        systemInit   = std::move(sysInit);


@@ 265,11 266,11 @@ namespace sys
            this, "cpuTick", constants::timerInitInterval, [this](sys::Timer &) { FreqUpdateTick(); });
        freqTimer.start();

        powerManagerEfficiencyTimer = sys::TimerFactory::createPeriodicTimer(
            this, "logPowerManagerEfficiency", constants::powerManagerLogsTimerInterval, [this](sys::Timer &) {
                powerManager->LogPowerManagerEfficiency();
        powerManagerStatisticsTimer = sys::TimerFactory::createPeriodicTimer(
            this, "PowerManagerStatisticsTimer", constants::powerManagerLogsTimerInterval, [this](sys::Timer &) {
                powerManager->LogPowerManagerStatistics();
            });
        powerManagerEfficiencyTimer.start();
        powerManagerStatisticsTimer.start();
    }

    bool SystemManagerCommon::Restore(Service *s)


@@ 705,7 706,7 @@ namespace sys
        // In case if other power down request arrive in the meantime
        lowBatteryShutdownDelay.stop();
        freqTimer.stop();
        powerManagerEfficiencyTimer.stop();
        powerManagerStatisticsTimer.stop();

        // We are going to remove services in reversed order of creation
        CriticalSection::Enter();

A module-sys/SystemManager/TaskStatistics.cpp => module-sys/SystemManager/TaskStatistics.cpp +117 -0
@@ 0,0 1,117 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <SystemManager/TaskStatistics.hpp>
#include <log/log.hpp>
#include <semphr.h>
#include <Utils.hpp>
#include <algorithm>
#include <string>

namespace constants
{
    inline constexpr std::uint32_t cpuUsageThreshold{10};
    inline const std::string ignoredTaskName{"IDLE"};
} // namespace constants

namespace sys
{

    TaskStatistics::TaskStatistics() : totalSystemTick(0)
    {}

    void TaskStatistics::Update()
    {
        std::uint32_t currentSystemTick{0};
        const auto currentNumberOfTasks = uxTaskGetNumberOfTasks();

        TaskVector_t baseTasks;
        DeletedTaskVector_t delTasks;
        std::vector<TaskStatus_t> aliveTasks(currentNumberOfTasks);

        deletedTasks.MigrateDeletedTasks(delTasks);
        uxTaskGetSystemState(aliveTasks.data(), currentNumberOfTasks, &currentSystemTick);

        MergeDeletedTasks(baseTasks, delTasks);
        MergeAliveTasks(baseTasks, aliveTasks);
        MergeOldTasks(baseTasks, tasks);
        UpdateCpuUsage(baseTasks, utils::computeIncrease(currentSystemTick, totalSystemTick));
        ClearTasks(baseTasks);

        totalSystemTick = currentSystemTick;
    }

    void TaskStatistics::LogCpuUsage() const
    {
        for (auto &task : tasks) {
            if (!constants::ignoredTaskName.compare(task.name))
                continue;
            if (task.cpuUsage > constants::cpuUsageThreshold) {
                LOG_INFO("Task %s had %" PRIu32 "%% CPU usage in the last period.", task.name, task.cpuUsage);
            }
        }
    }

    void TaskStatistics::UpdateCpuUsage(TaskVector_t &baseTasks, const std::uint32_t systemTickIncrease)
    {
        for (auto &task : baseTasks) {
            task.cpuUsage = ComputePercentageCpuUsage(task.tickIncrements, systemTickIncrease);
        }
    }

    void TaskStatistics::MergeDeletedTasks(TaskVector_t &baseTasks, const DeletedTaskVector_t &tasksToMerge)
    {
        for (auto &task : tasksToMerge) {
            baseTasks.push_back({.name           = task.name,
                                 .isAlive        = false,
                                 .totalTick      = 0,
                                 .tickIncrements = task.tickIncrements,
                                 .cpuUsage       = 0});
        }
    }
    void TaskStatistics::MergeAliveTasks(TaskVector_t &baseTasks, const std::vector<TaskStatus_t> &tasksToMerge)
    {
        for (auto &task : tasksToMerge) {
            auto taskIt = std::find_if(
                std::begin(baseTasks), std::end(baseTasks), [task](auto &el) { return el.name == task.pcTaskName; });

            if (taskIt != std::end(baseTasks)) {
                taskIt->tickIncrements += task.ulRunTimeCounter;
                taskIt->totalTick = task.ulRunTimeCounter;
                taskIt->isAlive   = true;
            }
            else {
                baseTasks.push_back({.name           = task.pcTaskName,
                                     .isAlive        = true,
                                     .totalTick      = task.ulRunTimeCounter,
                                     .tickIncrements = task.ulRunTimeCounter,
                                     .cpuUsage       = 0});
            }
        }
    }
    void TaskStatistics::MergeOldTasks(TaskVector_t &baseTasks, const TaskVector_t &tasksToMerge)
    {
        for (auto &task : tasksToMerge) {
            if (!task.isAlive)
                continue;
            auto taskIt = std::find_if(
                std::begin(baseTasks), std::end(baseTasks), [task](auto &el) { return el.name == task.name; });

            if (taskIt != std::end(baseTasks)) {
                taskIt->tickIncrements -= task.totalTick;
            }
        }
    }
    void TaskStatistics::ClearTasks(TaskVector_t &baseTasks)
    {
        tasks.assign(baseTasks.begin(), baseTasks.end());
        baseTasks.clear();
    }

    [[nodiscard]] std::uint32_t TaskStatistics::ComputePercentageCpuUsage(const std::uint32_t taskTickIncrease,
                                                                          const std::uint32_t totalTickIncrease) const
    {
        return totalTickIncrease == 0u ? 0 : ((static_cast<std::uint64_t>(taskTickIncrease) * 100) / totalTickIncrease);
    }

} // namespace sys

M module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp => module-sys/SystemManager/include/SystemManager/CpuStatistics.hpp +0 -1
@@ 28,7 28,6 @@ namespace sys
        void UpdatePercentageCpuLoad();
        /// 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};

M module-sys/SystemManager/include/SystemManager/PowerManager.hpp => module-sys/SystemManager/include/SystemManager/PowerManager.hpp +12 -4
@@ 10,6 10,7 @@
#include "drivers/semc/DriverSEMC.hpp"
#include "SysCpuUpdateResult.hpp"
#include "CpuGovernor.hpp"
#include "TaskStatistics.hpp"
#include <bsp/lpm/PowerProfile.hpp>
#include <vector>



@@ 28,18 29,23 @@ namespace sys
        explicit CpuFrequencyMonitor(const std::string name);

        [[nodiscard]] auto GetName() const noexcept -> std::string;
        [[nodiscard]] auto GetRuntimePercentage() const noexcept -> std::uint32_t;
        [[nodiscard]] auto GetPeriodRuntimePercentage(const TickType_t periodTicksIncrease) const noexcept
            -> std::uint32_t;
        [[nodiscard]] auto GetTotalRuntimePercentage(const TickType_t totalTicksIncrease) const noexcept
            -> std::uint32_t;
        void IncreaseTicks(TickType_t ticks);
        void SavePeriodTicks();

      private:
        std::string levelName;
        std::uint64_t totalTicksCount{0};
        std::uint32_t totalTicksCount{0};
        std::uint32_t lastTotalTicksCount{0};
    };

    class PowerManager
    {
      public:
        explicit PowerManager(CpuStatistics &stats);
        explicit PowerManager(CpuStatistics &cpuStats, TaskStatistics &taskStats);
        ~PowerManager();

        int32_t PowerOff();


@@ 65,7 71,7 @@ namespace sys
        void SetPernamentFrequency(bsp::CpuFrequencyMHz freq);
        void ResetPernamentFrequency();

        void LogPowerManagerEfficiency();
        void LogPowerManagerStatistics();
        void SetBootSuccess();

      private:


@@ 75,6 81,7 @@ namespace sys
        void UpdateCpuFrequencyMonitor(bsp::CpuFrequencyMHz currentFreq);

        TickType_t lastCpuFrequencyChangeTimestamp{0};
        TickType_t lastLogStatisticsTimestamp{0};

        std::vector<CpuFrequencyMonitor> cpuFrequencyMonitor;



@@ 85,6 92,7 @@ namespace sys

        std::unique_ptr<sys::cpu::AlgorithmFactory> cpuAlgorithms;
        CpuStatistics &cpuStatistics;
        TaskStatistics &taskStatistics;
    };

} // namespace sys

M module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp => module-sys/SystemManager/include/SystemManager/SystemManagerCommon.hpp +3 -1
@@ 16,6 16,7 @@
#include <hal/key_input/RawKey.hpp>
#include <system/Constants.hpp>
#include "CpuStatistics.hpp"
#include "TaskStatistics.hpp"
#include "DeviceManager.hpp"
#include <chrono>
#include <vector>


@@ 201,7 202,7 @@ namespace sys
        sys::TimerHandle freqTimer;
        sys::TimerHandle serviceCloseTimer;
        sys::TimerHandle lowBatteryShutdownDelay;
        sys::TimerHandle powerManagerEfficiencyTimer;
        sys::TimerHandle powerManagerStatisticsTimer;
        InitFunction userInit;
        InitFunction systemInit;
        DeinitFunction systemDeinit;


@@ 214,6 215,7 @@ namespace sys
        static cpp_freertos::MutexStandard serviceDestroyMutex;
        static cpp_freertos::MutexStandard appDestroyMutex;
        std::unique_ptr<CpuStatistics> cpuStatistics;
        std::unique_ptr<TaskStatistics> taskStatistics;
        std::unique_ptr<PowerManager> powerManager;
        std::unique_ptr<DeviceManager> deviceManager;
    };

A module-sys/SystemManager/include/SystemManager/TaskStatistics.hpp => module-sys/SystemManager/include/SystemManager/TaskStatistics.hpp +51 -0
@@ 0,0 1,51 @@
// 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 <FreeRTOS.h>
#include <task.h>
#include <vector>
#include <cstdint>
#include <DeletedTasks.hpp>

namespace sys
{

    class TaskStatistics
    {

      public:
        TaskStatistics();

        /// update the total running time allocated so far for all tasks
        void Update();
        /// print the percentage of CPU usage in the last period
        void LogCpuUsage() const;

      protected:
        struct TaskDetails_t
        {
            const char *name;             // A pointer to the task's name
            bool isAlive;                 // Flag with information if task exists
            std::uint32_t totalTick;      // The total run time allocated to the task so far
            std::uint32_t tickIncrements; // run time allocated to the task for the last period
            std::uint32_t cpuUsage;       // Percentage of CPU usage for the last period
        };
        using TaskVector_t        = std::vector<TaskDetails_t>;
        using DeletedTaskVector_t = std::vector<DeletedTasks::DeletedTaskDetails_t>;

        TaskVector_t tasks;
        std::uint32_t totalSystemTick;
        DeletedTasks deletedTasks;

        [[nodiscard]] std::uint32_t ComputePercentageCpuUsage(const std::uint32_t taskTickIncrease,
                                                              const std::uint32_t totalTickIncrease) const;
        void UpdateCpuUsage(TaskVector_t &baseTasks, const std::uint32_t systemTickIncrease);
        void MergeDeletedTasks(TaskVector_t &baseTasks, const DeletedTaskVector_t &tasksToMerge);
        void MergeAliveTasks(TaskVector_t &baseTasks, const std::vector<TaskStatus_t> &tasksToMerge);
        void MergeOldTasks(TaskVector_t &baseTasks, const TaskVector_t &tasksToMerge);
        void ClearTasks(TaskVector_t &baseTasks);
    };

} // namespace sys

M module-sys/SystemManager/tests/CMakeLists.txt => module-sys/SystemManager/tests/CMakeLists.txt +9 -0
@@ 24,3 24,12 @@ add_catch2_executable(
    LIBS
        module-sys
)

add_catch2_executable(
    NAME
        task-statistics
    SRCS
        test-taskStatistics.cpp
    LIBS
        module-sys
)

A module-sys/SystemManager/tests/test-taskStatistics.cpp => module-sys/SystemManager/tests/test-taskStatistics.cpp +133 -0
@@ 0,0 1,133 @@
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>

#include "SystemManager/TaskStatistics.hpp"

namespace mockup
{
    class TaskStat : public sys::TaskStatistics
    {
      public:
        using TaskVector_t        = std::vector<TaskDetails_t>;
        using DeletedTaskVector_t = std::vector<sys::DeletedTasks::DeletedTaskDetails_t>;

        [[nodiscard]] auto GetData() const noexcept -> TaskVector_t
        {
            return sys::TaskStatistics::tasks;
        }
        [[nodiscard]] auto GetPercentageCpuUsage(const std::uint32_t taskTickIncrease,
                                                 const std::uint32_t totalTickIncrease) const noexcept -> std::uint32_t
        {
            return ComputePercentageCpuUsage(taskTickIncrease, totalTickIncrease);
        }
        void mergeDeletedTasks(TaskVector_t &baseTasks, const DeletedTaskVector_t &tasksToMerge)
        {
            MergeDeletedTasks(baseTasks, tasksToMerge);
        }
        void mergeAliveTasks(TaskVector_t &baseTasks, const std::vector<TaskStatus_t> &tasksToMerge)
        {
            MergeAliveTasks(baseTasks, tasksToMerge);
        }
        void mergeOldTasks(TaskVector_t &baseTasks, const TaskVector_t &tasksToMerge)
        {
            MergeOldTasks(baseTasks, tasksToMerge);
        }
        void clearTasks(TaskVector_t &baseTasks)
        {
            ClearTasks(baseTasks);
        }
    };
} // namespace mockup

TEST_CASE("CpuUsage")
{
    auto taskStat = std::make_unique<mockup::TaskStat>();
    SECTION("positive")
    {
        const auto data = taskStat->GetPercentageCpuUsage(100, 1000);
        REQUIRE(data == 10);
    }
    SECTION("null")
    {
        const auto data = taskStat->GetPercentageCpuUsage(100, 0);
        REQUIRE(data == 0);
    }
    SECTION("overflow")
    {
        const auto data = taskStat->GetPercentageCpuUsage(50000000, 100000000);
        REQUIRE(data == 50);
    }
}

TEST_CASE("Tick Increments")
{
    auto taskStat = std::make_unique<mockup::TaskStat>();
    mockup::TaskStat::TaskVector_t baseTasks;
    mockup::TaskStat::DeletedTaskVector_t deletedTasks;
    std::vector<TaskStatus_t> aliveTasks;
    mockup::TaskStat::TaskVector_t oldTasks;

    oldTasks.push_back({.name = "test_1", .isAlive = true, .totalTick = 1000, .tickIncrements = 100, .cpuUsage = 0});
    oldTasks.push_back({.name = "test_2", .isAlive = true, .totalTick = 1100, .tickIncrements = 200, .cpuUsage = 0});
    oldTasks.push_back({.name = "test_3", .isAlive = false, .totalTick = 1200, .tickIncrements = 300, .cpuUsage = 0});
    oldTasks.push_back({.name = "test_4", .isAlive = false, .totalTick = 1300, .tickIncrements = 400, .cpuUsage = 0});
    oldTasks.push_back({.name = "test_5", .isAlive = true, .totalTick = 1400, .tickIncrements = 500, .cpuUsage = 0});
    oldTasks.push_back({.name = "test_6", .isAlive = false, .totalTick = 1500, .tickIncrements = 600, .cpuUsage = 0});

    aliveTasks.push_back({.xHandle              = NULL,
                          .pcTaskName           = "test_2",
                          .xTaskNumber          = 0,
                          .eCurrentState        = eTaskState::eSuspended,
                          .uxCurrentPriority    = 0,
                          .uxBasePriority       = 0,
                          .ulRunTimeCounter     = 1000,
                          .pxStackBase          = nullptr,
                          .usStackHighWaterMark = 0});
    aliveTasks.push_back({.xHandle              = NULL,
                          .pcTaskName           = "test_4",
                          .xTaskNumber          = 0,
                          .eCurrentState        = eTaskState::eSuspended,
                          .uxCurrentPriority    = 0,
                          .uxBasePriority       = 0,
                          .ulRunTimeCounter     = 1500,
                          .pxStackBase          = nullptr,
                          .usStackHighWaterMark = 0});
    aliveTasks.push_back({.xHandle              = NULL,
                          .pcTaskName           = "test_5",
                          .xTaskNumber          = 0,
                          .eCurrentState        = eTaskState::eSuspended,
                          .uxCurrentPriority    = 0,
                          .uxBasePriority       = 0,
                          .ulRunTimeCounter     = 2000,
                          .pxStackBase          = nullptr,
                          .usStackHighWaterMark = 0});
    aliveTasks.push_back({.xHandle              = NULL,
                          .pcTaskName           = "test_6",
                          .xTaskNumber          = 0,
                          .eCurrentState        = eTaskState::eSuspended,
                          .uxCurrentPriority    = 0,
                          .uxBasePriority       = 0,
                          .ulRunTimeCounter     = 2500,
                          .pxStackBase          = nullptr,
                          .usStackHighWaterMark = 0});

    deletedTasks.push_back({.name = "test_1", .tickIncrements = 1200});
    deletedTasks.push_back({.name = "test_2", .tickIncrements = 400});
    deletedTasks.push_back({.name = "test_3", .tickIncrements = 600});
    deletedTasks.push_back({.name = "test_4", .tickIncrements = 800});

    taskStat->mergeDeletedTasks(baseTasks, deletedTasks);
    taskStat->mergeAliveTasks(baseTasks, aliveTasks);
    taskStat->mergeOldTasks(baseTasks, oldTasks);
    taskStat->clearTasks(baseTasks);
    auto data = taskStat->GetData();

    REQUIRE(data[0].tickIncrements == 200);
    REQUIRE(data[1].tickIncrements == 300);
    REQUIRE(data[2].tickIncrements == 600);
    REQUIRE(data[3].tickIncrements == 2300);
    REQUIRE(data[4].tickIncrements == 600);
    REQUIRE(data[5].tickIncrements == 2500);
}

M module-utils/utility/Utils.hpp => module-utils/utility/Utils.hpp +11 -0
@@ 259,6 259,17 @@ namespace utils
        }
    }

    template <class T>
    [[nodiscard]] inline T computeIncrease(const T currentCount, const T lastCount)
    {
        if (currentCount >= lastCount) {
            return currentCount - lastCount;
        }
        else {
            return std::numeric_limits<T>::max() - lastCount + currentCount;
        }
    }

    static inline void findAndReplaceAll(std::string &data,
                                         const std::vector<std::pair<std::string, std::optional<std::string>>> &values,
                                         std::function<std::string(int)> getReplaceString = nullptr)

M module-utils/utility/tests/unittest_utils.cpp => module-utils/utility/tests/unittest_utils.cpp +16 -0
@@ 625,3 625,19 @@ TEST_CASE("Generate random Id")
        REQUIRE((ret.size() == expectedSize));
    }
}

TEST_CASE("Compute increase")
{
    SECTION("positive")
    {
        const std::uint64_t a{2500}, b{2000};
        const auto data = utils::computeIncrease(a, b);
        REQUIRE(data == 500);
    }
    SECTION("overfow")
    {
        const std::uint32_t a{500}, b{0xFFFFFFFF - 500};
        const auto data = utils::computeIncrease(a, b);
        REQUIRE(data == 1000);
    }
}

M pure_changelog.md => pure_changelog.md +1 -0
@@ 38,6 38,7 @@
### Added
* Added tethering information on the status bar
* Added basic MMS handling
* Added run-time statistics for tasks

## [1.3.0 2022-08-04]