From 955b2212188a2fc67f550544cdbc36b3caafe881 Mon Sep 17 00:00:00 2001 From: jsalling Date: Mon, 1 Feb 2016 23:54:06 -0600 Subject: [PATCH] Add configuration option UNITY_EXCLUDE_STDLIB_MALLOC to Fixture This feature removes the dependency on malloc/free for constrained embedded systems without a heap. It uses a static heap inside Unity Fixture. Setting UNITY_INTERNAL_HEAP_SIZE_BYTES sizes the heap. Add tests for new option, add targets to makefile for running tests. UNITY_FIXTURE_MALLOC for Fixture use only, remove from unity_output_Spy.c. --- extras/fixture/src/unity_fixture.c | 47 +++++++++++--- .../src/unity_fixture_malloc_overrides.h | 12 +++- extras/fixture/test/Makefile | 18 +++++- extras/fixture/test/main/AllTests.c | 3 +- extras/fixture/test/unity_fixture_Test.c | 62 ++++++++++++++++++- .../fixture/test/unity_fixture_TestRunner.c | 8 +++ extras/fixture/test/unity_output_Spy.c | 4 +- 7 files changed, 139 insertions(+), 15 deletions(-) diff --git a/extras/fixture/src/unity_fixture.c b/extras/fixture/src/unity_fixture.c index 348d815..c8275f4 100644 --- a/extras/fixture/src/unity_fixture.c +++ b/extras/fixture/src/unity_fixture.c @@ -156,7 +156,12 @@ void UnityMalloc_MakeMallocFailAfterCount(int countdown) #undef calloc #undef realloc +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC +static unsigned char unity_heap[UNITY_INTERNAL_HEAP_SIZE_BYTES]; +static unsigned int heap_index; +#else #include +#endif typedef struct GuardBytes { @@ -171,19 +176,31 @@ void* unity_malloc(size_t size) { char* mem; Guard* guard; + size_t total_size = size + sizeof(Guard) + sizeof(end); if (malloc_fail_countdown != MALLOC_DONT_FAIL) { if (malloc_fail_countdown == 0) - return 0; + return NULL; malloc_fail_countdown--; } if (size == 0) return NULL; - malloc_count++; - - guard = (Guard*)UNITY_FIXTURE_MALLOC(size + sizeof(Guard) + sizeof(end)); +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + if (heap_index + total_size > UNITY_INTERNAL_HEAP_SIZE_BYTES) + { + guard = NULL; + } + else + { + guard = (Guard*) &unity_heap[heap_index]; + heap_index += total_size; + } +#else + guard = (Guard*)UNITY_FIXTURE_MALLOC(total_size); +#endif if (guard == NULL) return NULL; + malloc_count++; guard->size = size; mem = (char*)&(guard[1]); memcpy(&mem[size], end, sizeof(end)); @@ -206,7 +223,14 @@ static void release_memory(void* mem) guard--; malloc_count--; +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + if (mem == unity_heap + heap_index - guard->size - sizeof(end)) + { + heap_index -= (guard->size + sizeof(Guard) + sizeof(end)); + } +#else UNITY_FIXTURE_FREE(guard); +#endif } void unity_free(void* mem) @@ -218,7 +242,7 @@ void unity_free(void* mem) return; } - overrun = isOverrun(mem);//strcmp(&memAsChar[guard->size], end) != 0; + overrun = isOverrun(mem); release_memory(mem); if (overrun) { @@ -230,14 +254,13 @@ void* unity_calloc(size_t num, size_t size) { void* mem = unity_malloc(num * size); if (mem == NULL) return NULL; - memset(mem, 0, num*size); + memset(mem, 0, num * size); return mem; } void* unity_realloc(void* oldMem, size_t size) { Guard* guard = (Guard*)oldMem; -// char* memAsChar = (char*)oldMem; void* newMem; if (oldMem == NULL) return unity_malloc(size); @@ -257,10 +280,18 @@ void* unity_realloc(void* oldMem, size_t size) if (guard->size >= size) return oldMem; +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC // Optimization if memory is expandable + if (oldMem == unity_heap + heap_index - guard->size - sizeof(end) && + heap_index + size - guard->size <= UNITY_INTERNAL_HEAP_SIZE_BYTES) + { + release_memory(oldMem); // Not thread-safe, like unity_heap generally + return unity_malloc(size); // No memcpy since data is in place + } +#endif newMem = unity_malloc(size); if (newMem == NULL) return NULL; // Do not release old memory memcpy(newMem, oldMem, guard->size); - unity_free(oldMem); + release_memory(oldMem); return newMem; } diff --git a/extras/fixture/src/unity_fixture_malloc_overrides.h b/extras/fixture/src/unity_fixture_malloc_overrides.h index cc9c42b..e01a5fb 100644 --- a/extras/fixture/src/unity_fixture_malloc_overrides.h +++ b/extras/fixture/src/unity_fixture_malloc_overrides.h @@ -10,11 +10,21 @@ #include +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC +// Define this macro to remove the use of stdlib.h, malloc, and free. +// Many embedded systems do not have a heap or malloc/free by default. +// This internal unity_malloc() provides allocated memory deterministically from +// the end of an array only, unity_free() only releases from end-of-array, +// blocks are not coalesced, and memory not freed in LIFO order is stranded. + #ifndef UNITY_INTERNAL_HEAP_SIZE_BYTES + #define UNITY_INTERNAL_HEAP_SIZE_BYTES 256 + #endif +#endif + // These functions are used by the Unity Fixture to allocate and release memory // on the heap and can be overridden with platform-specific implementations. // For example, when using FreeRTOS UNITY_FIXTURE_MALLOC becomes pvPortMalloc() // and UNITY_FIXTURE_FREE becomes vPortFree(). - #if !defined(UNITY_FIXTURE_MALLOC) || !defined(UNITY_FIXTURE_FREE) #define UNITY_FIXTURE_MALLOC(size) malloc(size) #define UNITY_FIXTURE_FREE(ptr) free(ptr) diff --git a/extras/fixture/test/Makefile b/extras/fixture/test/Makefile index 04ec722..8888d6f 100644 --- a/extras/fixture/test/Makefile +++ b/extras/fixture/test/Makefile @@ -14,6 +14,22 @@ SRC = ../src/unity_fixture.c \ INC_DIR = -I../src -I../../../src/ TARGET = fixture_tests.exe -all: +all: default noStdlibMalloc 32bits + +default: $(CC) $(CFLAGS) $(DEFINES) $(SRC) $(INC_DIR) -o $(TARGET) + @ echo "default build" ./$(TARGET) + +32bits: + $(CC) $(CFLAGS) $(DEFINES) $(SRC) $(INC_DIR) -o $(TARGET) -m32 + @ echo "32bits build" + ./$(TARGET) + +noStdlibMalloc: + $(CC) $(CFLAGS) $(DEFINES) $(SRC) $(INC_DIR) -o $(TARGET) -D UNITY_EXCLUDE_STDLIB_MALLOC + @ echo "build with noStdlibMalloc" + ./$(TARGET) + +clangEverything: + $(CC) $(CFLAGS) $(DEFINES) $(SRC) $(INC_DIR) -o $(TARGET) -m64 -Weverything # || true #prevents make from failing diff --git a/extras/fixture/test/main/AllTests.c b/extras/fixture/test/main/AllTests.c index 7d0577e..7cfbe8e 100644 --- a/extras/fixture/test/main/AllTests.c +++ b/extras/fixture/test/main/AllTests.c @@ -11,7 +11,8 @@ static void runAllTests(void) { RUN_TEST_GROUP(UnityFixture); RUN_TEST_GROUP(UnityCommandOptions); - RUN_TEST_GROUP(LeakDetection) + RUN_TEST_GROUP(LeakDetection); + RUN_TEST_GROUP(InternalMalloc); } int main(int argc, const char* argv[]) diff --git a/extras/fixture/test/unity_fixture_Test.c b/extras/fixture/test/unity_fixture_Test.c index 5c44627..9ca3bfd 100644 --- a/extras/fixture/test/unity_fixture_Test.c +++ b/extras/fixture/test/unity_fixture_Test.c @@ -82,7 +82,7 @@ TEST(UnityFixture, ReallocLargerNeeded) CHECK(m1); strcpy((char*)m1, "123456789"); m2 = realloc(m1, 15); - CHECK(m1 != m2); + // CHECK(m1 != m2); //Depends on implementation STRCMP_EQUAL("123456789", m2); free(m2); } @@ -136,7 +136,7 @@ TEST(UnityFixture, PointerSet) TEST(UnityFixture, FreeNULLSafety) { - unity_free(NULL); + free(NULL); } //------------------------------------------------------------ @@ -290,7 +290,11 @@ TEST_GROUP(LeakDetection); TEST_SETUP(LeakDetection) { +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + UnityOutputCharSpy_Create(200); +#else UnityOutputCharSpy_Create(1000); +#endif } TEST_TEAR_DOWN(LeakDetection) @@ -376,3 +380,57 @@ TEST(LeakDetection, BufferOverrunFoundDuringRealloc) CHECK(strstr(UnityOutputCharSpy_Get(), "Buffer overrun detected during realloc()")); #endif } + +TEST_GROUP(InternalMalloc); + +TEST_SETUP(InternalMalloc) { } +TEST_TEAR_DOWN(InternalMalloc) { } + +TEST(InternalMalloc, MallocPastBufferFails) +{ +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + void* m = malloc(UNITY_INTERNAL_HEAP_SIZE_BYTES/2 + 1); + TEST_ASSERT_NOT_NULL(m); + void* n = malloc(UNITY_INTERNAL_HEAP_SIZE_BYTES/2); + TEST_ASSERT_NULL(n); + free(m); +#endif +} + +TEST(InternalMalloc, CallocPastBufferFails) +{ +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + void* m = calloc(1, UNITY_INTERNAL_HEAP_SIZE_BYTES/2 + 1); + TEST_ASSERT_NOT_NULL(m); + void* n = calloc(1, UNITY_INTERNAL_HEAP_SIZE_BYTES/2); + TEST_ASSERT_NULL(n); + free(m); +#endif +} + +TEST(InternalMalloc, MallocThenReallocGrowsMemoryInPlace) +{ +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + void* m = malloc(UNITY_INTERNAL_HEAP_SIZE_BYTES/2 + 1); + TEST_ASSERT_NOT_NULL(m); + void* n = realloc(m, UNITY_INTERNAL_HEAP_SIZE_BYTES/2 + 9); + TEST_ASSERT_EQUAL(m, n); + free(n); +#endif +} + +TEST(InternalMalloc, ReallocFailDoesNotFreeMem) +{ +#ifdef UNITY_EXCLUDE_STDLIB_MALLOC + void* m = malloc(UNITY_INTERNAL_HEAP_SIZE_BYTES/2); + TEST_ASSERT_NOT_NULL(m); + void* n1 = malloc(10); + void* out_of_mem = realloc(n1, UNITY_INTERNAL_HEAP_SIZE_BYTES/2 + 1); + TEST_ASSERT_NULL(out_of_mem); + void* n2 = malloc(10); + TEST_ASSERT_NOT_EQUAL(n2, n1); + free(n2); + free(n1); + free(m); +#endif +} diff --git a/extras/fixture/test/unity_fixture_TestRunner.c b/extras/fixture/test/unity_fixture_TestRunner.c index 6c1a90c..4f5eefc 100644 --- a/extras/fixture/test/unity_fixture_TestRunner.c +++ b/extras/fixture/test/unity_fixture_TestRunner.c @@ -41,3 +41,11 @@ TEST_GROUP_RUNNER(LeakDetection) RUN_TEST_CASE(LeakDetection, BufferOverrunFoundDuringFree); RUN_TEST_CASE(LeakDetection, BufferOverrunFoundDuringRealloc); } + +TEST_GROUP_RUNNER(InternalMalloc) +{ + RUN_TEST_CASE(InternalMalloc, MallocPastBufferFails); + RUN_TEST_CASE(InternalMalloc, CallocPastBufferFails); + RUN_TEST_CASE(InternalMalloc, MallocThenReallocGrowsMemoryInPlace); + RUN_TEST_CASE(InternalMalloc, ReallocFailDoesNotFreeMem); +} diff --git a/extras/fixture/test/unity_output_Spy.c b/extras/fixture/test/unity_output_Spy.c index 767471a..36bcf37 100644 --- a/extras/fixture/test/unity_output_Spy.c +++ b/extras/fixture/test/unity_output_Spy.c @@ -22,7 +22,7 @@ void UnityOutputCharSpy_Create(int s) size = s; count = 0; spy_enable = 0; - buffer = UNITY_FIXTURE_MALLOC(size); + buffer = malloc(size); TEST_ASSERT_NOT_NULL_MESSAGE(buffer, "Internal malloc failed in Spy Create():" __FILE__); memset(buffer, 0, size); } @@ -30,7 +30,7 @@ void UnityOutputCharSpy_Create(int s) void UnityOutputCharSpy_Destroy(void) { size = 0; - UNITY_FIXTURE_FREE(buffer); + free(buffer); } int UnityOutputCharSpy_OutputChar(int c)