feat(mempool)!: 添加内存追踪及修复多处内存泄漏

- 新增 mempool 模块用于统计内存分配次数并检测泄漏
- 启用 UNICSTL_MALLOC_CUSTOM 宏以接管标准库内存函数
- 修复 segarray 销毁时未释放 _mapfree 导致的内存泄漏
- 修复 darray 迭代器 next 方法中错误的对象访问方式
- 调整 segarray 不支持外部缓存
- 修复测试用例中未释放 arraylist 导致的误报
This commit is contained in:
建峰 2026-05-17 14:13:33 +08:00
parent 529cf60218
commit 82d0167c81
9 changed files with 192 additions and 67 deletions

26
include/mempool.h Normal file
View File

@ -0,0 +1,26 @@
/**
* @file mempool.h
* @author wenjf (Orig5826@163.com)
* @brief
* @version 0.1
* @date 2026-05-17
*
* @copyright Copyright (c) 2026
*
*/
#ifndef _MEMPOOL_H_
#define _MEMPOOL_H_
#include "unicstl_internal.h"
#ifdef UNICSTL_MALLOC_CUSTOM
extern void *unicstl_malloc(size_t size);
extern void *unicstl_calloc(size_t num, size_t size);
extern void *unicstl_realloc(void *ptr, size_t size);
extern void unicstl_free(void *ptr);
#endif
void mempool_init(void);
void mempool_deinit(void);
#endif // _MEMPOOL_H_

View File

@ -13,6 +13,8 @@
#include "unicstl_internal.h" #include "unicstl_internal.h"
#include "mempool.h"
#include "darray.h" #include "darray.h"
#include "linklist.h" #include "linklist.h"
#include "dlinklist.h" #include "dlinklist.h"

View File

@ -37,7 +37,7 @@
* *
*/ */
#define UNICSTL_MALLOC_ENABLE // malloc enable #define UNICSTL_MALLOC_ENABLE // malloc enable
// #define UNICSTL_MALLOC_CUSTOM // malloc custom support #define UNICSTL_MALLOC_CUSTOM // malloc custom support
/** /**

2
mk.bat
View File

@ -1,6 +1,8 @@
@REM D:\Lang\cmake-3.27.5-windows-x86_64\bin\cmake.EXE --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_C_COMPILER:FILEPATH=D:\Software\mingw64\bin\gcc.exe -DCMAKE_CXX_COMPILER:FILEPATH=D:\Software\mingw64\bin\g++.exe -SF:/OpenDemo/1_vsc_cmake -Bf:/OpenDemo/1_vsc_cmake/build -G "MinGW Makefiles" @REM D:\Lang\cmake-3.27.5-windows-x86_64\bin\cmake.EXE --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_C_COMPILER:FILEPATH=D:\Software\mingw64\bin\gcc.exe -DCMAKE_CXX_COMPILER:FILEPATH=D:\Software\mingw64\bin\g++.exe -SF:/OpenDemo/1_vsc_cmake -Bf:/OpenDemo/1_vsc_cmake/build -G "MinGW Makefiles"
@REM cmake -Bbuild -G "Visual Studio 17 2022" @REM cmake -Bbuild -G "Visual Studio 17 2022"
del ".\build\release\bin\test.exe"
cmake -B build -G "MinGW Makefiles" cmake -B build -G "MinGW Makefiles"
@REM cmake -B build -G "Unix Makefiles" @REM cmake -B build -G "Unix Makefiles"

View File

@ -221,10 +221,7 @@ const void *darray_iter_next(struct _iterator *iter)
{ {
iter->_index = iter->_index - 1; iter->_index = iter->_index - 1;
} }
return obj_at(self->obj, index, self->_obj_size); return self->at(self, index);
// log_debug("index:%zu", index);
// return self->at(self->obj, index); // TODO: 这里有问题,结构体指针都崩了
} }
iterator_t darray_iter(struct _darray *self, linear_order_t order) iterator_t darray_iter(struct _darray *self, linear_order_t order)

90
src/mempool.c Normal file
View File

@ -0,0 +1,90 @@
/**
* @file mempool.c
* @author wenjf (Orig5826@163.com)
* @brief
* @version 0.1
* @date 2026-05-17
*
* @copyright Copyright (c) 2026
*
*/
#include "mempool.h"
#ifdef UNICSTL_MALLOC_CUSTOM
typedef struct _mempool
{
size_t count_total;
size_t count_free;
size_t count_malloc;
size_t count_calloc;
size_t count_realloc;
}mempool_t;
static mempool_t mempool;
void mempool_init()
{
memset(&mempool, 0, sizeof(mempool_t));
}
void mempool_deinit()
{
printf("\n------------------------------ \n");
printf("count_total: %zu\n", mempool.count_total);
printf("count_free: %zu\n", mempool.count_free);
printf("count_malloc: %zu\n", mempool.count_malloc);
printf("count_calloc: %zu\n", mempool.count_calloc);
printf("count_realloc: %zu\n", mempool.count_realloc);
size_t leak = mempool.count_total - mempool.count_free;
if (mempool.count_total > mempool.count_free)
{
printf("\nERROR: maybe leak: %zu\n", leak);
}
else if (mempool.count_total < mempool.count_free)
{
printf("\nERROR: maybe free too many\n");
}
else
{
printf("\nOK: no leak\n");
}
memset(&mempool, 0, sizeof(mempool_t));
}
void *unicstl_malloc(size_t size)
{
mempool.count_malloc++;
mempool.count_total++;
return malloc(size);
}
void *unicstl_calloc(size_t num, size_t size)
{
mempool.count_calloc++;
mempool.count_total++;
return calloc(num, size);
}
void *unicstl_realloc(void *ptr, size_t size)
{
mempool.count_realloc++;
if(ptr == NULL)
{
mempool.count_total++;
}
return realloc(ptr, size);
}
void unicstl_free(void *ptr)
{
if (ptr != NULL)
{
mempool.count_free++;
free(ptr);
}
}
#endif

