From 95d82daa61c9f862bfb1e1d13797268213a6b5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChristy?= <“[christy.guirguis@ucalgary.ca]”> Date: Thu, 29 Jan 2026 21:14:46 -0700 Subject: [PATCH 1/6] test profiler function created --- Profiler.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ Profiler.hpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 Profiler.cpp create mode 100644 Profiler.hpp diff --git a/Profiler.cpp b/Profiler.cpp new file mode 100644 index 0000000..30e92b8 --- /dev/null +++ b/Profiler.cpp @@ -0,0 +1,43 @@ +/** + ******************************************************************************** + * @file Profiler.cpp + * @author Christy + * @date Jan 28, 2026 + * @brief + ******************************************************************************** + */ + +/************************************ + * INCLUDES + ************************************/ +#include "Profiler.hpp" +#include "SystemDefines.hpp" +/************************************ + * PRIVATE MACROS AND DEFINES + ************************************/ + +/************************************ + * VARIABLES + ************************************/ + +/************************************ + * FUNCTION DECLARATIONS + ************************************/ + +/************************************ + * FUNCTION DEFINITIONS + ************************************/ + + +void TestFunctions() { + + char buffer[512]; + + SOAR_PRINT("\nName State Prio Stack Num\n"); + SOAR_PRINT("-------------------------------------\n"); + + vTaskList(buffer); + + SOAR_PRINT("%s\n", buffer); + return; +} \ No newline at end of file diff --git a/Profiler.hpp b/Profiler.hpp new file mode 100644 index 0000000..e477842 --- /dev/null +++ b/Profiler.hpp @@ -0,0 +1,33 @@ +/** + ******************************************************************************** + * @file Profiler.hpp + * @author Christy + * @date Jan 28, 2026 + * @brief + ******************************************************************************** + */ + +#ifndef PROFILER_HPP_ +#define PROFILER_HPP_ + +/************************************ + * INCLUDES + ************************************/ + +/************************************ + * MACROS AND DEFINES + ************************************/ + +/************************************ + * TYPEDEFS + ************************************/ + +/************************************ + * CLASS DEFINITIONS + ************************************/ + +/************************************ + * FUNCTION DECLARATIONS + ************************************/ +void TestFunctions(); +#endif /* PROFILER_HPP_ */ From f652d8aaf09f61bf3ae7305647926d6e7e66f031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChristy?= <“[christy.guirguis@ucalgary.ca]”> Date: Sat, 31 Jan 2026 13:49:21 -0700 Subject: [PATCH 2/6] profiling messages made --- Profiler.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++++----- Profiler.hpp | 8 ++++-- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/Profiler.cpp b/Profiler.cpp index 30e92b8..7907719 100644 --- a/Profiler.cpp +++ b/Profiler.cpp @@ -12,6 +12,10 @@ ************************************/ #include "Profiler.hpp" #include "SystemDefines.hpp" +#include +#include +#include + /************************************ * PRIVATE MACROS AND DEFINES ************************************/ @@ -19,7 +23,7 @@ /************************************ * VARIABLES ************************************/ - +std::vector TaskNames; /************************************ * FUNCTION DECLARATIONS ************************************/ @@ -28,16 +32,74 @@ * FUNCTION DEFINITIONS ************************************/ - -void TestFunctions() { +void PrintTaskList() { + // print list of tasks + SOAR_PRINT("\n\nName State Priority Stack Num\r\n"); + SOAR_PRINT("**********************************************\r\n"); char buffer[512]; + vTaskList(buffer); + SOAR_PRINT("%s\n", buffer); - SOAR_PRINT("\nName State Prio Stack Num\n"); - SOAR_PRINT("-------------------------------------\n"); + // populate global vector of task names + TaskNames.clear(); + + // parse output and take first entry as task name + char* line = strtok(buffer, "\n"); + while (line != NULL) { + char taskName[configMAX_TASK_NAME_LEN]; + if (sscanf(line, "%s", taskName) == 1) { // scan first entry splitting on whitespace in line + TaskNames.push_back(std::string(taskName)); // append to global vector + } + line = strtok(NULL, "\n"); + } + return; +} - vTaskList(buffer); +void PrintCPUStats() { + // print runtime stats using vTaskGetRunTimeStats + SOAR_PRINT("\n\nCPU Runtime Stats\r\n"); + SOAR_PRINT("**********************************************\r\n"); + char buffer[512]; + vTaskGetRunTimeStats(buffer); SOAR_PRINT("%s\n", buffer); - return; +} + + +void PrintHeap() { + // print heap stats using xPortGetFreeHeapSize and xPortGetMinimumEverFreeHeapSize + SOAR_PRINT("\n\nHeap Stats\r\n"); + SOAR_PRINT("**********************************************\r\n"); + SOAR_PRINT("Current System Free Heap: %d Bytes\r\n", xPortGetFreeHeapSize()); + SOAR_PRINT("Lowest Ever Free Heap: %d Bytes\r\n", xPortGetMinimumEverFreeHeapSize()); +} + + +void PrintStack() { + SOAR_PRINT("\n\nStack Remaining per Task\r\n"); + SOAR_PRINT("*************************\r\n"); + + // iterate through global task name vector + for (size_t i = 0; i < TaskNames.size(); ++i) { + std::string& taskName = TaskNames[i]; + + // get task handle for each task name + TaskHandle_t handle = xTaskGetHandle(taskName.c_str()); + if (handle != NULL) { + // print high watermark attribute for each task + UBaseType_t highWater = uxTaskGetStackHighWaterMark(handle); + SOAR_PRINT("%-12s : %lu words\r\n", taskName.c_str(), (unsigned long)highWater); + } + } +} + + +void ProfileSystem() { + // call methods to print profile messages + SOAR_PRINT("\r\n"); + PrintTaskList(); + PrintCPUStats(); + PrintHeap(); + PrintStack(); } \ No newline at end of file diff --git a/Profiler.hpp b/Profiler.hpp index e477842..9e120b5 100644 --- a/Profiler.hpp +++ b/Profiler.hpp @@ -29,5 +29,9 @@ /************************************ * FUNCTION DECLARATIONS ************************************/ -void TestFunctions(); -#endif /* PROFILER_HPP_ */ +void ProfileSystem(); +void PrintTaskList(); +void PrintHeap(); +void PrintStack(); +void PrintCPUStats(); +#endif /* PROFILER_HPP_ */ \ No newline at end of file From d65d8c75dc052eb2d47cfeec1c3c6350f7f49ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChristy?= <“[christy.guirguis@ucalgary.ca]”> Date: Wed, 4 Feb 2026 22:14:55 -0700 Subject: [PATCH 3/6] refactored display --- Profiler.cpp | 255 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 202 insertions(+), 53 deletions(-) diff --git a/Profiler.cpp b/Profiler.cpp index 7907719..4c00634 100644 --- a/Profiler.cpp +++ b/Profiler.cpp @@ -23,83 +23,232 @@ /************************************ * VARIABLES ************************************/ -std::vector TaskNames; -/************************************ - * FUNCTION DECLARATIONS - ************************************/ +// std::vector TaskNames; + +struct TaskProfile { + std::string name; + char state; + int priority; + int stackRemaining; + std::string cpuPercent; +}; + +std::vector Profiles; +// /************************************ +// * FUNCTION DECLARATIONS +// ************************************/ + +// /************************************ +// * FUNCTION DEFINITIONS +// ************************************/ + +// void PrintTaskList() { +// // print list of tasks +// SOAR_PRINT("\n\nName State Priority Stack Num\r\n"); +// SOAR_PRINT("**********************************************\r\n"); + +// char buffer[512]; +// vTaskList(buffer); +// SOAR_PRINT("%s\n", buffer); + +// // populate global vector of task names +// TaskNames.clear(); + +// // parse output and take first entry as task name +// char* line = strtok(buffer, "\n"); +// while (line != NULL) { +// char taskName[configMAX_TASK_NAME_LEN]; +// if (sscanf(line, "%s", taskName) == 1) { // scan first entry splitting on whitespace in line +// TaskNames.push_back(std::string(taskName)); // append to global vector +// } +// line = strtok(NULL, "\n"); +// } +// return; +// } + + +// void PrintCPUStats() { +// // print runtime stats using vTaskGetRunTimeStats +// SOAR_PRINT("\n\nCPU Runtime Stats\r\n"); +// SOAR_PRINT("**********************************************\r\n"); +// char buffer[512]; +// vTaskGetRunTimeStats(buffer); +// SOAR_PRINT("%s\n", buffer); +// } + + +// void PrintHeap() { +// // print heap stats using xPortGetFreeHeapSize and xPortGetMinimumEverFreeHeapSize +// SOAR_PRINT("\n\nHeap Stats\r\n"); +// SOAR_PRINT("**********************************************\r\n"); +// SOAR_PRINT("Current System Free Heap: %d Bytes\r\n", xPortGetFreeHeapSize()); +// SOAR_PRINT("Lowest Ever Free Heap: %d Bytes\r\n", xPortGetMinimumEverFreeHeapSize()); +// } + + +// void PrintStack() { +// SOAR_PRINT("\n\nStack Remaining per Task\r\n"); +// SOAR_PRINT("*************************\r\n"); + +// // iterate through global task name vector +// for (size_t i = 0; i < TaskNames.size(); ++i) { +// std::string& taskName = TaskNames[i]; + +// // get task handle for each task name +// TaskHandle_t handle = xTaskGetHandle(taskName.c_str()); +// if (handle != NULL) { +// // print high watermark attribute for each task +// UBaseType_t highWater = uxTaskGetStackHighWaterMark(handle); +// SOAR_PRINT("%-12s : %lu words\r\n", taskName.c_str(), (unsigned long)highWater); +// } +// } +// } + + +// void ProfileSystem() { +// // call methods to print profile messages +// SOAR_PRINT("\r\n"); +// PrintTaskList(); +// PrintCPUStats(); +// PrintHeap(); +// PrintStack(); +// } + + + + + + + + + + + -/************************************ - * FUNCTION DEFINITIONS - ************************************/ -void PrintTaskList() { - // print list of tasks - SOAR_PRINT("\n\nName State Priority Stack Num\r\n"); - SOAR_PRINT("**********************************************\r\n"); + + + + + + + + + + + +void CollectTaskList() { + // place vTaskList output into buffer char buffer[512]; vTaskList(buffer); - SOAR_PRINT("%s\n", buffer); - - // populate global vector of task names - TaskNames.clear(); - // parse output and take first entry as task name + // parse tasks from line char* line = strtok(buffer, "\n"); while (line != NULL) { - char taskName[configMAX_TASK_NAME_LEN]; - if (sscanf(line, "%s", taskName) == 1) { // scan first entry splitting on whitespace in line - TaskNames.push_back(std::string(taskName)); // append to global vector + char name[configMAX_TASK_NAME_LEN]; + char state; + int priority; + int stack; + int num; + + if (sscanf(line, "%15[^ ] %c %d %d %d", name, &state, &priority, &stack, &num) == 5) { + TaskProfile profile; + + // if name, state, priority, stack, and num successfully read populate profile object + profile.name = name; + profile.state = state; + profile.priority = priority; + profile.stackRemaining = stack; + profile.cpuPercent = "?"; + + // append task profile to global vector + Profiles.push_back(profile); } + line = strtok(NULL, "\n"); } - return; } -void PrintCPUStats() { - // print runtime stats using vTaskGetRunTimeStats - SOAR_PRINT("\n\nCPU Runtime Stats\r\n"); - SOAR_PRINT("**********************************************\r\n"); +void CollectCPUStats() { + // place vTaskGetRunTimeStats output into buffer char buffer[512]; vTaskGetRunTimeStats(buffer); - SOAR_PRINT("%s\n", buffer); -} + // parse runtime stats + char* line = strtok(buffer, "\n"); + while (line != NULL) { + char name[32]; + char percent[8]; + + if (sscanf(line, "%15s %*s %3s", name, percent) == 2) { // ignore abs time + // if name and time percentage sucessfully read, find task in global vector + for (auto& p : Profiles) { + if (p.name == name) { + // initialize task percentage + p.cpuPercent = percent; + } + } + } -void PrintHeap() { - // print heap stats using xPortGetFreeHeapSize and xPortGetMinimumEverFreeHeapSize - SOAR_PRINT("\n\nHeap Stats\r\n"); - SOAR_PRINT("**********************************************\r\n"); - SOAR_PRINT("Current System Free Heap: %d Bytes\r\n", xPortGetFreeHeapSize()); - SOAR_PRINT("Lowest Ever Free Heap: %d Bytes\r\n", xPortGetMinimumEverFreeHeapSize()); + line = strtok(NULL, "\n"); + } } -void PrintStack() { - SOAR_PRINT("\n\nStack Remaining per Task\r\n"); - SOAR_PRINT("*************************\r\n"); - // iterate through global task name vector - for (size_t i = 0; i < TaskNames.size(); ++i) { - std::string& taskName = TaskNames[i]; +void DisplayTable() { + // header + SOAR_PRINT("\r\nSystem Profile\r\n"); + SOAR_PRINT("================================================================================\r\n"); - // get task handle for each task name - TaskHandle_t handle = xTaskGetHandle(taskName.c_str()); - if (handle != NULL) { - // print high watermark attribute for each task - UBaseType_t highWater = uxTaskGetStackHighWaterMark(handle); - SOAR_PRINT("%-12s : %lu words\r\n", taskName.c_str(), (unsigned long)highWater); - } - } + // print task names + SOAR_PRINT("%-12s", "Metric\\Task"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10s ", p.name.c_str()); + SOAR_PRINT("\r\n"); + SOAR_PRINT("--------------------------------------------------------------------------------\r\n"); + + // print task state + SOAR_PRINT("%-12s", "State"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10c ", p.state); + SOAR_PRINT("\r\n"); + + // print task priority + SOAR_PRINT("%-12s", "Priority"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10d ", p.priority); + SOAR_PRINT("\r\n"); + + // print task high water mark (stack remaining/closer to 0 means stack is running out) + SOAR_PRINT("%-12s", "Stack Rem"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10d ", p.stackRemaining); + SOAR_PRINT("\r\n"); + + // print task cpu time percent use + SOAR_PRINT("%-12s", "CPU %"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10s ", p.cpuPercent.c_str()); + SOAR_PRINT("\r\n"); + SOAR_PRINT("================================================================================\r\n"); } void ProfileSystem() { - // call methods to print profile messages - SOAR_PRINT("\r\n"); - PrintTaskList(); - PrintCPUStats(); - PrintHeap(); - PrintStack(); -} \ No newline at end of file + // clear terminal, previous task profiles + SOAR_PRINT("\033[2J\033[H"); + Profiles.clear(); + + // collect profile stats, display profile stat table + CollectTaskList(); + CollectCPUStats(); + DisplayTable(); + + // display heap stats + SOAR_PRINT("\r\nFree Heap: %lu bytes | Min Ever Free Heap: %lu bytes\r\n", + xPortGetFreeHeapSize(), + xPortGetMinimumEverFreeHeapSize()); +} From 1fae0975d076d1c8550bcea5893598498a661593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChristy?= <“[christy.guirguis@ucalgary.ca]”> Date: Sat, 7 Feb 2026 12:07:55 -0700 Subject: [PATCH 4/6] display in table, run as task --- Profiler.cpp | 254 ----------------------------------------------- Profiler.hpp | 37 ------- ProfilerTask.cpp | 190 +++++++++++++++++++++++++++++++++++ ProfilerTask.hpp | 73 ++++++++++++++ 4 files changed, 263 insertions(+), 291 deletions(-) delete mode 100644 Profiler.cpp delete mode 100644 Profiler.hpp create mode 100644 ProfilerTask.cpp create mode 100644 ProfilerTask.hpp diff --git a/Profiler.cpp b/Profiler.cpp deleted file mode 100644 index 4c00634..0000000 --- a/Profiler.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/** - ******************************************************************************** - * @file Profiler.cpp - * @author Christy - * @date Jan 28, 2026 - * @brief - ******************************************************************************** - */ - -/************************************ - * INCLUDES - ************************************/ -#include "Profiler.hpp" -#include "SystemDefines.hpp" -#include -#include -#include - -/************************************ - * PRIVATE MACROS AND DEFINES - ************************************/ - -/************************************ - * VARIABLES - ************************************/ -// std::vector TaskNames; - -struct TaskProfile { - std::string name; - char state; - int priority; - int stackRemaining; - std::string cpuPercent; -}; - -std::vector Profiles; -// /************************************ -// * FUNCTION DECLARATIONS -// ************************************/ - -// /************************************ -// * FUNCTION DEFINITIONS -// ************************************/ - -// void PrintTaskList() { -// // print list of tasks -// SOAR_PRINT("\n\nName State Priority Stack Num\r\n"); -// SOAR_PRINT("**********************************************\r\n"); - -// char buffer[512]; -// vTaskList(buffer); -// SOAR_PRINT("%s\n", buffer); - -// // populate global vector of task names -// TaskNames.clear(); - -// // parse output and take first entry as task name -// char* line = strtok(buffer, "\n"); -// while (line != NULL) { -// char taskName[configMAX_TASK_NAME_LEN]; -// if (sscanf(line, "%s", taskName) == 1) { // scan first entry splitting on whitespace in line -// TaskNames.push_back(std::string(taskName)); // append to global vector -// } -// line = strtok(NULL, "\n"); -// } -// return; -// } - - -// void PrintCPUStats() { -// // print runtime stats using vTaskGetRunTimeStats -// SOAR_PRINT("\n\nCPU Runtime Stats\r\n"); -// SOAR_PRINT("**********************************************\r\n"); -// char buffer[512]; -// vTaskGetRunTimeStats(buffer); -// SOAR_PRINT("%s\n", buffer); -// } - - -// void PrintHeap() { -// // print heap stats using xPortGetFreeHeapSize and xPortGetMinimumEverFreeHeapSize -// SOAR_PRINT("\n\nHeap Stats\r\n"); -// SOAR_PRINT("**********************************************\r\n"); -// SOAR_PRINT("Current System Free Heap: %d Bytes\r\n", xPortGetFreeHeapSize()); -// SOAR_PRINT("Lowest Ever Free Heap: %d Bytes\r\n", xPortGetMinimumEverFreeHeapSize()); -// } - - -// void PrintStack() { -// SOAR_PRINT("\n\nStack Remaining per Task\r\n"); -// SOAR_PRINT("*************************\r\n"); - -// // iterate through global task name vector -// for (size_t i = 0; i < TaskNames.size(); ++i) { -// std::string& taskName = TaskNames[i]; - -// // get task handle for each task name -// TaskHandle_t handle = xTaskGetHandle(taskName.c_str()); -// if (handle != NULL) { -// // print high watermark attribute for each task -// UBaseType_t highWater = uxTaskGetStackHighWaterMark(handle); -// SOAR_PRINT("%-12s : %lu words\r\n", taskName.c_str(), (unsigned long)highWater); -// } -// } -// } - - -// void ProfileSystem() { -// // call methods to print profile messages -// SOAR_PRINT("\r\n"); -// PrintTaskList(); -// PrintCPUStats(); -// PrintHeap(); -// PrintStack(); -// } - - - - - - - - - - - - - - - - - - - - - - - - - -void CollectTaskList() { - // place vTaskList output into buffer - char buffer[512]; - vTaskList(buffer); - - // parse tasks from line - char* line = strtok(buffer, "\n"); - while (line != NULL) { - char name[configMAX_TASK_NAME_LEN]; - char state; - int priority; - int stack; - int num; - - if (sscanf(line, "%15[^ ] %c %d %d %d", name, &state, &priority, &stack, &num) == 5) { - TaskProfile profile; - - // if name, state, priority, stack, and num successfully read populate profile object - profile.name = name; - profile.state = state; - profile.priority = priority; - profile.stackRemaining = stack; - profile.cpuPercent = "?"; - - // append task profile to global vector - Profiles.push_back(profile); - } - - line = strtok(NULL, "\n"); - } -} - - -void CollectCPUStats() { - // place vTaskGetRunTimeStats output into buffer - char buffer[512]; - vTaskGetRunTimeStats(buffer); - - // parse runtime stats - char* line = strtok(buffer, "\n"); - while (line != NULL) { - char name[32]; - char percent[8]; - - if (sscanf(line, "%15s %*s %3s", name, percent) == 2) { // ignore abs time - // if name and time percentage sucessfully read, find task in global vector - for (auto& p : Profiles) { - if (p.name == name) { - // initialize task percentage - p.cpuPercent = percent; - } - } - } - - line = strtok(NULL, "\n"); - } -} - - - -void DisplayTable() { - // header - SOAR_PRINT("\r\nSystem Profile\r\n"); - SOAR_PRINT("================================================================================\r\n"); - - // print task names - SOAR_PRINT("%-12s", "Metric\\Task"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10s ", p.name.c_str()); - SOAR_PRINT("\r\n"); - SOAR_PRINT("--------------------------------------------------------------------------------\r\n"); - - // print task state - SOAR_PRINT("%-12s", "State"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10c ", p.state); - SOAR_PRINT("\r\n"); - - // print task priority - SOAR_PRINT("%-12s", "Priority"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10d ", p.priority); - SOAR_PRINT("\r\n"); - - // print task high water mark (stack remaining/closer to 0 means stack is running out) - SOAR_PRINT("%-12s", "Stack Rem"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10d ", p.stackRemaining); - SOAR_PRINT("\r\n"); - - // print task cpu time percent use - SOAR_PRINT("%-12s", "CPU %"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10s ", p.cpuPercent.c_str()); - SOAR_PRINT("\r\n"); - SOAR_PRINT("================================================================================\r\n"); -} - - -void ProfileSystem() { - // clear terminal, previous task profiles - SOAR_PRINT("\033[2J\033[H"); - Profiles.clear(); - - // collect profile stats, display profile stat table - CollectTaskList(); - CollectCPUStats(); - DisplayTable(); - - // display heap stats - SOAR_PRINT("\r\nFree Heap: %lu bytes | Min Ever Free Heap: %lu bytes\r\n", - xPortGetFreeHeapSize(), - xPortGetMinimumEverFreeHeapSize()); -} diff --git a/Profiler.hpp b/Profiler.hpp deleted file mode 100644 index 9e120b5..0000000 --- a/Profiler.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - ******************************************************************************** - * @file Profiler.hpp - * @author Christy - * @date Jan 28, 2026 - * @brief - ******************************************************************************** - */ - -#ifndef PROFILER_HPP_ -#define PROFILER_HPP_ - -/************************************ - * INCLUDES - ************************************/ - -/************************************ - * MACROS AND DEFINES - ************************************/ - -/************************************ - * TYPEDEFS - ************************************/ - -/************************************ - * CLASS DEFINITIONS - ************************************/ - -/************************************ - * FUNCTION DECLARATIONS - ************************************/ -void ProfileSystem(); -void PrintTaskList(); -void PrintHeap(); -void PrintStack(); -void PrintCPUStats(); -#endif /* PROFILER_HPP_ */ \ No newline at end of file diff --git a/ProfilerTask.cpp b/ProfilerTask.cpp new file mode 100644 index 0000000..247c9ca --- /dev/null +++ b/ProfilerTask.cpp @@ -0,0 +1,190 @@ +/** + ******************************************************************************** + * @file Profiler.cpp + * @author Christy + * @date Jan 28, 2026 + * @brief + ******************************************************************************** + */ + +/************************************ + * INCLUDES + ************************************/ +#include "ProfilerTask.hpp" +#include "SystemDefines.hpp" +#include +#include +#include + +/************************************ + * PRIVATE MACROS AND DEFINES + ************************************/ + +/************************************ + * VARIABLES + ************************************/ +bool profileSystem = false; + +/************************************ + * FUNCTION DECLARATIONS + ************************************/ + +/************************************ + * FUNCTION DEFINITIONS + ************************************/ +/** + * @brief Constructor, sets all member variables + */ +ProfilerTask::ProfilerTask() + : Task(TASK_PROFILER_QUEUE_DEPTH_OBJS) {} + +/** + * @brief Init task for RTOS + */ +void ProfilerTask::InitTask() { + // Make sure the task is not already initialized + SOAR_ASSERT(rtTaskHandle == nullptr, "Cannot initialize Profiler task twice"); + + // Start the task + BaseType_t rtValue = xTaskCreate( + (TaskFunction_t)ProfilerTask::RunTask, (const char*)"ProfilerTask", + (uint16_t)TASK_PROFILER_STACK_DEPTH_WORDS, (void*)this, + (UBaseType_t)TASK_PROFILER_PRIORITY, (TaskHandle_t*)&rtTaskHandle); + + // Ensure creation succeded + SOAR_ASSERT(rtValue == pdPASS, "ProfilerTask::InitTask - xTaskCreate() failed"); +} + + +/** + * @brief Runcode for the ProfilerTask + */ +void ProfilerTask::Run(void* pvParams) { + while (1) { + // profile system while profileSystem is true + if (profileSystem) { + ProfileSystem(); + } + vTaskDelay(pdMS_TO_TICKS(2000)); // delay interval + } +} + + +void ProfilerTask::CollectTaskList(std::vector& Profiles) { + // place vTaskList output into buffer + char buffer[512]; + vTaskList(buffer); + + // parse tasks from line + char* line = strtok(buffer, "\n"); + while (line != NULL) { + char name[configMAX_TASK_NAME_LEN]; + char state; + int priority; + int stack; + int num; + + // read 5 attributes succesfully + if (sscanf(line, "%15[^ ] %c %d %d %d", name, &state, &priority, &stack, &num) == 5) { + TaskProfile profile; + + // if name, state, priority, stack, and num successfully read populate profile object + strncpy(profile.name, name, sizeof(profile.name)); + profile.state = state; + profile.priority = priority; + profile.stackRemaining = stack; + strcpy(profile.cpuPercent, "?"); + + // append task profile to global vector + Profiles.push_back(profile); + } + + line = strtok(NULL, "\n"); + } +} + + +void ProfilerTask::CollectCPUStats(std::vector& Profiles) { + // place vTaskGetRunTimeStats output into buffer + char buffer[512]; + vTaskGetRunTimeStats(buffer); + + // parse runtime stats + char* line = strtok(buffer, "\n"); + while (line != NULL) { + char name[32]; + char percent[8]; + + // read 2 attributes succesfully + if (sscanf(line, "%15s %*s %3s", name, percent) == 2) { // ignore abs time + // if name and time percentage sucessfully read, find task in global vector + for (auto& p : Profiles) { + if (strcmp(p.name, name) == 0) { + // initialize task percentage + strncpy(p.cpuPercent, percent, sizeof(p.cpuPercent)); + } + } + } + + line = strtok(NULL, "\n"); + } +} + + + +void ProfilerTask::DisplayTable(std::vector& Profiles) { + // header + SOAR_PRINT("\r\nSystem Profile\r\n"); + SOAR_PRINT("================================================================================\r\n"); + + // print task names + SOAR_PRINT("%-12s", "Metric\\Task"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10s ", p.name); + SOAR_PRINT("\r\n"); + SOAR_PRINT("--------------------------------------------------------------------------------\r\n"); + + // print task state + SOAR_PRINT("%-12s", "State"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10c ", p.state); + SOAR_PRINT("\r\n"); + + // print task priority + SOAR_PRINT("%-12s", "Priority"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10d ", p.priority); + SOAR_PRINT("\r\n"); + + // print task high water mark (stack remaining/closer to 0 means stack is running out) + SOAR_PRINT("%-12s", "Stack Remain"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10d ", p.stackRemaining); + SOAR_PRINT("\r\n"); + + // print task cpu time percent use + SOAR_PRINT("%-12s", "CPU %"); + for (auto& p : Profiles) + SOAR_PRINT("| %-10s ", p.cpuPercent); + SOAR_PRINT("\r\n"); + SOAR_PRINT("================================================================================\r\n"); +} + + +void ProfilerTask::ProfileSystem() { + // clear terminal, previous task profiles + SOAR_PRINT("\033[2J\033[H"); + + // store task structs in vector + std::vector Profiles; + + // collect profile stats, display profile stat table + CollectTaskList(Profiles); + CollectCPUStats(Profiles); + DisplayTable(Profiles); + + // display heap stats + SOAR_PRINT("\r\nFree Heap: %lu bytes | Min Ever Free Heap: %lu bytes\r\n", xPortGetFreeHeapSize(), + xPortGetMinimumEverFreeHeapSize()); + +} diff --git a/ProfilerTask.hpp b/ProfilerTask.hpp new file mode 100644 index 0000000..9722754 --- /dev/null +++ b/ProfilerTask.hpp @@ -0,0 +1,73 @@ +/** + ******************************************************************************** + * @file Profiler.hpp + * @author Christy + * @date Jan 28, 2026 + * @brief + ******************************************************************************** + */ + +#ifndef PROFILER_TASK_HPP_ +#define PROFILER_TASK_HPP_ +extern bool profileSystem; +/************************************ + * INCLUDES + ************************************/ +#include "Task.hpp" +#include "SystemDefines.hpp" +#include +/************************************ + * MACROS AND DEFINES + ************************************/ +extern bool profileSystem; // profiling status bool + +// struct to store task data +struct TaskProfile { + char name[configMAX_TASK_NAME_LEN]; + char state; + int priority; + int stackRemaining; + char cpuPercent[8]; +}; +/************************************ + * TYPEDEFS + ************************************/ + +/************************************ + * CLASS DEFINITIONS + ************************************/ +class ProfilerTask : public Task { +public: + static ProfilerTask& Inst() { + static ProfilerTask inst; + return inst; + } + + void InitTask(); + void ProfileSystem(); // public handle to run profiling + +protected: + // Main run code + void Run(void* pvParams); + + // Static Task Interface, passes control to the instance Run(); + static void RunTask(void* pvParams) { + ProfilerTask::Inst().Run(pvParams); + } + + // profiling methods + void CollectTaskList(std::vector& Profiles); + void CollectCPUStats(std::vector& Profiles); + void DisplayTable(std::vector& Profiles); + +private: + ProfilerTask(); // Private constructor + ProfilerTask(const ProfilerTask&); // Prevent copy-construction + ProfilerTask& operator=(const ProfilerTask&); // Prevent assignment + +}; +/************************************ + * FUNCTION DECLARATIONS + ************************************/ + +#endif /* PROFILER_TASK_HPP_ */ \ No newline at end of file From 0808d1b02fea1e52cc41ebc5b1fce8eec5f848d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChristy?= <“[christy.guirguis@ucalgary.ca]”> Date: Sat, 7 Feb 2026 13:47:11 -0700 Subject: [PATCH 5/6] modified output to comma seperated values --- ProfilerTask.cpp | 64 ++++++++++++++++++------------------------------ ProfilerTask.hpp | 3 ++- 2 files changed, 26 insertions(+), 41 deletions(-) diff --git a/ProfilerTask.cpp b/ProfilerTask.cpp index 247c9ca..677792f 100644 --- a/ProfilerTask.cpp +++ b/ProfilerTask.cpp @@ -90,7 +90,22 @@ void ProfilerTask::CollectTaskList(std::vector& Profiles) { // if name, state, priority, stack, and num successfully read populate profile object strncpy(profile.name, name, sizeof(profile.name)); - profile.state = state; + + // convert state to human readable format + std::string readableState; + switch (state) { + case 'R': readableState = "Ready"; + break; + case 'B': readableState = "Blocked"; + break; + case 'S': readableState = "Suspended"; + break; + case 'X': readableState = "Executing"; + break; + default: readableState = "Unknown"; + break; + } + strncpy(profile.state, readableState.c_str(), sizeof(profile.state)); profile.priority = priority; profile.stackRemaining = stack; strcpy(profile.cpuPercent, "?"); @@ -131,43 +146,13 @@ void ProfilerTask::CollectCPUStats(std::vector& Profiles) { } - void ProfilerTask::DisplayTable(std::vector& Profiles) { - // header - SOAR_PRINT("\r\nSystem Profile\r\n"); - SOAR_PRINT("================================================================================\r\n"); - - // print task names - SOAR_PRINT("%-12s", "Metric\\Task"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10s ", p.name); - SOAR_PRINT("\r\n"); - SOAR_PRINT("--------------------------------------------------------------------------------\r\n"); - - // print task state - SOAR_PRINT("%-12s", "State"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10c ", p.state); - SOAR_PRINT("\r\n"); - - // print task priority - SOAR_PRINT("%-12s", "Priority"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10d ", p.priority); - SOAR_PRINT("\r\n"); - - // print task high water mark (stack remaining/closer to 0 means stack is running out) - SOAR_PRINT("%-12s", "Stack Remain"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10d ", p.stackRemaining); - SOAR_PRINT("\r\n"); - - // print task cpu time percent use - SOAR_PRINT("%-12s", "CPU %"); - for (auto& p : Profiles) - SOAR_PRINT("| %-10s ", p.cpuPercent); - SOAR_PRINT("\r\n"); - SOAR_PRINT("================================================================================\r\n"); + // print profile message for tasks + SOAR_PRINT("Task Name, Task State, Task Priority, Stack High Water Mark (words), Task CPU Usage\r\n\n"); + + for (auto& p : Profiles) { + SOAR_PRINT("%s, %s, %d, %d, %s\r\n", p.name, p.state, p.priority, p.stackRemaining, p.cpuPercent); + } } @@ -184,7 +169,6 @@ void ProfilerTask::ProfileSystem() { DisplayTable(Profiles); // display heap stats - SOAR_PRINT("\r\nFree Heap: %lu bytes | Min Ever Free Heap: %lu bytes\r\n", xPortGetFreeHeapSize(), - xPortGetMinimumEverFreeHeapSize()); - + SOAR_PRINT("\r\nFree Heap (bytes), Min Ever Free Heap (bytes)"); + SOAR_PRINT("\r\n%lu, %lu", xPortGetFreeHeapSize(), xPortGetMinimumEverFreeHeapSize()); } diff --git a/ProfilerTask.hpp b/ProfilerTask.hpp index 9722754..2b8a71a 100644 --- a/ProfilerTask.hpp +++ b/ProfilerTask.hpp @@ -16,6 +16,7 @@ extern bool profileSystem; #include "Task.hpp" #include "SystemDefines.hpp" #include + /************************************ * MACROS AND DEFINES ************************************/ @@ -24,7 +25,7 @@ extern bool profileSystem; // profiling status bool // struct to store task data struct TaskProfile { char name[configMAX_TASK_NAME_LEN]; - char state; + char state[10]; int priority; int stackRemaining; char cpuPercent[8]; From b5e47f5b35f19ad3d89ea8a719019b342cac7993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChristy?= <“[christy.guirguis@ucalgary.ca]”> Date: Sat, 7 Feb 2026 14:57:57 -0700 Subject: [PATCH 6/6] moved profiler to seperate folder, modified readme --- ProfilerTask.cpp => Profiler/ProfilerTask.cpp | 0 ProfilerTask.hpp => Profiler/ProfilerTask.hpp | 0 README.md | 83 +++++++++++++++++++ 3 files changed, 83 insertions(+) rename ProfilerTask.cpp => Profiler/ProfilerTask.cpp (100%) rename ProfilerTask.hpp => Profiler/ProfilerTask.hpp (100%) diff --git a/ProfilerTask.cpp b/Profiler/ProfilerTask.cpp similarity index 100% rename from ProfilerTask.cpp rename to Profiler/ProfilerTask.cpp diff --git a/ProfilerTask.hpp b/Profiler/ProfilerTask.hpp similarity index 100% rename from ProfilerTask.hpp rename to Profiler/ProfilerTask.hpp diff --git a/README.md b/README.md index 39460e4..1b8cc25 100644 --- a/README.md +++ b/README.md @@ -135,3 +135,86 @@ An example project utilizing Cube++ with basic SOAR_PRINT support, in addition t +# System Profiling +This system includes a task to profile FreeRTOS tasks using various APIs. Profiling requires an external timer. You can enable or disable profiling via a macro. For an example implementation, see the [Profiler branch](https://github.com/UCSOAR/H743VIT6TemplateRepository/tree/Christy/Profiler) in the H743VIT6TemplateRepository. + + +## 1. Enable Profiling in FreeRTOSConfig.h + +Open `FreeRTOSConfig.h` and add the following in the "/* USER CODE BEGIN Defines */" section: +``` +// enable definitions for profiler +#define configGENERATE_RUN_TIME_STATS 1 // 1 = enable profiling, 0 = disable profiling + +#if (configGENERATE_RUN_TIME_STATS == 1) +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 + +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() StartStatsTimer() +#define portGET_RUN_TIME_COUNTER_VALUE() GetStatsTimerCount() + +#define INCLUDE_uxTaskGetStackHighWaterMark 1 + +#define INCLUDE_xTaskGetHandle 1 +#endif +``` +Flip ```configGENERATE_RUN_TIME_STATS``` to 1 to enable profiling, and 0 to disable it. + +## 2. Update ```DebugTask.cpp``` +### 1. Import the profiler header: ```#include "ProfilerTask.hpp"``` +### 2. Add profiling commands to ```HandleDebugMessage```: +``` +#if (configGENERATE_RUN_TIME_STATS == 1) // enable profiling commands if profiling enabled + else if (strcmp(msg, "top") == 0) { + profileSystem = true; + } else if (strcmp(msg, "stoptop") == 0) { + profileSystem = false; + } +#endif +``` + +## 3. Initialize the Profiler Task in main_system.cpp +### 1. Import the profiler header: ```#include "ProfilerTask.hpp"``` + +### 2. In the ```run_main``` method, add the following: +``` +#if (configGENERATE_RUN_TIME_STATS == 1) +ProfilerTask::Inst().InitTask(); +#endif +``` + +## 4. Define Profiler Task Parameters +### 1. in ```SystemDefines.hpp```, include a section for the profiler task: +``` +// Profiler task +constexpr uint8_t TASK_PROFILER_PRIORITY = 3; // Priority of the profiler task +constexpr uint8_t TASK_PROFILER_QUEUE_DEPTH_OBJS =10; // Size of the profiler task queue +constexpr uint16_t TASK_PROFILER_STACK_DEPTH_WORDS =512; // Size of the profiler task stack +``` +## 5. Configure the Timer +1. In the ioc file, pick an unused timer and change the clock source to "Internal Clock". +2. Select a prescalar value to configure the timer to be 10-20x faster than configTICK_RATE_HZ (normally set to 1000, can be found in FreeRTOSConfig.h). The prescalar you select depends on your system clock. + +## 6. Conditionally Initialize Timer in ```main.c``` +### 1. Navigate to "USER CODE BEGIN 2", and move the timer initialization call inside a conditional: +``` +#if (configGENERATE_RUN_TIME_STATS == 1) +MX_TIM2_Init(); // Replace TIM2 with your selected timer +#endif +``` + +### 2. Then navigate to "USER CODE BEGIN 4" and link the timer to the FreeRTOS API by copying the following: +``` +#if (configGENERATE_RUN_TIME_STATS == 1) +void StartStatsTimer(void) { + HAL_TIM_Base_Start(&htim2); // replace htim2 with your selected timer +} + +uint32_t GetStatsTimerCount(void) { + return __HAL_TIM_GET_COUNTER(&htim2); // replace htim2 with your selected timer +} +#endif +``` + +