esp-idf/components/freertos/test/integration/tasks/test_tasks_snapshot.c
Darian Leung e0b1ccf6ce freertos: Fix flakey task snapshot tests
The task snapshot tests use esp_cpu_stall() to stall the other CPU before
manually walking the task lists. However, it is possible that the other CPU
was also accessing the task lists when esp_cpu_stall() is called, leading to
flakey tests

This commit fixes the test by using a 2-way handshake instead of
esp_cpu_stall().
2023-03-15 16:12:24 +08:00

192 lines
6.3 KiB
C

/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#if CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT
#include <stdio.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/task_snapshot.h"
#include "esp_cpu.h"
#include "esp_rom_sys.h"
#include "unity.h"
#include "sdkconfig.h"
#define TEST_MAX_TASKS_NUM 32
#define NUM_TASKS_PER_LIST 2
#define TASK_PRIORITY (configMAX_PRIORITIES - 2)
static volatile bool start_ready_flag = false;
static volatile bool start_resp_flag = false;
static volatile bool stop_flag = false;
static void ready_task(void *arg)
{
while (1) {
#if !CONFIG_FREERTOS_UNICORE
taskDISABLE_INTERRUPTS();
/*
The main task (on core 0) has a higher priority than this task. Thus, by the time this flag is set, this task
is guaranteed to running on core 1.
*/
if (start_ready_flag) { // Is the test starting?
start_resp_flag = true; // Indicate we are ready to start
while (!stop_flag) {
; // Wait until test completes
}
// Reset the flags
start_ready_flag = false;
start_resp_flag = false;
stop_flag = false;
}
taskENABLE_INTERRUPTS();
// Short non-blocking delay to allow INT WDT to feed
esp_rom_delay_us(1000);
#endif // !CONFIG_FREERTOS_UNICORE
}
}
static void blocked_task(void *arg)
{
// Delay for portMAX_DELAY - 1 as not to go on the suspended list
vTaskDelay(portMAX_DELAY - 1);
}
static void suspended_task(void *arg)
{
vTaskSuspend(NULL);
}
static void setup(TaskHandle_t *task_list, int *num_tasks_ret, UBaseType_t *old_priority_ret)
{
// Raise our priority so that we aren't preempted
*old_priority_ret = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1);
// Create tasks
int num_tasks = 0;
for (int i = 0; i < NUM_TASKS_PER_LIST; i++) {
//Ready task
xTaskCreate(ready_task, "ready", 1024, NULL, TASK_PRIORITY, &(task_list[num_tasks]));
num_tasks++;
//Blocked task
xTaskCreate(blocked_task, "blkd", 1024, NULL, TASK_PRIORITY, &(task_list[num_tasks]));
num_tasks++;
//Suspended task
xTaskCreate(suspended_task, "susp", 1024, NULL, TASK_PRIORITY, &(task_list[num_tasks]));
num_tasks++;
}
*num_tasks_ret = num_tasks;
// Short delay to allow tasks to spin up
vTaskDelay(10);
/*
Task snapshot functions are only meant to be called when FreeRTOS is no longer running (e.g., during a panic),
thus assumes that nothing else (interrupt or the other core) will access the task lists simultaneously. However, for
unit tests, FreeRTOS is still running. So we must guarantee thread safe access another way.
- For single core, simply disabling interrupts will guarantee that the current task is the only thread to access the
task lists
- For SMP, we also need to stop the other core from accessing the task lists. Thus, we use a 2-way handshake to put
the other core into a while loop with interrupts disabled.
*/
// Disable interrupts so that the current core isn't preempted.
taskDISABLE_INTERRUPTS();
#if !CONFIG_FREERTOS_UNICORE
// For SMP, we need to do a 2-way handshake to ensure the other core spins with interrupts disabled.
start_ready_flag = true;
while (!start_resp_flag) {
;
}
#endif // !CONFIG_FREERTOS_UNICORE
}
static void check_snapshots(TaskHandle_t *task_list, int num_tasks, TaskSnapshot_t *task_snapshots, UBaseType_t num_snapshots)
{
// Check task snapshots. Every created task should be found in the task snapshot
for (int i = 0; i < num_tasks; i++) {
bool found = false;
for (int j = 0; j < num_snapshots; j++) {
if (task_list[i] == (TaskHandle_t)task_snapshots[j].pxTCB) {
found = true;
break;
}
}
TEST_ASSERT(found);
}
}
static void teardown(TaskHandle_t *task_list, int num_tasks, UBaseType_t old_priority)
{
#if !CONFIG_FREERTOS_UNICORE
// Signal to the other core that it can exit the loop
stop_flag = true;
#endif // !CONFIG_FREERTOS_UNICORE
// Reenable interrupts on the current core.
taskENABLE_INTERRUPTS();
for (int i = 0; i < num_tasks; i++) {
vTaskDelete(task_list[i]);
}
// Restore priority
vTaskPrioritySet(NULL, old_priority);
// Short delay to allow tasks to clean up
vTaskDelay(10);
}
TEST_CASE("Task snapshot: Get all", "[freertos]")
{
// Short delay to allow both cores to spin up
vTaskDelay(10);
TaskHandle_t task_list[TEST_MAX_TASKS_NUM];
int num_tasks;
UBaseType_t old_priority;
setup(task_list, &num_tasks, &old_priority);
// Get task snapshots using uxTaskGetSnapshotAll()
TaskSnapshot_t task_snapshots[TEST_MAX_TASKS_NUM];
UBaseType_t tcb_size;
UBaseType_t num_snapshots;
num_snapshots = uxTaskGetSnapshotAll(task_snapshots, TEST_MAX_TASKS_NUM, &tcb_size);
TEST_ASSERT_LESS_OR_EQUAL(TEST_MAX_TASKS_NUM, num_snapshots);
check_snapshots(task_list, num_tasks, task_snapshots, num_snapshots);
teardown(task_list, num_tasks, old_priority);
}
TEST_CASE("Task snapshot: Iterate", "[freertos]")
{
// Short delay to allow both cores to spin up
vTaskDelay(10);
TaskHandle_t task_list[TEST_MAX_TASKS_NUM];
int num_tasks;
UBaseType_t old_priority;
setup(task_list, &num_tasks, &old_priority);
// Get task snapshots using pxTaskGetNext() and vTaskGetSnapshot()
TaskSnapshot_t task_snapshots[TEST_MAX_TASKS_NUM];
UBaseType_t num_snapshots = 0;
TaskHandle_t cur_task_handle = pxTaskGetNext(NULL);
while (cur_task_handle != NULL) {
// Get the task's snapshot
BaseType_t Result = vTaskGetSnapshot(cur_task_handle, &task_snapshots[num_snapshots]);
TEST_ASSERT_EQUAL(pdTRUE, Result);
num_snapshots++;
cur_task_handle = pxTaskGetNext(cur_task_handle);
}
TEST_ASSERT_LESS_OR_EQUAL(TEST_MAX_TASKS_NUM, num_snapshots);
check_snapshots(task_list, num_tasks, task_snapshots, num_snapshots);
teardown(task_list, num_tasks, old_priority);
}
#endif // CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT