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.
This commit is contained in:
jsalling
2016-02-01 23:54:06 -06:00
parent ccb29e80c2
commit 955b221218
7 changed files with 139 additions and 15 deletions

View File

@ -156,7 +156,12 @@ void UnityMalloc_MakeMallocFailAfterCount(int countdown)
#undef calloc #undef calloc
#undef realloc #undef realloc
#ifdef UNITY_EXCLUDE_STDLIB_MALLOC
static unsigned char unity_heap[UNITY_INTERNAL_HEAP_SIZE_BYTES];
static unsigned int heap_index;
#else
#include <stdlib.h> #include <stdlib.h>
#endif
typedef struct GuardBytes typedef struct GuardBytes
{ {
@ -171,19 +176,31 @@ void* unity_malloc(size_t size)
{ {
char* mem; char* mem;
Guard* guard; Guard* guard;
size_t total_size = size + sizeof(Guard) + sizeof(end);
if (malloc_fail_countdown != MALLOC_DONT_FAIL) if (malloc_fail_countdown != MALLOC_DONT_FAIL)
{ {
if (malloc_fail_countdown == 0) if (malloc_fail_countdown == 0)
return 0; return NULL;
malloc_fail_countdown--; malloc_fail_countdown--;
} }
if (size == 0) return NULL; if (size == 0) return NULL;
malloc_count++; #ifdef UNITY_EXCLUDE_STDLIB_MALLOC
if (heap_index + total_size > UNITY_INTERNAL_HEAP_SIZE_BYTES)
guard = (Guard*)UNITY_FIXTURE_MALLOC(size + sizeof(Guard) + sizeof(end)); {
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; if (guard == NULL) return NULL;
malloc_count++;
guard->size = size; guard->size = size;
mem = (char*)&(guard[1]); mem = (char*)&(guard[1]);
memcpy(&mem[size], end, sizeof(end)); memcpy(&mem[size], end, sizeof(end));
@ -206,7 +223,14 @@ static void release_memory(void* mem)
guard--; guard--;
malloc_count--; 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); UNITY_FIXTURE_FREE(guard);
#endif
} }
void unity_free(void* mem) void unity_free(void* mem)
@ -218,7 +242,7 @@ void unity_free(void* mem)
return; return;
} }
overrun = isOverrun(mem);//strcmp(&memAsChar[guard->size], end) != 0; overrun = isOverrun(mem);
release_memory(mem); release_memory(mem);
if (overrun) if (overrun)
{ {
@ -230,14 +254,13 @@ void* unity_calloc(size_t num, size_t size)
{ {
void* mem = unity_malloc(num * size); void* mem = unity_malloc(num * size);
if (mem == NULL) return NULL; if (mem == NULL) return NULL;
memset(mem, 0, num*size); memset(mem, 0, num * size);
return mem; return mem;
} }
void* unity_realloc(void* oldMem, size_t size) void* unity_realloc(void* oldMem, size_t size)
{ {
Guard* guard = (Guard*)oldMem; Guard* guard = (Guard*)oldMem;
// char* memAsChar = (char*)oldMem;
void* newMem; void* newMem;
if (oldMem == NULL) return unity_malloc(size); 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; 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); newMem = unity_malloc(size);
if (newMem == NULL) return NULL; // Do not release old memory if (newMem == NULL) return NULL; // Do not release old memory
memcpy(newMem, oldMem, guard->size); memcpy(newMem, oldMem, guard->size);
unity_free(oldMem); release_memory(oldMem);
return newMem; return newMem;
} }

View File

@ -10,11 +10,21 @@
#include <stddef.h> #include <stddef.h>
#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 // These functions are used by the Unity Fixture to allocate and release memory
// on the heap and can be overridden with platform-specific implementations. // on the heap and can be overridden with platform-specific implementations.
// For example, when using FreeRTOS UNITY_FIXTURE_MALLOC becomes pvPortMalloc() // For example, when using FreeRTOS UNITY_FIXTURE_MALLOC becomes pvPortMalloc()
// and UNITY_FIXTURE_FREE becomes vPortFree(). // and UNITY_FIXTURE_FREE becomes vPortFree().
#if !defined(UNITY_FIXTURE_MALLOC) || !defined(UNITY_FIXTURE_FREE) #if !defined(UNITY_FIXTURE_MALLOC) || !defined(UNITY_FIXTURE_FREE)
#define UNITY_FIXTURE_MALLOC(size) malloc(size) #define UNITY_FIXTURE_MALLOC(size) malloc(size)
#define UNITY_FIXTURE_FREE(ptr) free(ptr) #define UNITY_FIXTURE_FREE(ptr) free(ptr)

View File

@ -14,6 +14,22 @@ SRC = ../src/unity_fixture.c \
INC_DIR = -I../src -I../../../src/ INC_DIR = -I../src -I../../../src/
TARGET = fixture_tests.exe TARGET = fixture_tests.exe
all: all: default noStdlibMalloc 32bits
default:
$(CC) $(CFLAGS) $(DEFINES) $(SRC) $(INC_DIR) -o $(TARGET) $(CC) $(CFLAGS) $(DEFINES) $(SRC) $(INC_DIR) -o $(TARGET)
@ echo "default build"
./$(TARGET) ./$(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

View File

@ -11,7 +11,8 @@ static void runAllTests(void)
{ {
RUN_TEST_GROUP(UnityFixture); RUN_TEST_GROUP(UnityFixture);
RUN_TEST_GROUP(UnityCommandOptions); RUN_TEST_GROUP(UnityCommandOptions);
RUN_TEST_GROUP(LeakDetection) RUN_TEST_GROUP(LeakDetection);
RUN_TEST_GROUP(InternalMalloc);
} }
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])

