freertos and malloc

chegewara
Posts: 2371
Joined: Wed Jun 14, 2017 9:00 pm

freertos and malloc

Postby chegewara » Tue Oct 12, 2021 5:42 am

Long story short i am having strangely behaving vTaskDelete in platformio with arduino. Deleted tasks are still showing non 0 tasks stack size, like never being deleted.

To play with it a bit i made short test code with esp-idf and here all get really strange. Two almost the same pieces of code with malloc/calloc, both are without free allocated memory. Now its getting strange.
1. This code does not cause memory leak

Code: Select all

/* Hello World Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"

#define TAG ""

void directTasksStatus()
{
    UBaseType_t uxArraySize = uxTaskGetNumberOfTasks();
    TaskStatus_t *pxTaskStatusArray;
    uint32_t pulTotalRunTime = 0;


    pxTaskStatusArray = (TaskStatus_t *)malloc(sizeof(TaskStatus_t) * uxArraySize);
    uxTaskGetSystemState(&pxTaskStatusArray[0], uxArraySize, &pulTotalRunTime);
    for (int i = 0; i < uxArraySize; i++)
    {
        ESP_LOGI(TAG, "[%2d] %*s => stack remaining: %*d", i, 20, pxTaskStatusArray[i].pcTaskName, 4, pxTaskStatusArray[i].usStackHighWaterMark);
    }
    heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
    if (!heap_caps_check_integrity_all(true))
    {
        heap_caps_dump_all();
        // heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
    }
    ESP_LOGI(TAG, "directTasksStatus: stack remaining: %d", uxTaskGetStackHighWaterMark(NULL));
    free(pxTaskStatusArray);
}

static void test_task(void* p)
{
    while(1)
    {
        // printf("malloc\n");
        void* prv = calloc(1, 10000);
        uint32_t free_heap_size = 0, min_free_heap_size = 0;

        free_heap_size = esp_get_free_heap_size();
        min_free_heap_size = esp_get_minimum_free_heap_size();
        ESP_LOGI(TAG, "directTasksStatus: free heap size = %d \t  min_free_heap_size = %d", free_heap_size, min_free_heap_size);
        vTaskDelay(1000);
    }
}

TaskHandle_t handle1;
TaskHandle_t handle2;
TaskHandle_t handle3;
TaskHandle_t handle4;

void app_main(void)
{
    while(1){
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle1);
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle2);
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle3);
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle4);
        directTasksStatus();
        vTaskDelay(1000);
        vTaskDelete(handle1);
        vTaskDelete(handle2);
        vTaskDelete(handle3);
        vTaskDelete(handle4);
        if(handle1) printf("task1 stack: %d\n", uxTaskGetStackHighWaterMark(handle1));
        if(handle2) printf("task2 stack: %d\n", uxTaskGetStackHighWaterMark(handle2));
        if(handle3) printf("task3 stack: %d\n", uxTaskGetStackHighWaterMark(handle3));
        if(handle4) printf("task4 stack: %d\n", uxTaskGetStackHighWaterMark(handle4));
        vTaskDelay(1000);
        directTasksStatus();
        vTaskDelay(1000);
    }
}
2. This code is causing memory leak:

Code: Select all

/* Hello World Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"

#define TAG ""

void directTasksStatus()
{
    UBaseType_t uxArraySize = uxTaskGetNumberOfTasks();
    TaskStatus_t *pxTaskStatusArray;
    uint32_t pulTotalRunTime = 0;


    pxTaskStatusArray = (TaskStatus_t *)malloc(sizeof(TaskStatus_t) * uxArraySize);
    uxTaskGetSystemState(&pxTaskStatusArray[0], uxArraySize, &pulTotalRunTime);
    for (int i = 0; i < uxArraySize; i++)
    {
        ESP_LOGI(TAG, "[%2d] %*s => stack remaining: %*d", i, 20, pxTaskStatusArray[i].pcTaskName, 4, pxTaskStatusArray[i].usStackHighWaterMark);
    }
    heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
    if (!heap_caps_check_integrity_all(true))
    {
        heap_caps_dump_all();
        // heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
    }
    ESP_LOGI(TAG, "directTasksStatus: stack remaining: %d", uxTaskGetStackHighWaterMark(NULL));
    free(pxTaskStatusArray);
}

static void test_task(void* p)
{
    while(1)
    {
        // printf("malloc\n");
        void* prv = calloc(1, 10000);
        uint32_t free_heap_size = 0, min_free_heap_size = 0;

        free_heap_size = esp_get_free_heap_size();
        min_free_heap_size = esp_get_minimum_free_heap_size();
        if( prv)ESP_LOGI(TAG, "directTasksStatus: free heap size = %d \t  min_free_heap_size = %d", free_heap_size, min_free_heap_size);
        vTaskDelay(1000);
    }
}

TaskHandle_t handle1;
TaskHandle_t handle2;
TaskHandle_t handle3;
TaskHandle_t handle4;

void app_main(void)
{
    while(1){
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle1);
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle2);
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle3);
        xTaskCreate(test_task, "test", 4 * 1024, NULL, 5, &handle4);
        directTasksStatus();
        vTaskDelay(1000);
        vTaskDelete(handle1);
        vTaskDelete(handle2);
        vTaskDelete(handle3);
        vTaskDelete(handle4);
        if(handle1) printf("task1 stack: %d\n", uxTaskGetStackHighWaterMark(handle1));
        if(handle2) printf("task2 stack: %d\n", uxTaskGetStackHighWaterMark(handle2));
        if(handle3) printf("task3 stack: %d\n", uxTaskGetStackHighWaterMark(handle3));
        if(handle4) printf("task4 stack: %d\n", uxTaskGetStackHighWaterMark(handle4));
        vTaskDelay(1000);
        directTasksStatus();
        vTaskDelay(1000);
    }
}
The only difference is in this line of code:

Code: Select all

if( prv)ESP_LOGI(TAG, "directTasksStatus: free heap size = %d \t  min_free_heap_size = %d", free_heap_size, min_free_heap_size);
Any thoughts?

boarchuz
Posts: 606
Joined: Tue Aug 21, 2018 5:28 am

Re: freertos and malloc

Postby boarchuz » Tue Oct 12, 2021 7:38 am

I think that the allocation is being optimised out in the first case, because prv is unused. A quick search says that some compilers will do that, even though it's a little controversial whether they should be allowed to or not.

Is uxTaskGetStackHighWaterMark for a deleted task permitted? My gut says no, but might still work if you get in and out before the idle task has actually had a chance to clean it up and/or the memory hasn't been overwritten yet.

chegewara
Posts: 2371
Joined: Wed Jun 14, 2017 9:00 pm

Re: freertos and malloc

Postby chegewara » Tue Oct 12, 2021 7:49 am

I didnt know about optimization by compilers, that makes sense.
boarchuz wrote: Is uxTaskGetStackHighWaterMark for a deleted task permitted? My gut says no, but might still work if you get in and out before the idle task has actually had a chance to clean it up and/or the memory hasn't been overwritten yet.
That also makes sense to me now. When i add vTaskDelay() in that test app right after every vTaskDelete() then uxTaskGetStackHighWaterMark causing crash. And in my real app i am having few more tasks than i am deleting before OTA update, so it explains that IDLE task does not have change to run ever and do some cleanup.
I know its somehow basics when you are working with freertos, but all this bothered me few hours today, so you are life saving.

Thanks.

Who is online

Users browsing this forum: Majestic-12 [Bot] and 64 guests