From a83f067af9af82f034596f6f6154f4dccf598ace Mon Sep 17 00:00:00 2001 From: JF Date: Thu, 2 Jul 2020 21:38:52 +0200 Subject: Reduce RAM memory usage by tuning the stack of the stasks and the heap allocated for FreeRTOS. Add Monitor to log the stack usage of each task. --- src/SystemTask/SystemMonitor.h | 46 ++++++++++++++++++++++++++++++++++++++++++ src/SystemTask/SystemTask.cpp | 2 ++ src/SystemTask/SystemTask.h | 7 +++++++ 3 files changed, 55 insertions(+) create mode 100644 src/SystemTask/SystemMonitor.h (limited to 'src/SystemTask') diff --git a/src/SystemTask/SystemMonitor.h b/src/SystemTask/SystemMonitor.h new file mode 100644 index 00000000..50dd295a --- /dev/null +++ b/src/SystemTask/SystemMonitor.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include + + +namespace Pinetime { + namespace System { + struct DummyMonitor {}; + struct FreeRtosMonitor {}; + + template + class SystemMonitor { + public: + SystemMonitor() = delete; + }; + + template<> + class SystemMonitor { + public: + void Process() const {} + }; + + template<> + class SystemMonitor { + public: + void Process() const { + if(xTaskGetTickCount() - lastTick > 10000) { + NRF_LOG_INFO("---------------------------------------\nFee heap : %d", xPortGetFreeHeapSize()); + auto nb = uxTaskGetSystemState(tasksStatus, 10, NULL); + for (int i = 0; i < nb; i++) { + NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark); + if (tasksStatus[i].usStackHighWaterMark < 20) + NRF_LOG_INFO("WARNING!!! Task %s task is nearly full, only %dB available", tasksStatus[i].pcTaskName, + tasksStatus[i].usStackHighWaterMark * 4); + } + lastTick = xTaskGetTickCount(); + } + } + + private: + mutable TickType_t lastTick = 0; + mutable TaskStatus_t tasksStatus[10]; + }; + } +} \ No newline at end of file diff --git a/src/SystemTask/SystemTask.cpp b/src/SystemTask/SystemTask.cpp index 61b3c638..1f01b1b5 100644 --- a/src/SystemTask/SystemTask.cpp +++ b/src/SystemTask/SystemTask.cpp @@ -169,6 +169,8 @@ void SystemTask::Work() { dateTimeController.UpdateTime(systick_counter); batteryController.Update(); + monitor.Process(); + if(!nrf_gpio_pin_read(pinButton)) watchdog.Kick(); } diff --git a/src/SystemTask/SystemTask.h b/src/SystemTask/SystemTask.h index ab5f7010..e006058f 100644 --- a/src/SystemTask/SystemTask.h +++ b/src/SystemTask/SystemTask.h @@ -10,6 +10,7 @@ #include #include #include +#include "SystemMonitor.h" namespace Pinetime { namespace System { @@ -72,6 +73,12 @@ namespace Pinetime { bool doNotGoToSleep = false; void GoToRunning(); + +#if configUSE_TRACE_FACILITY == 1 + SystemMonitor monitor; +#else + SystemMonitor monitor; +#endif }; } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From ca549bcfd86ea3426a74292abbe2e4bd1303cb75 Mon Sep 17 00:00:00 2001 From: JF Date: Fri, 3 Jul 2020 14:45:01 +0200 Subject: Add doc about memory usage analysis. --- doc/MemoryAnalysis.md | 75 ++++++++++++++++++++++++++++++++++++++++++ src/CMakeLists.txt | 2 ++ src/SystemTask/SystemMonitor.h | 2 +- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 doc/MemoryAnalysis.md (limited to 'src/SystemTask') diff --git a/doc/MemoryAnalysis.md b/doc/MemoryAnalysis.md new file mode 100644 index 00000000..2e20cae6 --- /dev/null +++ b/doc/MemoryAnalysis.md @@ -0,0 +1,75 @@ +# Memory analysis +## FreeRTOS heap and task stack +FreeRTOS statically allocate its own heap buffer in a global variable named `ucHeap`. This is an aray of *uint8_t*. Its size is specified by the definition `configTOTAL_HEAP_SIZE` in *FreeRTOSConfig.h* +FreeRTOS uses this buffer to allocate memory for tasks stack and all the RTOS object created during runtime (timers, mutexes,...). + +The function `xPortGetFreeHeapSize()` returns the amount of memory available in this *ucHeap* buffer. If this value reaches 0, FreeRTOS runs out of memory. + +``` +NRF_LOG_INFO("Free heap : %d", xPortGetFreeHeapSize()); +``` + + +The function `uxTaskGetSystemState()` fetches some information about the running tasks like its name and the minimum amount of stack space that has remained for the task since the task was created: + +``` +TaskStatus_t tasksStatus[10] +auto nb = uxTaskGetSystemState(tasksStatus, 10, NULL); +for (int i = 0; i < nb; i++) { + NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark); +``` + + +## Global heap +Heap is used for **dynamic memory allocation (malloc() / new)**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__HEAP_SIZE=8192` in *src/CMakeLists.txt*: + +``` +add_definitions(-D__HEAP_SIZE=8192) +``` + +You can trace the dynamic memory allocation by using the flag `--wrap` of the linker. When this flag is enabled, the linker will replace the calls to a specific function by a call to __wrap_the_function(). For example, if you specify `-Wl,-wrap,malloc` in the linker flags, the linker will replace all calls to `void* malloc(size_t)` by calls to `void* __wrap_malloc(size_t)`. This is a function you'll have to define in your code. In this function, you can call `__real_malloc()` to call the actual `malloc()' function. + +This technic allows you to wrap all calls to malloc() with you own code. + +In *src/CMakeLists.txt*: + +``` +set_target_properties(${EXECUTABLE_NAME} PROPERTIES + ... + LINK_FLAGS "-Wl,-wrap,malloc ..." + ... + ) + +``` + +In *main.cpp*: + +``` +uint32_t totalMalloc = 0; +extern "C" { + extern void* __real_malloc(size_t s); + void *__wrap_malloc(size_t s) { + totalMalloc += s; + return __real_malloc(s); + } +} +``` +This function sums all the memory that is allocated during the runtime. You can monitor or log this value. You can also place breakpoints in this function to determine where the dynamic memory allocation occurs in your code. + + +# Global stack +The stack is used to allocate memory used by functions : **parameters and local variables**. NRF SDK defaults the heap size to 8KB. The size of the heap can be specified by defining `__STACK_SIZE=8192` in *src/CMakeLists.txt*: + +``` +add_definitions(-D__STACK_SIZE=8192) +``` + +*NOTE*: FreeRTOS uses its own stack buffer. Thus, the global stack is only used for main() and IRQ handlers. It should be possible to reduce its size to a much lower value. + +**NOTE**: [?] How to track the global stack usage? + +#LittleVGL buffer +*TODO* + +#NimBLE buffers +*TODO* \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b39d2b5..776bd637 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -548,6 +548,8 @@ add_definitions(-DOS_CPUTIME_FREQ) add_definitions(-DNRF52 -DNRF52832 -DNRF52832_XXAA -DNRF52_PAN_74 -DNRF52_PAN_64 -DNRF52_PAN_12 -DNRF52_PAN_58 -DNRF52_PAN_54 -DNRF52_PAN_31 -DNRF52_PAN_51 -DNRF52_PAN_36 -DNRF52_PAN_15 -DNRF52_PAN_20 -DNRF52_PAN_55 -DBOARD_PCA10040) add_definitions(-DFREERTOS) add_definitions(-DDEBUG_NRF_USER) +add_definitions(-D__STACK_SIZE=8192) +add_definitions(-D__HEAP_SIZE=8192) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") diff --git a/src/SystemTask/SystemMonitor.h b/src/SystemTask/SystemMonitor.h index 50dd295a..8fcfafb2 100644 --- a/src/SystemTask/SystemMonitor.h +++ b/src/SystemTask/SystemMonitor.h @@ -26,7 +26,7 @@ namespace Pinetime { public: void Process() const { if(xTaskGetTickCount() - lastTick > 10000) { - NRF_LOG_INFO("---------------------------------------\nFee heap : %d", xPortGetFreeHeapSize()); + NRF_LOG_INFO("---------------------------------------\nFree heap : %d", xPortGetFreeHeapSize()); auto nb = uxTaskGetSystemState(tasksStatus, 10, NULL); for (int i = 0; i < nb; i++) { NRF_LOG_INFO("Task [%s] - %d", tasksStatus[i].pcTaskName, tasksStatus[i].usStackHighWaterMark); -- cgit v1.2.3-70-g09d2 From 62de76ba99f28eccbe72bbf132ae64cd05eaf6b6 Mon Sep 17 00:00:00 2001 From: JF Date: Sat, 4 Jul 2020 18:10:30 +0200 Subject: Automatically switch the display on when receiving a notification --- src/SystemTask/SystemTask.cpp | 15 ++++++++++----- src/SystemTask/SystemTask.h | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'src/SystemTask') diff --git a/src/SystemTask/SystemTask.cpp b/src/SystemTask/SystemTask.cpp index 1f01b1b5..39e9751b 100644 --- a/src/SystemTask/SystemTask.cpp +++ b/src/SystemTask/SystemTask.cpp @@ -120,15 +120,15 @@ void SystemTask::Work() { isSleeping = true; break; case Messages::OnNewTime: - xTimerReset(idleTimer, 0); + ReloadIdleTimer(); displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::UpdateDateTime); break; case Messages::OnNewNotification: - xTimerReset(idleTimer, 0); + if(isSleeping) GoToRunning(); displayApp->PushMessage(Pinetime::Applications::DisplayApp::Messages::NewNotification); break; case Messages::BleConnected: - xTimerReset(idleTimer, 0); + ReloadIdleTimer(); isBleDiscoveryTimerRunning = true; bleDiscoveryTimer = 5; break; @@ -145,10 +145,10 @@ void SystemTask::Work() { NVIC_SystemReset(); break; case Messages::OnTouchEvent: - xTimerReset(idleTimer, 0); + ReloadIdleTimer(); break; case Messages::OnButtonEvent: - xTimerReset(idleTimer, 0); + ReloadIdleTimer(); break; default: break; } @@ -217,3 +217,8 @@ void SystemTask::OnIdle() { NRF_LOG_INFO("Idle timeout -> Going to sleep") PushMessage(Messages::GoToSleep); } + +void SystemTask::ReloadIdleTimer() const { + if(isSleeping) return; + xTimerReset(idleTimer, 0); +} diff --git a/src/SystemTask/SystemTask.h b/src/SystemTask/SystemTask.h index e006058f..860647a1 100644 --- a/src/SystemTask/SystemTask.h +++ b/src/SystemTask/SystemTask.h @@ -66,6 +66,7 @@ namespace Pinetime { static void Process(void* instance); void Work(); + void ReloadIdleTimer() const; bool isBleDiscoveryTimerRunning = false; uint8_t bleDiscoveryTimer = 0; static constexpr uint32_t idleTime = 15000; -- cgit v1.2.3-70-g09d2