fix(fatfs): fix a memory leak bug when FF_USE_DYN_BUFFER was enabled

This commit is contained in:
LiPeng
2026-04-24 18:59:28 +08:00
parent 7c1fd3fbbf
commit 4f7b1af0f9
6 changed files with 138 additions and 57 deletions
@@ -1,3 +1,3 @@
idf_component_register(SRCS "test_fatfs_dyn_buffers.c"
INCLUDE_DIRS "."
REQUIRES wear_levelling fatfs vfs)
REQUIRES unity wear_levelling fatfs vfs)
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -7,10 +7,14 @@
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include "unity.h"
#include "unity_test_utils.h"
#include "wear_levelling.h"
#include "diskio_wl.h"
#include "esp_partition.h"
#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "vfs_fat_internal.h"
#include "esp_heap_caps.h"
#include "sdkconfig.h"
#include "ff.h"
@@ -21,10 +25,19 @@ static const char* TAG = "Test dynamic buffers";
static volatile bool g_alloc_count_enable = false;
static volatile int g_buffer_alloc_count = 0;
static esp_vfs_fat_mount_config_t g_mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
};
// Some resources are lazy allocated, the threshold is left for that case
#define TEST_MEMORY_LEAK_THRESHOLD (1280)
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
esp_partition_unload_all(); //clean up some of the esp_partition's allocations
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
}
void esp_heap_trace_alloc_hook(void* ptr, size_t size, uint32_t caps)
{
@@ -42,32 +55,28 @@ void esp_heap_trace_alloc_hook(void* ptr, size_t size, uint32_t caps)
}
}
void app_main(void)
TEST_CASE("(dyn_buffers) basic mounting and file operations", "[fatfs][dyn_buffers]")
{
esp_err_t err = ESP_OK;
esp_vfs_fat_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
};
wl_handle_t wl_handle;
err = esp_vfs_fat_spiflash_format_cfg_rw_wl("/spiflash", NULL, &g_mount_config);
esp_vfs_fat_spiflash_format_cfg_rw_wl("/spiflash", NULL, &mount_config);
ESP_LOGI(TAG, "Mounting FATFS");
g_mount_config.format_if_mount_failed = false,
mount_config.format_if_mount_failed = false,
g_alloc_count_enable = true;
err = esp_vfs_fat_spiflash_mount_rw_wl("/spiflash", NULL, &g_mount_config, &wl_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "FATFS mount failed with error: %d", err);
return;
}
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash", NULL, &mount_config, &wl_handle));
ESP_LOGI(TAG, "Mounted");
int fd = open("/spiflash/test.txt", O_RDWR|O_CREAT);
if (fd < 0) {
ESP_LOGE(TAG, "Failed opening file");
}
TEST_ASSERT(fd >= 0);
close(fd);
@@ -83,19 +92,66 @@ void app_main(void)
, g_buffer_alloc_count);
#if CONFIG_FATFS_USE_DYN_BUFFERS
if (g_buffer_alloc_count != 2) {
ESP_LOGE(TAG, "FATFS buffer should have been allocated once for each context (file and fatfs)");
return;
}
TEST_ASSERT_MESSAGE(g_buffer_alloc_count == 2, "FATFS buffer should have been allocated once for each context (file and fatfs)");
#else
if (g_buffer_alloc_count != 0) {
ESP_LOGE(TAG, "FATFS buffer should not have been allocated");
return;
}
TEST_ASSERT_MESSAGE(g_buffer_alloc_count == 0, "FATFS buffer should not have been allocated");
#endif
ESP_LOGI(TAG, "Done");
}
TEST_CASE("(dyn_buffers) File system data does not exist, format and remount", "[fatfs][dyn_buffers]")
{
const char test_par_lab[] = "storage2";
const esp_partition_t * part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, test_par_lab);
TEST_ASSERT(part != NULL);
esp_partition_erase_range(part, 0, part->size);
esp_vfs_fat_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
};
wl_handle_t wl_handle;
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash2", test_par_lab, &mount_config, &wl_handle));
esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash2", wl_handle);
}
TEST_CASE("(dyn_buffers) Unmount and then close the file", "[fatfs][dyn_buffers]")
{
const char test_par_lab[] = "storage2";
esp_vfs_fat_mount_config_t mount_config = {
.format_if_mount_failed = true,
.max_files = 5,
};
wl_handle_t wl_handle;
TEST_ESP_OK(esp_vfs_fat_spiflash_mount_rw_wl("/spiflash2", test_par_lab, &mount_config, &wl_handle));
BYTE pdrv = ff_diskio_get_pdrv_wl(wl_handle);
char drv[3] = {(char)('0' + pdrv), ':', 0};
vfs_fat_spiflash_ctx_t *ctx = get_vfs_fat_spiflash_ctx(wl_handle);
TEST_ASSERT_NOT_NULL(ctx);
FATFS *fs = ctx->fs;
TEST_ASSERT_NOT_NULL(fs);
int fd = open("/spiflash2/test.txt", O_RDWR|O_CREAT);
TEST_ASSERT(fd >= 0);
// Unmount fatfs only
f_mount(0, drv, 0);
close(fd);
f_mount(fs, drv, 1);
fd = open("/spiflash2/test.txt", O_RDWR|O_CREAT);
TEST_ASSERT(fd >= 0);
// Unmount fatfs and vfs
esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash2", wl_handle);
close(fd);
}
void app_main(void)
{
unity_run_menu();
}
@@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@@ -15,8 +15,4 @@ from pytest_embedded_idf.utils import idf_parametrize
)
@idf_parametrize('target', ['esp32'], indirect=['target'])
def test_fatfs_flash_dyn_buffers(config: str, dut: Dut) -> None:
dut.expect('Mounting FATFS')
dut.expect('Mounted')
dut.expect('Unmounting FATFS')
dut.expect('Unmounted')
dut.expect('Done')
dut.run_all_single_board_cases(timeout=30)