aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--02-usart/include/kern/mem.h2
-rw-r--r--02-usart/src/kern/mem.c114
-rw-r--r--02-usart/tests/test_memory.c163
3 files changed, 236 insertions, 43 deletions
diff --git a/02-usart/include/kern/mem.h b/02-usart/include/kern/mem.h
index d150744..c0999f5 100644
--- a/02-usart/include/kern/mem.h
+++ b/02-usart/include/kern/mem.h
@@ -24,6 +24,8 @@ void* debug_halloc_get_prev_ptr(void* ptr);
int debug_halloc_assert_consistency(char* error, size_t len);
+void debug_print_blocks();
+
#endif
diff --git a/02-usart/src/kern/mem.c b/02-usart/src/kern/mem.c
index 79bcabf..5234fff 100644
--- a/02-usart/src/kern/mem.c
+++ b/02-usart/src/kern/mem.c
@@ -26,6 +26,8 @@ void* memset(void* dest, int c, size_t n);
typedef uint16_t halloc_off_t;
+#define CANARY 0x5a
+
// The sizes will count the number of WORDS allocated.
// Since there's a max size of 16k, only 12 bits will be
// needed for this.
@@ -34,19 +36,25 @@ typedef struct HALLOC_NODE {
uint32_t header;
struct {
/* Is this memory block currently in use (hasn't been hfree'd) */
- bool used:1;
+ uint8_t used:1;
/* Number of words allocated. Does not include the header. */
- uint16_t size:15;
+ uint16_t size:12;
/* The location of the previous block (in WORDS from offest) */
- halloc_off_t prev;
+ halloc_off_t prev:12;
+ uint8_t canary:7;
} PACKED;
};
uint8_t mem[]; /* The memory to use. */
} halloc_node_t;
+static_assert(offsetof(halloc_node_t, mem) == 4, "Offset check failed.");
+
halloc_node_t* halloc_start;
+#define halloc_node_out_of_range(node) \
+ ((uint8_t*) (node) == ((uint8_t*)&DATA_SEGMENT_STOP) + MAX_HEAP_SIZE)
+
#define halloc_node_next(cur) \
((halloc_node_t*)(((uint8_t*)(cur)) + (((cur)->size + 1) * 4)))
@@ -58,6 +66,9 @@ halloc_node_t* halloc_start;
#define halloc_node_get_off(node) \
(((uint32_t)(((uint8_t*)(node)) - ((uint8_t*)(halloc_start)))) / 4)
+#define get_halloc_node(mem) \
+ ((halloc_node_t*)(((uint8_t*)mem) - 4))
+
#define size_for(n) \
(((n) / 4) + ((n) % 4 != 0))
@@ -67,6 +78,7 @@ void* halloc(size_t size)
halloc_start = (halloc_node_t*) DATA_SEGMENT_STOP_ADDR;
memset(halloc_start, 0, sizeof(halloc_node_t));
halloc_start->size = (MAX_HEAP_SIZE / 4) - 1;
+ halloc_start->canary = CANARY;
}
size_t realsz = size_for(size); /* Clip the size to the nearest word. */
@@ -85,6 +97,7 @@ void* halloc(size_t size)
next->used = 0;
next->size = orig_size - realsz - sizeof(halloc_node_t) / 4;
next->prev = offset;
+ next->canary = CANARY;
halloc_node_t* nextnext = halloc_node_next(next);
if (halloc_node_get_off(nextnext) < (MAX_HEAP_SIZE / 4)) {
@@ -101,36 +114,64 @@ void* halloc(size_t size)
return NULL;
}
+
/* Joins this node with the previous and next nodes if they're free. */
static void coalesce(halloc_node_t* cur)
{
- if (cur->used) return;
+ halloc_node_t* orig = cur;
+ halloc_node_t* last_freed;
+ halloc_node_t* next_used;
- halloc_node_t* next = halloc_node_next(cur);
- if (!next->used) {
- cur->size += next->size + sizeof(halloc_node_t) / 4;
+ /* Find the earliest contiguous free'd block. */
+ while (!cur->used && cur != halloc_start) {
+ cur = halloc_node_prev(cur);
}
- if (cur != halloc_start) {
- coalesce(halloc_node_prev(cur));
+ if (cur == halloc_start && !cur->used) {
+ last_freed = cur;
+ } else {
+ last_freed = halloc_node_next(cur);
}
-}
-void hfree(void* mem)
-{
- /* TODO this should be a critical section. */
- if (!mem) {
- /* Consistent with C, freeing a NULL pointer does nothing. */
- return;
+ /* Find the next used block. */
+ cur = orig;
+ while (!cur->used && !halloc_node_out_of_range(cur)) {
+ cur = halloc_node_next(cur);
}
- halloc_node_t* header = ((halloc_node_t*) mem) - 1;
+ next_used = cur;
+
+ if (!halloc_node_out_of_range(next_used)) {
+ next_used->prev = halloc_node_get_off(last_freed);
+ }
+
+ last_freed->size = ((uint8_t*) next_used - (last_freed->mem)) / 4;
+}
- if (!header->used) {
#ifdef FOR_TESTING
- assert(header->used);
+#include <assert.h>
+#include <stdio.h>
+void panic(const char* x)
+{
+ fprintf(stderr, "%s\n", x);
+ assert(0);
+}
+#else
+void panic(const char* x)
+{
+ for(;;);
+}
#endif
- return; // TODO make this fail on ARM.
+
+void hfree(void* mem)
+{
+ /* Like normal free(), do nothing on free'ing NULL */
+ if (!mem) return;
+
+ halloc_node_t* header = get_halloc_node(mem);
+ if (!header->used) {
+ panic("Heap double free or corruption!\n");
+ return;
}
header->used = 0;
@@ -157,6 +198,18 @@ void* debug_halloc_get_prev_ptr(void* ptr)
return prev->mem;
}
+void debug_print_blocks()
+{
+ printf("------ Print Blocks -------\n");
+ halloc_node_t* cur = halloc_node_at_off(0);
+
+ while (!halloc_node_out_of_range(cur)) {
+ printf("header (%04x) {used=%d, size=%5d, prev=%04x, canary=%02x}\n",
+ halloc_node_get_off(cur), cur->used, cur->size, cur->prev, cur->canary);
+ cur = halloc_node_next(cur);
+ }
+}
+
/* Tests that we can walk up and down the allocated blocks and that they
* are properly aligned. */
int debug_halloc_assert_consistency(char* error, size_t len)
@@ -164,8 +217,16 @@ int debug_halloc_assert_consistency(char* error, size_t len)
halloc_node_t* cur = halloc_node_at_off(0);
size_t total_size = 0;
size_t loop_check = 0;
+ size_t n_blocks = 1;
+ size_t n_blocks_back = 1;
while(1) {
+ if (cur->canary != CANARY) {
+ snprintf(error, len, "Node has corrupted canary. %02x vs expected %02x\n",
+ cur->canary, CANARY);
+ return 1;
+ }
+
total_size += cur->size + 1;
halloc_node_t* next = halloc_node_next(cur);
@@ -178,7 +239,9 @@ int debug_halloc_assert_consistency(char* error, size_t len)
(void*)(DATA_SEGMENT_STOP_ADDR + MAX_HEAP_SIZE));
return 1;
}
+
cur = next;
+ ++ n_blocks;
}
if (total_size * 4 != MAX_HEAP_SIZE) {
@@ -188,10 +251,21 @@ int debug_halloc_assert_consistency(char* error, size_t len)
return 1;
}
+ if (cur == halloc_start) {
+ return 0;
+ }
+
while (loop_check < 10000) {
halloc_node_t* prev = halloc_node_prev(cur);
+ ++ n_blocks_back;
if (prev == halloc_start) {
+ if (n_blocks != n_blocks_back) {
+ snprintf(
+ error, len, "Different number of blocks found on the way back. Found %lu on the way back vs %lu up.\n",
+ n_blocks_back, n_blocks);
+ return 1;
+ }
return 0;
}
diff --git a/02-usart/tests/test_memory.c b/02-usart/tests/test_memory.c
index 2272f20..04e9289 100644
--- a/02-usart/tests/test_memory.c
+++ b/02-usart/tests/test_memory.c
@@ -9,13 +9,6 @@
#include "kern/common.h"
#include "kern/mem.h"
-extern uint32_t* halloc_start;
-static void wipeout_halloc()
-{
- memset(halloc_start, 0, 1024);
- halloc_start = NULL;
-}
-
struct TEST_STRUCT {
uint32_t array[3];
};
@@ -24,6 +17,32 @@ struct TEST_STRUCT2 {
uint32_t array[10];
};
+/* Copy of the node structure. */
+typedef struct HALLOC_NODE {
+ union {
+ uint32_t header;
+ struct {
+ /* Is this memory block currently in use (hasn't been hfree'd) */
+ bool used:1;
+ /* Number of words allocated. Does not include the header. */
+ uint16_t size:12;
+ /* The location of the previous block (in WORDS from offest) */
+ uint16_t prev:12;
+ uint8_t canary:7;
+ } PACKED;
+ };
+
+ uint8_t mem[]; /* The memory to use. */
+} halloc_node_t;
+
+extern halloc_node_t* halloc_start;
+
+static void wipeout_halloc()
+{
+ memset(halloc_start, 0, 1024);
+ halloc_start = NULL;
+}
+
static struct TEST_STRUCT* new_test_struct()
{
@@ -71,6 +90,18 @@ TEST(memory, halloc)
ASSERT_CHAIN(test3, test4);
ASSERT_CHAIN(test4, test5);
+
+ char buf[1024];
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(
+ stderr,
+ "Consistency check failed. (%s:%d)\n",
+ __FILE__,
+ __LINE__);
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
+
wipeout_halloc();
return 0;
@@ -98,6 +129,10 @@ struct UNEVEN_STRUCT* new_uneven_struct()
TEST(memory, uneven_halloc)
{
+ if (halloc_start) {
+ wipeout_halloc();
+ }
+
struct UNEVEN_STRUCT* test1 = new_uneven_struct();
struct UNEVEN_STRUCT* test2 = new_uneven_struct();
@@ -110,6 +145,10 @@ TEST(memory, uneven_halloc)
TEST(memory, halloc_free)
{
+ if (halloc_start) {
+ wipeout_halloc();
+ }
+
struct TEST_STRUCT* test1 = new_test_struct();
struct TEST_STRUCT2* test2 = new_test_struct2();
struct TEST_STRUCT* test3 = new_test_struct();
@@ -122,7 +161,7 @@ TEST(memory, halloc_free)
hfree(test1);
hfree(test5);
- ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+ ASSERT_EQ((int) halloc_start->size * 4, MAX_HEAP_SIZE - 4);
test1 = new_test_struct();
test2 = new_test_struct2();
@@ -136,7 +175,7 @@ TEST(memory, halloc_free)
hfree(test4);
hfree(test5);
- ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+ ASSERT_EQ((int) halloc_start->size * 4, MAX_HEAP_SIZE - 4);
test1 = new_test_struct();
test2 = new_test_struct2();
@@ -150,7 +189,7 @@ TEST(memory, halloc_free)
hfree(test2);
hfree(test5);
- ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+ ASSERT_EQ((int) halloc_start->size * 4, MAX_HEAP_SIZE - 4);
wipeout_halloc();
@@ -159,6 +198,10 @@ TEST(memory, halloc_free)
TEST(memory, halloc_free_alloc2)
{
+ if (halloc_start) {
+ wipeout_halloc();
+ }
+
struct TEST_STRUCT2* test1 = new_test_struct2();
struct TEST_STRUCT2* test2 = new_test_struct2();
@@ -189,53 +232,127 @@ TEST(memory, halloc_free_alloc2)
return 0;
}
+TEST(memory, relink_backref_after_free)
+{
+ if (halloc_start) {
+ wipeout_halloc();
+ }
+
+ struct TEST_STRUCT* test2 = new_test_struct();
+ struct TEST_STRUCT* test3 = new_test_struct();
+
+ hfree(test2);
+ hfree(test3);
+
+ char buf[1024];
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(stderr, "Consistency check failed.\n");
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
+
+ return 0;
+}
+
TEST(memory, consistency_stress)
{
+#define NRUNS 500
if (halloc_start) {
wipeout_halloc();
}
int i;
- void* allocd[500] = { 0 };
+ void* allocd[NRUNS] = { 0 };
char buf[1024];
- for (i = 0; i < 500; ++ i) {
+ for (i = 0; i < NRUNS; ++ i) {
size_t nalloc = rand() % 20;
allocd[i] = halloc(nalloc);
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(
+ stderr,
+ "Consistency check failed. (At index=%d, %s:%d)\n",
+ i,
+ __FILE__,
+ __LINE__);
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
+
ASSERT_TRUE(allocd[i]);
- size_t idx = rand() % 500;
+
+ memset(allocd[i], 0xFF, nalloc);
+ size_t idx = rand() % NRUNS;
+
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(
+ stderr,
+ "Consistency check failed. (At index=%d, %s:%d)\n",
+ i,
+ __FILE__,
+ __LINE__);
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
hfree(allocd[idx]);
allocd[idx] = NULL;
- idx = rand() % 500;
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(
+ stderr,
+ "Consistency check failed. (At index=%d, %s:%d)\n",
+ i,
+ __FILE__,
+ __LINE__);
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
+
+ idx = rand() % NRUNS;
hfree(allocd[idx]);
allocd[idx] = NULL;
if (debug_halloc_assert_consistency(buf, 1024)) {
- fprintf(stderr, "Consistency check failed. (At index %d) nalloc=%lu\n", i, nalloc);
+ fprintf(
+ stderr,
+ "Consistency check failed. (At index=%d, %s:%d)\n",
+ i,
+ __FILE__,
+ __LINE__);
fprintf(stderr, buf);
ASSERT_TRUE(false);
}
}
- for(i = 0; i < 500; ++ i) {
- hfree(allocd[i]);
- }
+ for(i = 0; i < NRUNS; ++ i) {
+ if (allocd[i]) {
+ hfree(allocd[i]);
+ }
- if (debug_halloc_assert_consistency(buf, 1024)) {
- fprintf(stderr, "Consistency check failed. (Final)\n");
- fprintf(stderr, buf);
- ASSERT_TRUE(false);
+ if (debug_halloc_assert_consistency(buf, 1024)) {
+ fprintf(
+ stderr,
+ "Consistency check failed. (At index=%d, %s:%d)\n",
+ i,
+ __FILE__,
+ __LINE__);
+ fprintf(stderr, buf);
+ ASSERT_TRUE(false);
+ }
}
- ASSERT_EQ((*halloc_start >> 1) * 4, MAX_HEAP_SIZE - 4);
+ ASSERT_EQ((int) halloc_start->size * 4, MAX_HEAP_SIZE - 4);
return 0;
}
TEST(memory, halloc_free_alloc)
{
+ if (halloc_start) {
+ wipeout_halloc();
+ }
+
new_test_struct();
struct TEST_STRUCT2* test2 = new_test_struct2();
new_test_struct();