View File

@ -432,22 +432,33 @@ static bool segarray_clear(struct _segarray *self)
static void segarray_destory(struct _segarray *self) static void segarray_destory(struct _segarray *self)
{ {
unicstl_assert(self != NULL); unicstl_assert(self != NULL);
ringbuf_t map = self->_map; ringbuf_t map_used[2] = {
if (self->_dynamic && map != NULL) self->_map,
self->_mapfree
};
if(self->_dynamic == true)
{ {
rawbuf_t seg = NULL; rawbuf_t seg = NULL;
while(!map->empty(map))
ringbuf_t map = self->_map;
for(size_t i = 0; i < 2; i++)
{ {
if (map->pop_back(map, &seg)) map = map_used[i];
while(!map->empty(map))
{ {
if(seg == NULL) if (map->pop_back(map, &seg))
{ {
log_error("seg is NULL"); if(seg == NULL)
{
log_error("seg is NULL");
}
rawbuf_free(&seg);
} }
rawbuf_free(&seg);
} }
} }
ringbuf_free(&self->_map); ringbuf_free(&self->_map);
ringbuf_free(&self->_mapfree);
} }
log_debug("segarray destoryed!"); log_debug("segarray destoryed!");
} }
@ -540,11 +551,10 @@ iterator_t segarray_iter(struct _segarray *self, linear_order_t order)
* @param self * @param self
* @param obj_size * @param obj_size
* @param capacity * @param capacity
* @param mem_pool !!! mem_pool_size = capacity * obj_size
* @return true * @return true
* @return false * @return false
*/ */
bool segarray_init(struct _segarray *self, size_t obj_size, size_t capacity, void *mem_pool) static bool segarray_init(struct _segarray *self, size_t obj_size, size_t capacity)
{ {
unicstl_assert(self != NULL); unicstl_assert(self != NULL);
unicstl_assert(obj_size > 0); unicstl_assert(obj_size > 0);
@ -555,53 +565,6 @@ bool segarray_init(struct _segarray *self, size_t obj_size, size_t capacity, voi
self->_capacity = capacity; self->_capacity = capacity;
self->_segsize = capacity; self->_segsize = capacity;
if (mem_pool != NULL)
{
self->obj = (char *)mem_pool;
self->_dynamic = false;
}
else
{
self->_dynamic = true;
self->_map = ringbuf_new(sizeof(rawbuf_t), 8);
if (self->_map == NULL)
{
log_warn("self->_map new failed!");
return false;
}
self->_mapfree = ringbuf_new(sizeof(rawbuf_t), 8);
if (self->_mapfree == NULL)
{
log_warn("self->_mapfree new failed!");
ringbuf_free(&self->_map);
return false;
}
rawbuf_t seg = rawbuf_new(obj_size, self->_segsize);
if (seg == NULL)
{
log_warn("seg new failed!");
ringbuf_free(&self->_map);
ringbuf_free(&self->_mapfree);
return false;
}
// config first obj index in seg array
self->_seghead = clac_start_index(self->_segsize);
self->_segtail = self->_seghead;
// add first seg array to map
if(!self->_map->push_back(self->_map, &seg))
{
log_warn("self->_map push back failed!");
rawbuf_free(&seg);
ringbuf_free(&self->_map);
ringbuf_free(&self->_mapfree);
return false;
}
}
self->_destory = segarray_destory; self->_destory = segarray_destory;
// -------------------- public -------------------- // -------------------- public --------------------
@ -635,6 +598,46 @@ bool segarray_init(struct _segarray *self, size_t obj_size, size_t capacity, voi
// -------------------- debug -------------------- // -------------------- debug --------------------
self->print = segarray_print; self->print = segarray_print;
// -------------------- memory --------------------
self->_dynamic = true;
self->_map = ringbuf_new(sizeof(rawbuf_t), 8);
if (self->_map == NULL)
{
log_warn("self->_map new failed!");
return false;
}
self->_mapfree = ringbuf_new(sizeof(rawbuf_t), 8);
if (self->_mapfree == NULL)
{
log_warn("self->_mapfree new failed!");
ringbuf_free(&self->_map);
return false;
}
rawbuf_t seg = rawbuf_new(obj_size, self->_segsize);
if (seg == NULL)
{
log_warn("seg new failed!");
ringbuf_free(&self->_map);
ringbuf_free(&self->_mapfree);
return false;
}
// config first obj index in seg array
self->_seghead = clac_start_index(self->_segsize);
self->_segtail = self->_seghead;
// add first seg array to map
if(!self->_map->push_back(self->_map, &seg))
{
log_warn("self->_map push back failed!");
rawbuf_free(&seg);
ringbuf_free(&self->_map);
ringbuf_free(&self->_mapfree);
return false;
}
return true; return true;
} }
@ -648,7 +651,7 @@ segarray_t segarray_new(size_t obj_size, size_t capacity)
return NULL; return NULL;
} }
if (segarray_init(segarray, obj_size, capacity, NULL) != true) if (segarray_init(segarray, obj_size, capacity) != true)
{ {
log_warn("segarray init failed"); log_warn("segarray init failed");
unicstl_free(segarray); unicstl_free(segarray);

View File

@ -75,11 +75,12 @@ void tearDown(void)
int main(int argc, char const *argv[]) int main(int argc, char const *argv[])
{ {
log_init();
printf("----- Unicstl Unit Test -----\n"); printf("----- Unicstl Unit Test -----\n");
UNITY_BEGIN(); UNITY_BEGIN();
log_init();
mempool_init();
TEST_ADD(test_unicstl); TEST_ADD(test_unicstl);
TEST_ADD(test_linklist); TEST_ADD(test_linklist);
@ -94,15 +95,15 @@ int main(int argc, char const *argv[])
TEST_ADD(test_queue); TEST_ADD(test_queue);
TEST_ADD(test_stack); TEST_ADD(test_stack);
// TEST_ADD(test_list); TEST_ADD(test_list);
// TEST_ADD(test_heap); TEST_ADD(test_heap);
// TEST_ADD(test_tree); // TEST_ADD(test_tree);
// TEST_ADD(test_graph); // TEST_ADD(test_graph);
TEST_ADD(test_segarray); TEST_ADD(test_segarray);
TEST_ADD(test_arraylist); TEST_ADD(test_arraylist);
mempool_deinit();
log_deinit(); log_deinit();
return UNITY_END(); return UNITY_END();
} }

View File

@ -395,6 +395,8 @@ static void test_arraylist_at(void)
p_int = arraylist->at(arraylist, 9); p_int = arraylist->at(arraylist, 9);
TEST_ASSERT_EQUAL_INT(10, *p_int); TEST_ASSERT_EQUAL_INT(10, *p_int);
arraylist_free(&arraylist);
} }
static void test_arraylist_at_negative(void) static void test_arraylist_at_negative(void)
@ -420,6 +422,8 @@ static void test_arraylist_at_negative(void)
p_int = arraylist->at(arraylist, -1); p_int = arraylist->at(arraylist, -1);
TEST_ASSERT_EQUAL_INT(10, *p_int); TEST_ASSERT_EQUAL_INT(10, *p_int);
arraylist_free(&arraylist);
} }
static void test_arraylist_at_invalid(void) static void test_arraylist_at_invalid(void)