From 8ae05e6547a6b7745e98237f608deea65b7ace3a Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Sat, 29 Feb 2020 15:02:53 +0530 Subject: [PATCH] esp_wifi: fix occasional test failure due to memory leak indications Ensure that newly spawned task stack (dynamic) is getting freed up before test completion and thus preventing false memory leak indication failure. unit-test-app: add an API test_utils_task_delete This API ensures that dynamic memory of deleted task gets freed up before return. This helps for preventing false memory leak detections in test failures. --- components/esp_wifi/test/test_wifi_init.c | 23 +++++--- .../test_utils/include/test_utils.h | 9 ++- .../components/test_utils/test_utils.c | 59 ++++++++++++++++++- 3 files changed, 82 insertions(+), 9 deletions(-) diff --git a/components/esp_wifi/test/test_wifi_init.c b/components/esp_wifi/test/test_wifi_init.c index 8195f9c8dc..9932551afb 100644 --- a/components/esp_wifi/test/test_wifi_init.c +++ b/components/esp_wifi/test/test_wifi_init.c @@ -96,24 +96,27 @@ static void wifi_driver_can_start_on_APP_CPU_task(void* arg) TEST_ESP_OK(event_deinit()); ESP_LOGI(TAG, EMPH_STR("nvs_flash_deinit...")); TEST_ESP_OK(nvs_flash_deinit()); - xSemaphoreGive(*sema); ESP_LOGI(TAG, "exit task..."); - vTaskDelete(NULL); + xSemaphoreGive(*sema); + vTaskSuspend(NULL); } TEST_CASE("wifi driver can start on APP CPU", "[wifi_init]") { - TaskHandle_t th; + TaskHandle_t th = NULL; SemaphoreHandle_t sema = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(sema); printf("Creating tasks\n"); #ifndef CONFIG_FREERTOS_UNICORE xTaskCreatePinnedToCore(wifi_driver_can_start_on_APP_CPU_task, "wifi_driver_can_start_on_APP_CPU_task", 2048*2, &sema, 3, &th, 1); #else xTaskCreate(wifi_driver_can_start_on_APP_CPU_task, "wifi_driver_can_start_on_APP_CPU_task", 2048*2, &sema, 3, &th); #endif + TEST_ASSERT_NOT_NULL(th); xSemaphoreTake(sema, portMAX_DELAY); vSemaphoreDelete(sema); sema = NULL; + test_utils_task_delete(th); } static void wifi_start_stop_task(void* arg) @@ -148,22 +151,25 @@ static void wifi_start_stop_task(void* arg) nvs_flash_deinit(); ESP_LOGI(TAG, "test passed..."); xSemaphoreGive(*sema); - vTaskDelete(NULL); + vTaskSuspend(NULL); } TEST_CASE("Calling esp_wifi_stop() with start", "[wifi_init]") { - TaskHandle_t th; + TaskHandle_t th = NULL; SemaphoreHandle_t sema = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(sema); printf("Creating tasks\n"); #ifndef CONFIG_FREERTOS_UNICORE xTaskCreatePinnedToCore(wifi_start_stop_task, "wifi_start_stop_task", 2048*2, &sema, 3, &th, 0); #else xTaskCreate(wifi_start_stop_task, "wifi_start_stop_task", 2048*2, &sema, 3, &th); #endif + TEST_ASSERT_NOT_NULL(th); xSemaphoreTake(sema, portMAX_DELAY); vSemaphoreDelete(sema); sema = NULL; + test_utils_task_delete(th); } static void wifi_stop_task(void* arg) @@ -194,20 +200,23 @@ static void wifi_stop_task(void* arg) nvs_flash_deinit(); ESP_LOGI(TAG, "test passed..."); xSemaphoreGive(*sema); - vTaskDelete(NULL); + vTaskSuspend(NULL); } TEST_CASE("Calling esp_wifi_stop() without start", "[wifi_init]") { - TaskHandle_t th; + TaskHandle_t th = NULL; SemaphoreHandle_t sema = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL(sema); printf("Creating tasks\n"); #ifndef CONFIG_FREERTOS_UNICORE xTaskCreatePinnedToCore(wifi_stop_task, "wifi_stop_task", 2048*2, &sema, 3, &th, 0); #else xTaskCreate(wifi_stop_task, "wifi_stop_task", 2048*2, &sema, 3, &th); #endif + TEST_ASSERT_NOT_NULL(th); xSemaphoreTake(sema, portMAX_DELAY); vSemaphoreDelete(sema); sema = NULL; + test_utils_task_delete(th); } diff --git a/tools/unit-test-app/components/test_utils/include/test_utils.h b/tools/unit-test-app/components/test_utils/include/test_utils.h index d2d7f05380..aa809972cc 100644 --- a/tools/unit-test-app/components/test_utils/include/test_utils.h +++ b/tools/unit-test-app/components/test_utils/include/test_utils.h @@ -18,6 +18,8 @@ #include #include #include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" /* include performance pass standards header file */ #include "idf_performance.h" @@ -259,7 +261,6 @@ esp_err_t test_utils_set_leak_level(size_t leak_level, esp_type_leak_t type, esp size_t test_utils_get_leak_level(esp_type_leak_t type, esp_comp_leak_t component); - typedef struct test_utils_exhaust_memory_record_s *test_utils_exhaust_memory_rec; /** @@ -285,3 +286,9 @@ test_utils_exhaust_memory_rec test_utils_exhaust_memory(uint32_t caps, size_t li void test_utils_free_exhausted_memory(test_utils_exhaust_memory_rec rec); +/** + * @brief Delete task ensuring dynamic memory (for stack, tcb etc.) gets freed up immediately + * + * @param[in] thandle Handle of task to be deleted (should not be NULL or self handle) + */ +void test_utils_task_delete(TaskHandle_t thandle); diff --git a/tools/unit-test-app/components/test_utils/test_utils.c b/tools/unit-test-app/components/test_utils/test_utils.c index 7745f19c0f..0bbce43fae 100644 --- a/tools/unit-test-app/components/test_utils/test_utils.c +++ b/tools/unit-test-app/components/test_utils/test_utils.c @@ -19,6 +19,11 @@ #include "freertos/task.h" #include "esp_netif.h" #include "lwip/sockets.h" +#include "sdkconfig.h" +#if !CONFIG_FREERTOS_UNICORE +#include "esp_ipc.h" +#include "esp_freertos_hooks.h" +#endif const esp_partition_t *get_test_data_partition(void) { @@ -142,7 +147,6 @@ size_t test_utils_get_leak_level(esp_type_leak_t type_of_leak, esp_comp_leak_t c return leak_level; } - #define EXHAUST_MEMORY_ENTRIES 100 struct test_utils_exhaust_memory_record_s { @@ -179,3 +183,56 @@ void test_utils_free_exhausted_memory(test_utils_exhaust_memory_rec rec) free(rec); } +#if !CONFIG_FREERTOS_UNICORE +static SemaphoreHandle_t test_sem; + +static bool test_idle_hook_func(void) +{ + if (test_sem) { + xSemaphoreGive(test_sem); + } + return true; +} + +static void test_task_delete_func(void *arg) +{ + vTaskDelete(arg); +} +#endif // !CONFIG_FREERTOS_UNICORE + +void test_utils_task_delete(TaskHandle_t thandle) +{ + /* Self deletion can not free up associated task dynamic memory immediately, + * hence not recommended for test scenarios */ + TEST_ASSERT_NOT_NULL_MESSAGE(thandle, "test_utils_task_delete: handle is NULL"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(thandle, xTaskGetCurrentTaskHandle(), "test_utils_task_delete: handle is of currently executing task"); + +#if CONFIG_FREERTOS_UNICORE + vTaskDelete(thandle); +#else // CONFIG_FREERTOS_UNICORE + const BaseType_t tsk_affinity = xTaskGetAffinity(thandle); + const uint32_t core_id = xPortGetCoreID(); + + printf("Task_affinity: 0x%x, current_core: %d\n", tsk_affinity, core_id); + + if (tsk_affinity == tskNO_AFFINITY) { + /* For no affinity case, we wait for idle hook to trigger on different core */ + esp_err_t ret = esp_register_freertos_idle_hook_for_cpu(test_idle_hook_func, !core_id); + TEST_ASSERT_EQUAL_MESSAGE(ret, ESP_OK, "test_utils_task_delete: failed to register idle hook"); + vTaskDelete(thandle); + test_sem = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_NULL_MESSAGE(test_sem, "test_utils_task_delete: failed to create semaphore"); + xSemaphoreTake(test_sem, portMAX_DELAY); + esp_deregister_freertos_idle_hook_for_cpu(test_idle_hook_func, !core_id); + vSemaphoreDelete(test_sem); + test_sem = NULL; + } else if (tsk_affinity != core_id) { + /* Task affinity and current core are differnt, schedule IPC call (to delete task) + * on core where task is pinned to */ + esp_ipc_call_blocking(tsk_affinity, test_task_delete_func, thandle); + } else { + /* Task affinity and current core are same, so we can safely proceed for deletion */ + vTaskDelete(thandle); + } +#endif // !CONFIG_FREERTOS_UNICORE +}