View File

@ -82,7 +82,7 @@ TEST(UnityFixture, ReallocLargerNeeded)
CHECK(m1); CHECK(m1);
strcpy((char*)m1, "123456789"); strcpy((char*)m1, "123456789");
m2 = realloc(m1, 15); m2 = realloc(m1, 15);
CHECK(m1 != m2); // CHECK(m1 != m2); //Depends on implementation
STRCMP_EQUAL("123456789", m2); STRCMP_EQUAL("123456789", m2);
free(m2); free(m2);
} }
@ -136,7 +136,7 @@ TEST(UnityFixture, PointerSet)
TEST(UnityFixture, FreeNULLSafety) TEST(UnityFixture, FreeNULLSafety)
{ {
unity_free(NULL); free(NULL);
} }
//------------------------------------------------------------ //------------------------------------------------------------
@ -290,7 +290,11 @@ TEST_GROUP(LeakDetection);
TEST_SETUP(LeakDetection) TEST_SETUP(LeakDetection)
{ {
#ifdef UNITY_EXCLUDE_STDLIB_MALLOC
UnityOutputCharSpy_Create(200);
#else
UnityOutputCharSpy_Create(1000); UnityOutputCharSpy_Create(1000);
#endif
} }
TEST_TEAR_DOWN(LeakDetection) TEST_TEAR_DOWN(LeakDetection)
@ -376,3 +380,57 @@ TEST(LeakDetection, BufferOverrunFoundDuringRealloc)
CHECK(strstr(UnityOutputCharSpy_Get(), "Buffer overrun detected during realloc()")); CHECK(strstr(UnityOutputCharSpy_Get(), "Buffer overrun detected during realloc()"));
#endif #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
}

View File

@ -41,3 +41,11 @@ TEST_GROUP_RUNNER(LeakDetection)
RUN_TEST_CASE(LeakDetection, BufferOverrunFoundDuringFree); RUN_TEST_CASE(LeakDetection, BufferOverrunFoundDuringFree);
RUN_TEST_CASE(LeakDetection, BufferOverrunFoundDuringRealloc); 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);
}

View File

@ -22,7 +22,7 @@ void UnityOutputCharSpy_Create(int s)
size = s; size = s;
count = 0; count = 0;
spy_enable = 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__); TEST_ASSERT_NOT_NULL_MESSAGE(buffer, "Internal malloc failed in Spy Create():" __FILE__);
memset(buffer, 0, size); memset(buffer, 0, size);
} }
@ -30,7 +30,7 @@ void UnityOutputCharSpy_Create(int s)
void UnityOutputCharSpy_Destroy(void) void UnityOutputCharSpy_Destroy(void)
{ {
size = 0; size = 0;
UNITY_FIXTURE_FREE(buffer); free(buffer);
} }
int UnityOutputCharSpy_OutputChar(int c) int UnityOutputCharSpy_OutputChar(int c)