mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-08 04:02:27 +00:00
Try to merge tail with next block when splitting
When splitting a memory block, check if the next block is free. If it is, then just extend it upwards instead of creating a new block. This fixes a bug where when shrinking existing allocations would result in irreversible free space fragmentation. When testing on the host, test all the poisoning configurations.
This commit is contained in:

committed by
Angus Gratton

parent
393f3da37c
commit
4b7eb1792e
@@ -1,49 +1,18 @@
|
||||
TEST_PROGRAM=test_multi_heap
|
||||
all: $(TEST_PROGRAM)
|
||||
test: test_poisoning_disabled \
|
||||
test_poisoning_light \
|
||||
test_poisoning_comprehensive
|
||||
|
||||
SOURCE_FILES = $(abspath \
|
||||
../multi_heap.c \
|
||||
../multi_heap_poisoning.c \
|
||||
test_multi_heap.cpp \
|
||||
main.cpp \
|
||||
)
|
||||
test_poisoning_disabled:
|
||||
@echo ==== HEAP_POISONING_DISABLED ====
|
||||
CPPFLAGS= $(MAKE) -f Makefile.test clean test
|
||||
|
||||
INCLUDE_FLAGS = -I../include -I../../../tools/catch
|
||||
test_poisoning_light:
|
||||
@echo ==== HEAP_POISONING_LIGHT ====
|
||||
CPPFLAGS=-DCONFIG_HEAP_POISONING_LIGHT $(MAKE) -f Makefile.test clean test
|
||||
|
||||
GCOV ?= gcov
|
||||
test_poisoning_comprehensive:
|
||||
@echo ==== HEAP_POISONING_COMPREHENSIVE ====
|
||||
CPPFLAGS=-DCONFIG_HEAP_POISONING_COMPREHENSIVE $(MAKE) -f Makefile.test clean test
|
||||
|
||||
CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g -fstack-protector-all -m32
|
||||
CFLAGS += -Wall -Werror -fprofile-arcs -ftest-coverage
|
||||
CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -ftest-coverage
|
||||
LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage -m32
|
||||
|
||||
OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))
|
||||
|
||||
COVERAGE_FILES = $(OBJ_FILES:.o=.gc*)
|
||||
|
||||
$(TEST_PROGRAM): $(OBJ_FILES)
|
||||
g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES)
|
||||
|
||||
$(OUTPUT_DIR):
|
||||
mkdir -p $(OUTPUT_DIR)
|
||||
|
||||
test: $(TEST_PROGRAM)
|
||||
./$(TEST_PROGRAM)
|
||||
|
||||
$(COVERAGE_FILES): $(TEST_PROGRAM) test
|
||||
|
||||
coverage.info: $(COVERAGE_FILES)
|
||||
find ../ -name "*.gcno" -exec $(GCOV) -r -pb {} +
|
||||
lcov --capture --directory $(abspath ../) --no-external --output-file coverage.info --gcov-tool $(GCOV)
|
||||
|
||||
coverage_report: coverage.info
|
||||
genhtml coverage.info --output-directory coverage_report
|
||||
@echo "Coverage report is in coverage_report/index.html"
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ_FILES) $(TEST_PROGRAM)
|
||||
rm -f $(COVERAGE_FILES) *.gcov
|
||||
rm -rf coverage_report/
|
||||
rm -f coverage.info
|
||||
|
||||
.PHONY: clean all test
|
||||
%:
|
||||
CPPFLAGS=-DCONFIG_HEAP_POISONING_COMPREHENSIVE $(MAKE) -f Makefile.test $@
|
||||
|
49
components/heap/test_multi_heap_host/Makefile.test
Normal file
49
components/heap/test_multi_heap_host/Makefile.test
Normal file
@@ -0,0 +1,49 @@
|
||||
TEST_PROGRAM=test_multi_heap
|
||||
all: $(TEST_PROGRAM)
|
||||
|
||||
SOURCE_FILES = $(abspath \
|
||||
../multi_heap.c \
|
||||
../multi_heap_poisoning.c \
|
||||
test_multi_heap.cpp \
|
||||
main.cpp \
|
||||
)
|
||||
|
||||
INCLUDE_FLAGS = -I../include -I../../../tools/catch
|
||||
|
||||
GCOV ?= gcov
|
||||
|
||||
CPPFLAGS += $(INCLUDE_FLAGS) -D CONFIG_LOG_DEFAULT_LEVEL -g -fstack-protector-all -m32
|
||||
CFLAGS += -Wall -Werror -fprofile-arcs -ftest-coverage
|
||||
CXXFLAGS += -std=c++11 -Wall -Werror -fprofile-arcs -ftest-coverage
|
||||
LDFLAGS += -lstdc++ -fprofile-arcs -ftest-coverage -m32
|
||||
|
||||
OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o))
|
||||
|
||||
COVERAGE_FILES = $(OBJ_FILES:.o=.gc*)
|
||||
|
||||
$(TEST_PROGRAM): $(OBJ_FILES)
|
||||
g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES)
|
||||
|
||||
$(OUTPUT_DIR):
|
||||
mkdir -p $(OUTPUT_DIR)
|
||||
|
||||
test: $(TEST_PROGRAM)
|
||||
./$(TEST_PROGRAM)
|
||||
|
||||
$(COVERAGE_FILES): $(TEST_PROGRAM) test
|
||||
|
||||
coverage.info: $(COVERAGE_FILES)
|
||||
find ../ -name "*.gcno" -exec $(GCOV) -r -pb {} +
|
||||
lcov --capture --directory $(abspath ../) --no-external --output-file coverage.info --gcov-tool $(GCOV)
|
||||
|
||||
coverage_report: coverage.info
|
||||
genhtml coverage.info --output-directory coverage_report
|
||||
@echo "Coverage report is in coverage_report/index.html"
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ_FILES) $(TEST_PROGRAM)
|
||||
rm -f $(COVERAGE_FILES) *.gcov
|
||||
rm -rf coverage_report/
|
||||
rm -f coverage.info
|
||||
|
||||
.PHONY: clean all test
|
@@ -76,7 +76,7 @@ TEST_CASE("multi_heap fragmentation", "[multi_heap]")
|
||||
|
||||
printf("allocated %p %p %p %p\n", p[0], p[1], p[2], p[3]);
|
||||
|
||||
REQUIRE( multi_heap_malloc(heap, alloc_size * 3) == NULL ); /* no room to allocate 3*alloc_size now */
|
||||
REQUIRE( multi_heap_malloc(heap, alloc_size * 5) == NULL ); /* no room to allocate 5*alloc_size now */
|
||||
|
||||
printf("4 allocations:\n");
|
||||
multi_heap_dump(heap);
|
||||
@@ -105,6 +105,103 @@ TEST_CASE("multi_heap fragmentation", "[multi_heap]")
|
||||
multi_heap_free(heap, big);
|
||||
}
|
||||
|
||||
/* Test that malloc/free does not leave free space fragmented */
|
||||
TEST_CASE("multi_heap defrag", "[multi_heap]")
|
||||
{
|
||||
void *p[4];
|
||||
uint8_t small_heap[512];
|
||||
multi_heap_info_t info, info2;
|
||||
multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
|
||||
|
||||
printf("0 ---\n");
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
multi_heap_get_info(heap, &info);
|
||||
REQUIRE( 0 == info.allocated_blocks );
|
||||
REQUIRE( 1 == info.free_blocks );
|
||||
|
||||
printf("1 ---\n");
|
||||
p[0] = multi_heap_malloc(heap, 128);
|
||||
p[1] = multi_heap_malloc(heap, 32);
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
|
||||
printf("2 ---\n");
|
||||
multi_heap_free(heap, p[0]);
|
||||
p[2] = multi_heap_malloc(heap, 64);
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( p[2] == p[0] );
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
|
||||
printf("3 ---\n");
|
||||
multi_heap_free(heap, p[2]);
|
||||
p[3] = multi_heap_malloc(heap, 32);
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( p[3] == p[0] );
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
|
||||
multi_heap_get_info(heap, &info2);
|
||||
REQUIRE( 2 == info2.allocated_blocks );
|
||||
REQUIRE( 2 == info2.free_blocks );
|
||||
|
||||
multi_heap_free(heap, p[0]);
|
||||
multi_heap_free(heap, p[1]);
|
||||
multi_heap_get_info(heap, &info2);
|
||||
REQUIRE( 0 == info2.allocated_blocks );
|
||||
REQUIRE( 1 == info2.free_blocks );
|
||||
REQUIRE( info.total_free_bytes == info2.total_free_bytes );
|
||||
}
|
||||
|
||||
/* Test that malloc/free does not leave free space fragmented
|
||||
Note: With fancy poisoning, realloc is implemented as malloc-copy-free and this test does not apply.
|
||||
*/
|
||||
#ifndef MULTI_HEAP_POISONING_SLOW
|
||||
TEST_CASE("multi_heap defrag realloc", "[multi_heap]")
|
||||
{
|
||||
void *p[4];
|
||||
uint8_t small_heap[512];
|
||||
multi_heap_info_t info, info2;
|
||||
multi_heap_handle_t heap = multi_heap_register(small_heap, sizeof(small_heap));
|
||||
|
||||
printf("0 ---\n");
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
multi_heap_get_info(heap, &info);
|
||||
REQUIRE( 0 == info.allocated_blocks );
|
||||
REQUIRE( 1 == info.free_blocks );
|
||||
|
||||
printf("1 ---\n");
|
||||
p[0] = multi_heap_malloc(heap, 128);
|
||||
p[1] = multi_heap_malloc(heap, 32);
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
|
||||
printf("2 ---\n");
|
||||
p[2] = multi_heap_realloc(heap, p[0], 64);
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( p[2] == p[0] );
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
|
||||
printf("3 ---\n");
|
||||
p[3] = multi_heap_realloc(heap, p[2], 32);
|
||||
multi_heap_dump(heap);
|
||||
REQUIRE( p[3] == p[0] );
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
|
||||
multi_heap_get_info(heap, &info2);
|
||||
REQUIRE( 2 == info2.allocated_blocks );
|
||||
REQUIRE( 2 == info2.free_blocks );
|
||||
|
||||
multi_heap_free(heap, p[0]);
|
||||
multi_heap_free(heap, p[1]);
|
||||
multi_heap_get_info(heap, &info2);
|
||||
REQUIRE( 0 == info2.allocated_blocks );
|
||||
REQUIRE( 1 == info2.free_blocks );
|
||||
REQUIRE( info.total_free_bytes == info2.total_free_bytes );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
TEST_CASE("multi_heap many random allocations", "[multi_heap]")
|
||||
{
|
||||
uint8_t big_heap[1024];
|
||||
@@ -329,7 +426,13 @@ TEST_CASE("multi_heap_realloc()", "[multi_heap]")
|
||||
REQUIRE( multi_heap_check(heap, true) );
|
||||
REQUIRE( f == b ); /* 'b' should be extended in-place, over space formerly occupied by 'd' */
|
||||
|
||||
uint32_t *g = (uint32_t *)multi_heap_realloc(heap, e, 128); /* not enough contiguous space left in the heap */
|
||||
#ifdef MULTI_HEAP_POISONING
|
||||
#define TOO_MUCH 92 + 1
|
||||
#else
|
||||
#define TOO_MUCH 128 + 1
|
||||
#endif
|
||||
/* not enough contiguous space left in the heap */
|
||||
uint32_t *g = (uint32_t *)multi_heap_realloc(heap, e, TOO_MUCH);
|
||||
REQUIRE( g == NULL );
|
||||
|
||||
multi_heap_free(heap, f);
|
||||
|
Reference in New Issue
Block a user