diff --git a/CMakeLists.txt b/CMakeLists.txt index c68151f..c692cd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ set(headers src/lockhelper.h src/lockingqueue.h src/recursivelockhelper.h + src/schedulertask.h src/taskutils.h src/tickchrono.h src/wrappers/binary_semaphore.h @@ -22,6 +23,7 @@ set(headers set(sources src/espstrutils.cpp + src/schedulertask.cpp src/taskutils.cpp ) diff --git a/Kconfig.projbuild b/Kconfig.projbuild new file mode 100644 index 0000000..36a4ebe --- /dev/null +++ b/Kconfig.projbuild @@ -0,0 +1,40 @@ +menu "espcpputils settings" + +choice ESPCPPUTILS_LOG_LOCAL_LEVEL + bool "espcpputils log verbosity" + default ESPCPPUTILS_LOG_LOCAL_LEVEL_INFO + help + Specify how much output to compile into the binary. + You can set lower verbosity level at runtime using + esp_log_level_set function. + + Note that this setting limits which log statements + are compiled into the program in go-e sources. So + setting this to, say, "Warning" would mean that + changing log level to "Debug" at runtime will not + be possible. + + config ESPCPPUTILS_LOG_LOCAL_LEVEL_NONE + bool "No output" + config ESPCPPUTILS_LOG_LOCAL_LEVEL_ERROR + bool "Error" + config ESPCPPUTILS_LOG_LOCAL_LEVEL_WARN + bool "Warning" + config ESPCPPUTILS_LOG_LOCAL_LEVEL_INFO + bool "Info" + config ESPCPPUTILS_LOG_LOCAL_LEVEL_DEBUG + bool "Debug" + config ESPCPPUTILS_LOG_LOCAL_LEVEL_VERBOSE + bool "Verbose" +endchoice + +config ESPCPPUTILS_LOG_LOCAL_LEVEL + int + default 0 if ESPCPPUTILS_LOG_LOCAL_LEVEL_NONE + default 1 if ESPCPPUTILS_LOG_LOCAL_LEVEL_ERROR + default 2 if ESPCPPUTILS_LOG_LOCAL_LEVEL_WARN + default 3 if ESPCPPUTILS_LOG_LOCAL_LEVEL_INFO + default 4 if ESPCPPUTILS_LOG_LOCAL_LEVEL_DEBUG + default 5 if ESPCPPUTILS_LOG_LOCAL_LEVEL_VERBOSE + +endmenu diff --git a/src/espstrutils.cpp b/src/espstrutils.cpp index f185b80..f2bd211 100644 --- a/src/espstrutils.cpp +++ b/src/espstrutils.cpp @@ -1,5 +1,8 @@ #include "espstrutils.h" +#include "sdkconfig.h" +#define LOG_LOCAL_LEVEL CONFIG_ESPCPPUTILS_LOG_LOCAL_LEVEL + // esp-idf includes #include diff --git a/src/schedulertask.cpp b/src/schedulertask.cpp new file mode 100644 index 0000000..f35244f --- /dev/null +++ b/src/schedulertask.cpp @@ -0,0 +1,83 @@ +#include "schedulertask.h" + +#include "sdkconfig.h" +#define LOG_LOCAL_LEVEL CONFIG_ESPCPPUTILS_LOG_LOCAL_LEVEL + +// esp-idf includes +#include +#include + +using namespace std::chrono_literals; + +namespace espcpputils { +namespace { +constexpr const char * const TAG = "ESPCPPUTILS"; +} // namespace + +SchedulerTask::SchedulerTask(const char *name, void (&setupCallback)(), void (&loopCallback)(), std::chrono::milliseconds loopInterval, bool intervalImportant, std::string (*perfInfo)()) : + m_name{name}, m_setupCallback{setupCallback}, m_loopCallback{loopCallback}, m_loopInterval{loopInterval}, m_intervalImportant{intervalImportant}, m_perfInfo{perfInfo} +{ +} + +void SchedulerTask::loop() +{ + if (m_lastUpdate && espchrono::ago(*m_lastUpdate) < m_loopInterval) + return; + + ESP_LOGV(TAG, "start %s", m_name); + + if (m_intervalImportant) + { + if (!m_lastUpdate) + goto trotzdem; + *m_lastUpdate += m_loopInterval; + if (espchrono::ago(*m_lastUpdate) > m_loopInterval/2) + goto trotzdem; + } + else +trotzdem: + m_lastUpdate = espchrono::millis_clock::now(); + + const auto timeBefore = espchrono::millis_clock::now(); + m_loopCallback(); + const auto timeAfter = espchrono::millis_clock::now(); + + ESP_LOGV(TAG, "end %s", m_name); + + m_callCountTemp++; + + m_lastElapsed = timeAfter-timeBefore; + m_totalElapsedTemp += m_lastElapsed; + if (m_lastElapsed > m_maxElapsedTemp) + m_maxElapsedTemp = m_lastElapsed; + + if (m_lastElapsed < 100ms) + ESP_LOGV(TAG, "task %s hang for %lldms (heap8=%zd)", + m_name, std::chrono::milliseconds{m_lastElapsed}.count(), heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT)); + else + ESP_LOGW(TAG, "task %s hang for %lldms (heap8=%zd) %s", + m_name, std::chrono::milliseconds{m_lastElapsed}.count(), heap_caps_get_free_size(MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT), + m_perfInfo ? m_perfInfo().c_str() : ""); +} + +void SchedulerTask::pushStats(bool printTask) +{ + m_callCount = m_callCountTemp; + m_totalElapsed = m_totalElapsedTemp; + m_averageElapsed = m_callCount > 0 ? (m_totalElapsed / m_callCount) : std::chrono::milliseconds{}; + m_maxElapsed = m_maxElapsedTemp; + + m_callCountTemp = 0; + m_totalElapsedTemp = {}; + m_maxElapsedTemp = {}; + + if (printTask) + ESP_LOGI(TAG, "name=%s, count=%i, last=%lldms, total=%lldms, max=%lldms", + m_name, m_callCount, + std::chrono::milliseconds{m_lastElapsed}.count(), + std::chrono::milliseconds{m_totalElapsed}.count(), + std::chrono::milliseconds{m_maxElapsed}.count() + ); +} + +} // namespace espcpputils diff --git a/src/schedulertask.h b/src/schedulertask.h new file mode 100644 index 0000000..00c61fd --- /dev/null +++ b/src/schedulertask.h @@ -0,0 +1,51 @@ +#pragma once + +// system includes +#include +#include +#include + +// local includes +#include "espchrono.h" +#include "arrayview.h" + +namespace espcpputils { +class SchedulerTask +{ +public: + SchedulerTask(const char *name, void (&setupCallback)(), void (&loopCallback)(), + std::chrono::milliseconds loopInterval, bool intervalImportant = false, + std::string (*perfInfo)() = nullptr); + + const char *name() const { return m_name; } + const std::chrono::milliseconds &lastElapsed() const { return m_lastElapsed; } + const std::chrono::milliseconds &averageElapsed() const { return m_averageElapsed; } + const std::chrono::milliseconds &maxElapsed() const { return m_maxElapsed; } + int callCount() const { return m_callCount; } + + void setup() const { m_setupCallback(); } + void loop(); + void pushStats(bool printTask); + +private: + const char * const m_name; + void (&m_setupCallback)(); + void (&m_loopCallback)(); + const std::chrono::milliseconds m_loopInterval; + const bool m_intervalImportant; + std::string (* const m_perfInfo)(); + + std::optional m_lastUpdate; + + std::chrono::milliseconds m_lastElapsed; + + std::chrono::milliseconds m_totalElapsed; + std::chrono::milliseconds m_averageElapsed; + std::chrono::milliseconds m_maxElapsed; + int m_callCount{}; + + std::chrono::milliseconds m_totalElapsedTemp; + std::chrono::milliseconds m_maxElapsedTemp; + int m_callCountTemp{}; +}; +} // namespace espcpputils diff --git a/src/taskutils.cpp b/src/taskutils.cpp index 74aec07..2549296 100644 --- a/src/taskutils.cpp +++ b/src/taskutils.cpp @@ -1,6 +1,19 @@ #include "taskutils.h" +#include "sdkconfig.h" +#define LOG_LOCAL_LEVEL CONFIG_ESPCPPUTILS_LOG_LOCAL_LEVEL + +// esp-idf includes +#include + +// local includes +#include "futurecpp.h" + namespace espcpputils { +namespace { +constexpr const char * const TAG = "ESPCPPUTILS"; +} // namespace + BaseType_t createTask(TaskFunction_t pvTaskCode, const char * const pcName, const uint32_t usStackDepth, @@ -13,11 +26,22 @@ BaseType_t createTask(TaskFunction_t pvTaskCode, { case CoreAffinity::Core0: case CoreAffinity::Core1: - return xTaskCreatePinnedToCore(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask, int(coreAffinity)); + { + const auto result = xTaskCreatePinnedToCore(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask, int(coreAffinity)); + if (result != pdPASS) + ESP_LOGW(TAG, "xTaskCreatePinnedToCore() %s failed with %i", pcName, result); + return result; + } case CoreAffinity::Both: - return xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask); + { + const auto result = xTaskCreate(pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask); + if (result != pdPASS) + ESP_LOGW(TAG, "xTaskCreate() %s failed with %i", pcName, result); + return result; + } default: - __builtin_unreachable(); + ESP_LOGE(TAG, "unknown coreAffinity %i", std::to_underlying(coreAffinity)); + return pdFAIL; } }