cleanup
This commit is contained in:
574
1 software design/components/perf_tests/test/perf_tests.cpp
Normal file
574
1 software design/components/perf_tests/test/perf_tests.cpp
Normal file
@@ -0,0 +1,574 @@
|
||||
/**
|
||||
* @file perf_tests.cpp
|
||||
* @brief Unity tests for CPU load, memory consumption, and stack usage.
|
||||
*
|
||||
* This file implements comprehensive performance monitoring for ESP32 systems.
|
||||
* It tests three critical aspects of system health:
|
||||
* 1. Memory Usage - Ensures sufficient free heap memory
|
||||
* 2. Stack Usage - Monitors task stack consumption to prevent overflows
|
||||
* 3. CPU Load - Analyzes processor utilization across both cores
|
||||
*
|
||||
* @author Mahmoud Elmohtady (improvements)
|
||||
* @company Nabd solutions - ASF
|
||||
* @copyright 2025
|
||||
*/
|
||||
|
||||
// Standard C library includes for string operations and formatted output
|
||||
#include <string.h> // For strcasecmp(), strstr() - string comparison functions
|
||||
#include <stdio.h> // For printf-style functions (used by ESP_LOGI)
|
||||
#include <inttypes.h> // For portable integer type definitions
|
||||
|
||||
// FreeRTOS includes - the real-time operating system running on ESP32
|
||||
#include "freertos/FreeRTOS.h" // Core FreeRTOS definitions and types
|
||||
#include "freertos/task.h" // Task management functions (create, delay, etc.)
|
||||
#include "freertos/portmacro.h" // Platform-specific macros and definitions
|
||||
|
||||
// ESP-IDF (Espressif IoT Development Framework) includes
|
||||
#include "esp_timer.h" // High-resolution timer functions
|
||||
#include "esp_system.h" // System-level functions and utilities
|
||||
#include "esp_heap_caps.h" // Memory management with capability-based allocation
|
||||
#include "esp_log.h" // Logging system for debug output
|
||||
|
||||
// Conditional includes based on configuration
|
||||
#if CONFIG_SPIRAM_SUPPORT
|
||||
#include "esp_spiram.h" // External SPIRAM (Serial Peripheral RAM) support
|
||||
#endif
|
||||
|
||||
#if CONFIG_ENABLE_PERF_TESTS
|
||||
#include "unity.h" // Unity test framework for professional testing
|
||||
#endif
|
||||
|
||||
// extern "C" tells the C++ compiler to use C-style function naming
|
||||
// This is necessary because FreeRTOS and ESP-IDF are written in C
|
||||
extern "C" {
|
||||
|
||||
// TAG is used by ESP_LOGI() for log message identification
|
||||
// All log messages from this file will be prefixed with "perf_tests"
|
||||
static const char *TAG = "perf_tests";
|
||||
|
||||
/* ========================================================================
|
||||
* CONFIGURABLE THRESHOLDS - Adjust these values for your specific application
|
||||
* ======================================================================== */
|
||||
|
||||
// Minimum free heap memory (in bytes) to consider the system "healthy"
|
||||
// 1024 bytes = 1KB - very conservative threshold
|
||||
// Increase this value for applications that need more memory headroom
|
||||
#ifndef PERF_MIN_HEAP_BYTES
|
||||
#define PERF_MIN_HEAP_BYTES (1024U)
|
||||
#endif
|
||||
|
||||
// Minimum unused stack space (in WORDS, not bytes) for the current task
|
||||
// 100 words = 400 bytes on ESP32 (since each word is 4 bytes)
|
||||
// This prevents stack overflow crashes in your application tasks
|
||||
#ifndef PERF_MIN_STACK_WORDS_CURRENT
|
||||
#define PERF_MIN_STACK_WORDS_CURRENT (100U)
|
||||
#endif
|
||||
|
||||
// Minimum unused stack space (in WORDS) for system idle tasks
|
||||
// 50 words = 200 bytes - idle tasks do minimal work so need less stack
|
||||
// Critical for system stability - idle task crashes = system crash
|
||||
#ifndef PERF_MIN_STACK_WORDS_IDLE
|
||||
#define PERF_MIN_STACK_WORDS_IDLE (50U)
|
||||
#endif
|
||||
|
||||
// Tolerance for CPU usage percentage calculations (as a float)
|
||||
// 20.0% tolerance accounts for multi-core timing complexities
|
||||
// In dual-core systems, percentages don't always sum to exactly 100%
|
||||
#ifndef PERF_RUNTIME_PERCENT_TOLERANCE
|
||||
#define PERF_RUNTIME_PERCENT_TOLERANCE (20.0f)
|
||||
#endif
|
||||
|
||||
/* ========================================================================
|
||||
* UTILITY FUNCTIONS
|
||||
* ======================================================================== */
|
||||
|
||||
/**
|
||||
* @brief Simple delay function that yields CPU to other tasks
|
||||
* @param ms Delay time in milliseconds
|
||||
*
|
||||
* This is better than a busy-wait loop because it allows other tasks to run.
|
||||
* vTaskDelay() puts the current task to sleep and wakes it up after the specified time.
|
||||
* pdMS_TO_TICKS() converts milliseconds to FreeRTOS tick units.
|
||||
*/
|
||||
static void wait_ms(uint32_t ms)
|
||||
{
|
||||
// Convert milliseconds to FreeRTOS ticks and delay the current task
|
||||
// This allows other tasks to run during the delay period
|
||||
vTaskDelay(pdMS_TO_TICKS(ms));
|
||||
}
|
||||
|
||||
/* ========================================================================
|
||||
* PERFORMANCE TEST FUNCTIONS
|
||||
* These functions only compile when CONFIG_ENABLE_PERF_TESTS is enabled
|
||||
* ======================================================================== */
|
||||
#if CONFIG_ENABLE_PERF_TESTS
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* MEMORY CONSUMPTION TEST
|
||||
* ------------------------------------------------------------------------ */
|
||||
#if CONFIG_ENABLE_MEMORY_TEST
|
||||
/**
|
||||
* @brief Tests system memory consumption and availability
|
||||
*
|
||||
* This test checks different types of memory on the ESP32:
|
||||
* 1. 8-bit accessible memory (most common, used by malloc())
|
||||
* 2. DMA-capable memory (required for hardware peripherals)
|
||||
* 3. SPIRAM memory (external RAM, if configured)
|
||||
*
|
||||
* The test ensures the system has sufficient free memory for stable operation.
|
||||
* Low memory can cause malloc() failures, task creation failures, or system crashes.
|
||||
*/
|
||||
void test_memory_consumption_basic(void)
|
||||
{
|
||||
/* ---- CHECK 8-BIT ACCESSIBLE HEAP ---- */
|
||||
// This is the most commonly used memory type - where malloc() allocates from
|
||||
|
||||
// Get current free memory in the 8-bit heap
|
||||
size_t free_now_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
|
||||
// Get the minimum free memory since boot (worst case scenario)
|
||||
// This shows if the system has been under memory pressure
|
||||
size_t free_min_8bit = heap_caps_get_minimum_free_size(MALLOC_CAP_8BIT);
|
||||
|
||||
// Log the results for debugging and monitoring
|
||||
ESP_LOGI(TAG, "8-bit heap: free_now=%u free_min=%u", (unsigned)free_now_8bit, (unsigned)free_min_8bit);
|
||||
|
||||
// CRITICAL TEST: Ensure we have enough free memory
|
||||
// If this fails, your application may crash due to out-of-memory conditions
|
||||
TEST_ASSERT_MESSAGE(free_now_8bit >= PERF_MIN_HEAP_BYTES,
|
||||
"Not enough free 8-bit heap; increase RAM or lower threshold");
|
||||
|
||||
/* ---- CHECK DMA-CAPABLE HEAP ---- */
|
||||
// DMA memory is required for hardware peripherals like SPI, I2C, WiFi, etc.
|
||||
// Some ESP32 configurations share this with 8-bit heap, others separate it
|
||||
|
||||
// Get current free DMA-capable memory
|
||||
size_t free_now_dma = heap_caps_get_free_size(MALLOC_CAP_DMA);
|
||||
|
||||
// Get minimum free DMA memory since boot
|
||||
size_t free_min_dma = heap_caps_get_minimum_free_size(MALLOC_CAP_DMA);
|
||||
|
||||
// Log DMA memory status
|
||||
ESP_LOGI(TAG, "DMA heap: free_now=%u free_min=%u", (unsigned)free_now_dma, (unsigned)free_min_dma);
|
||||
|
||||
// Basic sanity check - ensure the API call succeeded
|
||||
// Note: On some configurations, DMA heap is the same as 8-bit heap
|
||||
TEST_ASSERT_GREATER_OR_EQUAL_size_t(0, free_min_dma);
|
||||
|
||||
/* ---- CHECK SPIRAM (EXTERNAL RAM) IF AVAILABLE ---- */
|
||||
#if CONFIG_SPIRAM_SUPPORT
|
||||
// SPIRAM is external RAM that can expand memory from ~300KB to several MB
|
||||
// It's slower than internal RAM but great for large buffers
|
||||
|
||||
// Check if SPIRAM was successfully initialized at boot
|
||||
if (esp_spiram_is_initialized()) {
|
||||
// Get free SPIRAM memory (combined with 8-bit capability)
|
||||
size_t free_spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
// Get minimum free SPIRAM since boot
|
||||
size_t min_spiram = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
|
||||
// Log SPIRAM status
|
||||
ESP_LOGI(TAG, "SPIRAM: free_now=%u free_min=%u", (unsigned)free_spiram, (unsigned)min_spiram);
|
||||
|
||||
// Ensure SPIRAM is functioning (basic sanity check)
|
||||
TEST_ASSERT_GREATER_OR_EQUAL_size_t(0, min_spiram);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif // CONFIG_ENABLE_MEMORY_TEST
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* STACK USAGE MONITORING TESTS
|
||||
* ------------------------------------------------------------------------ */
|
||||
#if CONFIG_ENABLE_STACK_USAGE_TEST
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert stack words to bytes for human readability
|
||||
* @param words Stack size in words (FreeRTOS native unit)
|
||||
* @return Stack size in bytes
|
||||
*
|
||||
* FreeRTOS measures stack in "words" which are platform-specific.
|
||||
* On ESP32, 1 word = 4 bytes (32-bit architecture).
|
||||
* This function converts to bytes for easier understanding.
|
||||
*/
|
||||
static inline size_t stack_words_to_bytes(UBaseType_t words)
|
||||
{
|
||||
// sizeof(StackType_t) is 4 bytes on ESP32 (32-bit words)
|
||||
return (size_t)words * sizeof(StackType_t);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tests stack usage of the currently running task
|
||||
*
|
||||
* Stack overflow is one of the most common causes of embedded system crashes.
|
||||
* This test monitors how much stack space remains unused in the current task.
|
||||
*
|
||||
* IMPORTANT: "High water mark" means UNUSED stack, not USED stack!
|
||||
* - High value = lots of unused stack = good
|
||||
* - Low value = little unused stack = danger of overflow
|
||||
*/
|
||||
void test_stack_usage_current_task(void)
|
||||
{
|
||||
// Get the "high water mark" - minimum unused stack since task creation
|
||||
// NULL parameter means "check the currently running task"
|
||||
UBaseType_t high_water_words = uxTaskGetStackHighWaterMark(NULL);
|
||||
|
||||
// Convert to bytes for easier understanding
|
||||
size_t high_water_bytes = stack_words_to_bytes(high_water_words);
|
||||
|
||||
// Log the results for monitoring and debugging
|
||||
ESP_LOGI(TAG, "Current task high-water: %u words (%u bytes)",
|
||||
(unsigned)high_water_words, (unsigned)high_water_bytes);
|
||||
|
||||
// CRITICAL TEST: Ensure sufficient unused stack remains
|
||||
// If this fails, the task is at risk of stack overflow crash
|
||||
TEST_ASSERT_MESSAGE(high_water_words >= PERF_MIN_STACK_WORDS_CURRENT,
|
||||
"Current task stack high-water too low; consider increasing stack size");
|
||||
}
|
||||
|
||||
/* ---- MULTI-CORE vs SINGLE-CORE IDLE TASK MONITORING ---- */
|
||||
#if (configNUM_CORES > 1)
|
||||
/**
|
||||
* @brief Tests stack usage of idle tasks on multi-core ESP32
|
||||
*
|
||||
* In multi-core systems, each CPU core has its own idle task:
|
||||
* - IDLE0: Runs on CPU core 0 when no other tasks need it
|
||||
* - IDLE1: Runs on CPU core 1 when no other tasks need it
|
||||
*
|
||||
* Idle task stack overflow crashes the entire system!
|
||||
*/
|
||||
void test_stack_usage_idle_tasks(void)
|
||||
{
|
||||
// Loop through each CPU core (ESP32 has 2 cores: 0 and 1)
|
||||
for (int cpu = 0; cpu < configNUM_CORES; ++cpu) {
|
||||
|
||||
// Get the idle task handle for this specific CPU core
|
||||
TaskHandle_t idle = xTaskGetIdleTaskHandleForCore(cpu);
|
||||
|
||||
// Ensure we got a valid handle (system sanity check)
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(idle, "Idle task handle is NULL for a CPU");
|
||||
|
||||
// Get the high water mark for this idle task
|
||||
UBaseType_t hw = uxTaskGetStackHighWaterMark(idle);
|
||||
|
||||
// Convert to bytes for logging
|
||||
size_t hw_bytes = stack_words_to_bytes(hw);
|
||||
|
||||
// Log idle task stack status for each core
|
||||
ESP_LOGI(TAG, "Idle task (cpu %d) high-water: %u words (%u bytes)",
|
||||
cpu, (unsigned)hw, (unsigned)hw_bytes);
|
||||
|
||||
// CRITICAL TEST: Ensure idle task has sufficient stack
|
||||
// Idle task crash = system crash, so this is very important
|
||||
TEST_ASSERT_MESSAGE(hw >= PERF_MIN_STACK_WORDS_IDLE,
|
||||
"Idle task stack high-water too low; increase idle task stack for CPU");
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
/**
|
||||
* @brief Tests stack usage of idle task on single-core ESP32
|
||||
*
|
||||
* Single-core systems have only one idle task that runs when no other tasks are active.
|
||||
* This is a simpler version of the multi-core test above.
|
||||
*/
|
||||
void test_stack_usage_idle_task(void)
|
||||
{
|
||||
// Get the single idle task handle (no core specification needed)
|
||||
TaskHandle_t idle = xTaskGetIdleTaskHandle();
|
||||
|
||||
// Ensure we got a valid handle
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(idle, "Idle task handle is NULL");
|
||||
|
||||
// Get high water mark for the idle task
|
||||
UBaseType_t hw = uxTaskGetStackHighWaterMark(idle);
|
||||
|
||||
// Convert to bytes for logging
|
||||
size_t hw_bytes = stack_words_to_bytes(hw);
|
||||
|
||||
// Log idle task stack status
|
||||
ESP_LOGI(TAG, "Idle task high-water: %u words (%u bytes)", (unsigned)hw, (unsigned)hw_bytes);
|
||||
|
||||
// CRITICAL TEST: Ensure idle task has sufficient stack
|
||||
TEST_ASSERT_MESSAGE(hw >= PERF_MIN_STACK_WORDS_IDLE,
|
||||
"Idle task stack high-water too low; increase idle task stack");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_ENABLE_STACK_USAGE_TEST
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* CPU LOAD ANALYSIS TEST
|
||||
* ------------------------------------------------------------------------ */
|
||||
#if CONFIG_ENABLE_CPU_LOAD_TEST
|
||||
|
||||
/**
|
||||
* @brief Analyzes CPU utilization across all cores and tasks
|
||||
*
|
||||
* This is the most complex test because it deals with multi-core timing statistics.
|
||||
* It measures how much CPU time each task consumes and identifies performance bottlenecks.
|
||||
*
|
||||
* Key concepts:
|
||||
* - Runtime statistics track CPU time per task using a high-resolution timer
|
||||
* - Idle tasks run when no other tasks need CPU (high idle = low system load)
|
||||
* - Multi-core systems have separate idle tasks per core
|
||||
* - Percentages may not sum to exactly 100% due to timing complexities
|
||||
*
|
||||
* Requirements:
|
||||
* - configGENERATE_RUN_TIME_STATS must be enabled in FreeRTOS config
|
||||
* - High-resolution timer must be configured (ESP_TIMER recommended)
|
||||
*/
|
||||
void test_cpu_load_estimation(void)
|
||||
{
|
||||
/* ---- CHECK IF RUNTIME STATISTICS ARE ENABLED ---- */
|
||||
#if ( configGENERATE_RUN_TIME_STATS == 1 )
|
||||
|
||||
/* ---- STEP 1: STABILIZATION PERIOD ---- */
|
||||
// Allow the system to reach steady state before measurement
|
||||
// Longer period = more accurate measurements, but slower test
|
||||
wait_ms(1000); // 1 second should be sufficient for most applications
|
||||
|
||||
/* ---- STEP 2: GET TASK COUNT ---- */
|
||||
// Find out how many tasks are currently running in the system
|
||||
UBaseType_t num_tasks = uxTaskGetNumberOfTasks();
|
||||
|
||||
// Sanity check - ensure FreeRTOS is reporting tasks correctly
|
||||
TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, num_tasks, "No tasks reported by uxTaskGetNumberOfTasks()");
|
||||
|
||||
/* ---- STEP 3: ALLOCATE MEMORY FOR TASK DATA ---- */
|
||||
// We need an array to hold information about each task
|
||||
// Add a small margin in case new tasks are created during measurement
|
||||
const UBaseType_t margin = 4;
|
||||
|
||||
// TaskStatus_t is a FreeRTOS structure containing task information
|
||||
TaskStatus_t *task_array = (TaskStatus_t *)malloc(sizeof(TaskStatus_t) * (num_tasks + margin));
|
||||
|
||||
// Ensure memory allocation succeeded
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(task_array, "Failed to allocate task array for system state");
|
||||
|
||||
/* ---- STEP 4: CAPTURE SYSTEM STATE SNAPSHOT ---- */
|
||||
// This is the critical measurement - atomic snapshot of all task states
|
||||
unsigned long total_run_time = 0; // FreeRTOS will fill this with total CPU time
|
||||
|
||||
// uxTaskGetSystemState() captures all task information atomically
|
||||
UBaseType_t fetched = uxTaskGetSystemState(task_array, num_tasks + margin, &total_run_time);
|
||||
|
||||
// Log the raw data for debugging
|
||||
ESP_LOGI(TAG, "uxTaskGetSystemState fetched=%u total_run_time=%lu", (unsigned)fetched, total_run_time);
|
||||
|
||||
/* ---- STEP 5: VALIDATE DATA QUALITY ---- */
|
||||
// Ensure the data capture was successful
|
||||
TEST_ASSERT_GREATER_THAN_UINT32_MESSAGE(0, fetched, "uxTaskGetSystemState returned no tasks");
|
||||
TEST_ASSERT_GREATER_THAN_MESSAGE(0UL, total_run_time, "Total run time is zero; ensure runtime stats timer is configured");
|
||||
|
||||
/* ---- STEP 6: MULTI-CORE AWARENESS ---- */
|
||||
// Multi-core systems have complex timing behavior
|
||||
// Each core contributes independently to the total runtime
|
||||
(void)total_run_time; // Suppress unused variable warning for normalized_total_time
|
||||
|
||||
#if (configNUM_CORES > 1)
|
||||
// Log that we're handling multi-core complexity
|
||||
ESP_LOGI(TAG, "Multi-core system detected, using adjusted calculation");
|
||||
#endif
|
||||
|
||||
/* ---- STEP 7: INITIALIZE ANALYSIS VARIABLES ---- */
|
||||
double percent_sum = 0.0; // Sum of non-idle task percentages
|
||||
double idle_percent_total = 0.0; // Sum of all idle task percentages
|
||||
int idle_task_count = 0; // Number of idle tasks found
|
||||
|
||||
/* ---- STEP 8: ANALYZE EACH TASK ---- */
|
||||
for (UBaseType_t i = 0; i < fetched; ++i) {
|
||||
|
||||
// Get the runtime counter for this task (CPU time consumed)
|
||||
unsigned long run = task_array[i].ulRunTimeCounter;
|
||||
|
||||
// Calculate percentage: (task_time / total_time) * 100
|
||||
double pct = 0.0;
|
||||
if (total_run_time > 0) {
|
||||
pct = ((double)run * 100.0) / (double)total_run_time;
|
||||
}
|
||||
|
||||
// Log detailed information for each task (useful for debugging)
|
||||
ESP_LOGI(TAG, "Task[%u] name='%s' handle=%p run=%lu pct=%.2f",
|
||||
(unsigned)i, task_array[i].pcTaskName, task_array[i].xHandle, run, pct);
|
||||
|
||||
/* ---- IDENTIFY IDLE TASKS ---- */
|
||||
// Idle tasks have special names and behavior
|
||||
// In multi-core: "IDLE0" (core 0), "IDLE1" (core 1)
|
||||
// In single-core: usually just "IDLE"
|
||||
if (task_array[i].pcTaskName != NULL &&
|
||||
(strcasecmp(task_array[i].pcTaskName, "IDLE0") == 0 ||
|
||||
strcasecmp(task_array[i].pcTaskName, "IDLE1") == 0 ||
|
||||
strstr(task_array[i].pcTaskName, "IDLE") != NULL))
|
||||
{
|
||||
// This is an idle task - track it separately
|
||||
idle_percent_total += pct;
|
||||
idle_task_count++;
|
||||
ESP_LOGI(TAG, "Found idle task: %s with %.2f%% usage", task_array[i].pcTaskName, pct);
|
||||
}
|
||||
|
||||
/* ---- SEPARATE ACTIVE vs IDLE TASKS ---- */
|
||||
// Only count non-idle tasks for system load calculation
|
||||
// Idle tasks run when nothing else needs CPU, so they don't represent "work"
|
||||
if (task_array[i].pcTaskName == NULL ||
|
||||
(strstr(task_array[i].pcTaskName, "IDLE") == NULL)) {
|
||||
percent_sum += pct;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- STEP 9: CLEANUP MEMORY ---- */
|
||||
// Free the allocated task array
|
||||
free(task_array);
|
||||
|
||||
/* ---- STEP 10: ANALYZE RESULTS ---- */
|
||||
// Log the analysis results
|
||||
ESP_LOGI(TAG, "Non-idle task runtime sum = %.2f%%", percent_sum);
|
||||
ESP_LOGI(TAG, "Total idle task runtime = %.2f%% (across %d idle tasks)", idle_percent_total, idle_task_count);
|
||||
ESP_LOGI(TAG, "System appears to be %.2f%% busy", percent_sum);
|
||||
|
||||
/* ---- STEP 11: VALIDATE SYSTEM HEALTH ---- */
|
||||
// These are relaxed validations designed for multi-core systems
|
||||
// The goal is to ensure the system is reporting reasonable values
|
||||
|
||||
// Ensure we found idle tasks (basic system sanity check)
|
||||
TEST_ASSERT_MESSAGE(idle_task_count > 0, "No idle tasks found; runtime stats may not be working");
|
||||
|
||||
// Ensure idle percentage makes sense (not negative)
|
||||
TEST_ASSERT_MESSAGE(idle_percent_total >= 0.0, "Idle task percentage is negative");
|
||||
|
||||
// Ensure active task percentage is reasonable (not impossibly high)
|
||||
TEST_ASSERT_MESSAGE(percent_sum >= 0.0 && percent_sum <= 200.0, "Non-idle task percentage sum is unreasonable");
|
||||
|
||||
// Check for system overload (too much CPU usage)
|
||||
TEST_ASSERT_MESSAGE(percent_sum < 150.0, "System appears overloaded or runtime stats misconfigured");
|
||||
|
||||
// Log successful completion
|
||||
ESP_LOGI(TAG, "CPU load test completed - system load appears reasonable");
|
||||
|
||||
#else
|
||||
/* ---- RUNTIME STATISTICS DISABLED ---- */
|
||||
// If FreeRTOS runtime statistics are not enabled, skip this test
|
||||
TEST_IGNORE_MESSAGE("configGENERATE_RUN_TIME_STATS is disabled; enable to run CPU load test");
|
||||
#endif // configGENERATE_RUN_TIME_STATS
|
||||
}
|
||||
#endif // CONFIG_ENABLE_CPU_LOAD_TEST
|
||||
|
||||
#endif // CONFIG_ENABLE_PERF_TESTS
|
||||
|
||||
} // extern "C"
|
||||
|
||||
/* ========================================================================
|
||||
* UNITY TEST FRAMEWORK INTEGRATION
|
||||
*
|
||||
* This section integrates our performance tests with the Unity test framework.
|
||||
* Unity provides professional test reporting, assertions, and test management.
|
||||
* ======================================================================== */
|
||||
#if CONFIG_ENABLE_PERF_TESTS
|
||||
|
||||
// Forward declaration of app_main (defined in main.cpp)
|
||||
extern "C" void app_main(void);
|
||||
|
||||
/**
|
||||
* @brief Test registration function (currently unused but kept for future expansion)
|
||||
*
|
||||
* This function could be used for dynamic test registration if needed.
|
||||
* Currently, we use the simpler RUN_TEST() approach in unity_task().
|
||||
*/
|
||||
extern "C" void register_perf_tests(void)
|
||||
{
|
||||
// This function demonstrates how to register tests by name
|
||||
// Currently not used, but available for future dynamic test loading
|
||||
|
||||
#if CONFIG_ENABLE_MEMORY_TEST
|
||||
unity_run_test_by_name("test_memory_consumption_basic");
|
||||
#endif
|
||||
|
||||
#if CONFIG_ENABLE_STACK_USAGE_TEST
|
||||
unity_run_test_by_name("test_stack_usage_current_task");
|
||||
#if (configNUM_CORES > 1)
|
||||
unity_run_test_by_name("test_stack_usage_idle_tasks");
|
||||
#else
|
||||
unity_run_test_by_name("test_stack_usage_idle_task");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_ENABLE_CPU_LOAD_TEST
|
||||
unity_run_test_by_name("test_cpu_load_estimation");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main Unity test runner task
|
||||
* @param pvParameters Task parameters (unused, required by FreeRTOS)
|
||||
*
|
||||
* This function runs as a FreeRTOS task and executes all performance tests.
|
||||
* It's called from main.cpp when performance testing is enabled.
|
||||
*
|
||||
* Execution flow:
|
||||
* 1. Allow system to stabilize after boot
|
||||
* 2. Initialize Unity test framework
|
||||
* 3. Run each enabled test function
|
||||
* 4. Print final test results
|
||||
* 5. Keep task alive for system stability
|
||||
*/
|
||||
extern "C" void unity_task(void *pvParameters)
|
||||
{
|
||||
// Suppress unused parameter warning (FreeRTOS requires this parameter)
|
||||
(void)pvParameters;
|
||||
|
||||
/* ---- STEP 1: SYSTEM STABILIZATION ---- */
|
||||
// Give the system a moment to finish initialization
|
||||
// This ensures all components are ready before testing begins
|
||||
vTaskDelay(2); // 2 FreeRTOS ticks (usually 20ms)
|
||||
|
||||
/* ---- STEP 2: INITIALIZE UNITY FRAMEWORK ---- */
|
||||
// UNITY_BEGIN() sets up the test framework and resets counters
|
||||
UNITY_BEGIN();
|
||||
|
||||
/* ---- STEP 3: RUN PERFORMANCE TESTS ---- */
|
||||
// Each RUN_TEST() macro executes a test function and tracks results
|
||||
// Tests are run conditionally based on configuration flags
|
||||
|
||||
#if CONFIG_ENABLE_MEMORY_TEST
|
||||
// Test memory consumption and availability
|
||||
RUN_TEST(test_memory_consumption_basic);
|
||||
#endif
|
||||
|
||||
#if CONFIG_ENABLE_STACK_USAGE_TEST
|
||||
// Test current task stack usage
|
||||
RUN_TEST(test_stack_usage_current_task);
|
||||
|
||||
// Test idle task stack usage (multi-core vs single-core)
|
||||
#if (configNUM_CORES > 1)
|
||||
RUN_TEST(test_stack_usage_idle_tasks); // Multi-core version
|
||||
#else
|
||||
RUN_TEST(test_stack_usage_idle_task); // Single-core version
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CONFIG_ENABLE_CPU_LOAD_TEST
|
||||
// Test CPU utilization across all cores and tasks
|
||||
RUN_TEST(test_cpu_load_estimation);
|
||||
#endif
|
||||
|
||||
/* ---- STEP 4: FINALIZE AND REPORT RESULTS ---- */
|
||||
// UNITY_END() prints the final test summary:
|
||||
// - Total tests run
|
||||
// - Number of failures
|
||||
// - Number of ignored tests
|
||||
// - Overall PASS/FAIL status
|
||||
UNITY_END();
|
||||
|
||||
/* ---- STEP 5: KEEP TASK ALIVE ---- */
|
||||
// The task must remain alive for system stability
|
||||
// Deleting this task could cause issues with FreeRTOS scheduler
|
||||
while(1) {
|
||||
// Sleep for 1 second, then repeat
|
||||
// This keeps the task alive with minimal CPU usage
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_ENABLE_PERF_TESTS
|
||||
|
||||
Reference in New Issue
Block a user