Skip to content

Commit

Permalink
Allocation fuzzing, style, stats, dict, command
Browse files Browse the repository at this point in the history
* The block allocator can be built so it will randomly return NULL when
it would normally succeed, this is so it can be used to test that the
interpreter behaves as it should under these circumstances instead of
crashing.
* Some style changes
* More statistics can be collected from the block allocator
* A 'command' command has been added, which can be used to introspect
the defined commands.
* Tests have been added to 'dict.c' in preparation for integration.
* An example program has been added 'ex3.tcl', which can be used for
profiling the interpreter.
  • Loading branch information
howerj committed Oct 13, 2018
1 parent e6ac4a0 commit 0917d7c
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 131 deletions.
9 changes: 5 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
picol
pickle
*.o
*.a
*.log
block
dict
*.log
*.o
pickle
picol
156 changes: 86 additions & 70 deletions block.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
* @copyright Richard James Howe (2018)
* @license BSD
*
* @todo Optimize, cleanup and improve. Write better unit tests.
* @todo There could be an option for falling back to malloc if an allocation
* fails.
* @todo Add a mechanism (which could be a set of callbacks) for simulating
* failure at arbitrary times. This could then be used for testing and making
* picol.c more robust in the face of failure.
* @todo Add maximum number of blocks allocated in a specific arena
*
* This file contains a simple memory pool allocator, and contains three main
Expand All @@ -35,12 +29,16 @@
#include <stdbool.h>
#include <limits.h>

#define FIND_BY_BIT (0)
#define STATISTICS (1)
#define FIND_BY_BIT (0) /* Slow, simply, find free bit by bit? */
#define STATISTICS (1) /* Collect statistics on allocations? */
#define FALLBACK (0) /* Fallback to malloc/free if we cannot allocate? */
#define RANDOM_FAIL (0) /* Simulate random failures? (pool allocations only) */
#define BITS (sizeof(bitmap_unit_t)*CHAR_BIT)
#define MASK (BITS-1)
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#define FAIL_PROBABILITY (RAND_MAX/1000)
#define FAIL_SEED (1987)

size_t bitmap_units(size_t bits) {
return bits/BITS + !!(bits & MASK);
Expand All @@ -52,7 +50,7 @@ size_t bitmap_bits(bitmap_t *b) {
}

void bitmap_free(bitmap_t *b) {
if(!b)
if (!b)
return;
free(b->map);
free(b);
Expand All @@ -63,10 +61,10 @@ bitmap_t *bitmap_new(size_t bits) {
assert(length < (SIZE_MAX/bits));
assert((length + sizeof(bitmap_t)) > length);
bitmap_t *r = calloc(sizeof(bitmap_t), 1);
if(!r)
if (!r)
return NULL;
r->map = calloc(length + 1, sizeof(r->map[0]));
if(!r->map) {
if (!r->map) {
free(r);
return NULL;
}
Expand All @@ -77,7 +75,7 @@ bitmap_t *bitmap_new(size_t bits) {
bitmap_t *bitmap_copy(const bitmap_t *b) {
assert(b);
bitmap_t *r = bitmap_new(b->bits);
if(!r)
if (!r)
return NULL;
memcpy(r->map, b->map, bitmap_units(b->bits) * sizeof(bitmap_unit_t));
return r;
Expand Down Expand Up @@ -114,42 +112,42 @@ static inline size_t block_count(block_arena_t *a) {

static long block_find_free(block_arena_t *a) {
assert(a);
if(FIND_BY_BIT) { /* much slower, simpler */
if (FIND_BY_BIT) { /* much slower, simpler */
bitmap_t *b = &a->freelist;
long r = -1, max = block_count(a);
for(long i = 0; i < max; i++)
if(!bitmap_get(b, i))
for (long i = 0; i < max; i++)
if (!bitmap_get(b, i))
return i;
return r;
}
bitmap_t *b = &a->freelist;
bitmap_unit_t *u = b->map;
long r = -1;
const long max = bitmap_units(b->bits), start = 0; // start = bitmap_units(a->lastfree);
if(a->lastfree) {
if (a->lastfree) {
const long i = a->lastfree;
a->lastfree = 0;
if(!bitmap_get(b, i))
if (!bitmap_get(b, i))
return i;
}
for(long i = start; i < max; i++) /**@todo start at last position, wrap around */
if(u[i] != (bitmap_unit_t)-1uLL) {
for (long i = start; i < max; i++) /**@todo start at last position, wrap around */
if (u[i] != (bitmap_unit_t)-1uLL) {
const size_t index = i * BITS;
const size_t end = MIN(b->bits, (index + BITS));
for(long j = index; j < (long)end; j++)
if(!bitmap_get(b, j))
for (long j = index; j < (long)end; j++)
if (!bitmap_get(b, j))
return j;
}
return r;
}

static inline bool is_aligned(void *v) {
uintptr_t p = (uintptr_t)v;
if(sizeof(p) == 2)
if (sizeof(p) == 2)
return !(p & 1);
if(sizeof(p) == 4)
if (sizeof(p) == 4)
return !(p & 3);
if(sizeof(p) == 8)
if (sizeof(p) == 8)
return !(p & 7);
return 0;
}
Expand All @@ -162,11 +160,16 @@ void *block_malloc(block_arena_t *a, size_t length) {
assert(a);
assert(is_power_of_2(a->blocksz));
assert((block_count(a) % sizeof(bitmap_unit_t)) == 0);
if(a->blocksz < length)
if (a->blocksz < length)
return NULL;
const long f = block_find_free(a);
if(f < 0)
if (f < 0)
return NULL;
if (STATISTICS) {
a->active++;
if (a->max < a->active)
a->max = a->active;
}
bitmap_set(&a->freelist, f);
void *r = ((char*)a->memory) + (f * a->blocksz);
assert(is_aligned(r));
Expand All @@ -175,48 +178,50 @@ void *block_malloc(block_arena_t *a, size_t length) {

void *block_calloc(block_arena_t *a, size_t length) {
void *r = block_malloc(a, length);
if(!r)
if (!r)
return r;
memset(r, 0, a->blocksz);
return r;
}

static inline int block_arena_valid_pointer(block_arena_t *a, void *v) {
const size_t max = block_count(a);
if(v < a->memory || (char*)v > ((char*)a->memory + (max * a->blocksz)))
if (v < a->memory || (char*)v > ((char*)a->memory + (max * a->blocksz)))
return 0;
return 1;
}

void block_free(block_arena_t *a, void *v) {
assert(a);
if(!v)
if (!v)
return;
if(!block_arena_valid_pointer(a, v))
if (!block_arena_valid_pointer(a, v))
abort();
const intptr_t p = ((char*)v - (char*)a->memory);
const size_t bit = p / a->blocksz;
if(!bitmap_get(&a->freelist, bit)) /* double free */
if (!bitmap_get(&a->freelist, bit)) /* double free */
abort();
if (STATISTICS)
a->active--;
bitmap_clear(&a->freelist, bit);
a->lastfree = bit;
}

void *block_realloc(block_arena_t *a, void *v, size_t length) {
assert(a);
if(!length) {
if (!length) {
block_free(a, v);
return NULL;
}
if(!v)
if (!v)
return block_malloc(a, length);
if(length < a->blocksz)
if (length < a->blocksz)
return v;
return NULL;
}

void block_delete(block_arena_t *a) {
if(!a)
if (!a)
return;
free(a->freelist.map);
free(a->memory);
Expand All @@ -225,15 +230,15 @@ void block_delete(block_arena_t *a) {

block_arena_t *block_new(size_t blocksz, size_t count) {
block_arena_t *a = NULL;
if(blocksz < sizeof(intptr_t))
if (blocksz < sizeof(intptr_t))
goto fail;
if(!is_power_of_2(blocksz) || !is_power_of_2(count))
if (!is_power_of_2(blocksz) || !is_power_of_2(count))
goto fail;
if(!(a = calloc(sizeof(*a), 1)))
if (!(a = calloc(sizeof(*a), 1)))
goto fail;
a->freelist.map = calloc((count / sizeof(bitmap_unit_t)) + sizeof(bitmap_unit_t), 1);
a->memory = calloc(blocksz, count);
if(!(a->freelist.map) || !(a->memory))
if (!(a->freelist.map) || !(a->memory))
goto fail;
a->freelist.bits = count;
a->blocksz = blocksz;
Expand All @@ -244,10 +249,10 @@ block_arena_t *block_new(size_t blocksz, size_t count) {
}

void pool_delete(pool_t *p) {
if(!p)
if (!p)
return;
if(p->arenas)
for(size_t i = 0; i < p->count; i++)
if (p->arenas)
for (size_t i = 0; i < p->count; i++)
block_delete(p->arenas[i]);
free(p->arenas);
free(p);
Expand All @@ -256,18 +261,20 @@ void pool_delete(pool_t *p) {
pool_t *pool_new(size_t length, const pool_specification_t *specs) {
assert(specs);
pool_t *p = calloc(sizeof *p, 1);
if(!p)
if (!p)
goto fail;
p->count = length;
p->arenas = calloc(sizeof(p->arenas[0]), p->count);
if(!(p->arenas))
if (!(p->arenas))
goto fail;
for(size_t i = 0; i < length; i++) {
for (size_t i = 0; i < length; i++) {
const pool_specification_t spec = specs[i];
p->arenas[i] = block_new(spec.blocksz, spec.count);
if(!(p->arenas[i]))
if (!(p->arenas[i]))
goto fail;
}
if (RANDOM_FAIL)
srand(FAIL_SEED);
return p;
fail:
pool_delete(p);
Expand All @@ -277,19 +284,24 @@ pool_t *pool_new(size_t length, const pool_specification_t *specs) {
void *pool_malloc(pool_t *p, size_t length) {
assert(p);
void *r = NULL;
if(STATISTICS)
if (RANDOM_FAIL)
if (rand() < FAIL_PROBABILITY)
return NULL;
if (STATISTICS)
p->allocs++, p->total += length;
for(size_t i = 0; i < p->count; i++)
if((r = block_malloc(p->arenas[i], length))) {
if(STATISTICS) {
for (size_t i = 0; i < p->count; i++)
if ((r = block_malloc(p->arenas[i], length))) {
if (STATISTICS) {
const size_t bsz = p->arenas[i]->blocksz;
p->active += bsz;
p->blocks += bsz;
if(p->max < p->active)
if (p->max < p->active)
p->max = p->active;
}
return r;
}
if (FALLBACK)
return malloc(length);
return r;
}

Expand All @@ -300,42 +312,46 @@ void *pool_calloc(pool_t *p, size_t length) {

void pool_free(pool_t *p, void *v) {
assert(p);
if(!v)
if (!v)
return;
if(STATISTICS)
if (STATISTICS)
p->freed++;
for(size_t i = 0; i < p->count; i++) {
if(block_arena_valid_pointer(p->arenas[i], v)) {
for (size_t i = 0; i < p->count; i++) {
if (block_arena_valid_pointer(p->arenas[i], v)) {
block_free(p->arenas[i], v);
if(STATISTICS)
if (STATISTICS)
p->active -= p->arenas[i]->blocksz;
return;
}
}
if (FALLBACK) {
free(v);
return;
}
abort();
}

size_t pool_block_size(pool_t *p, void *v) {
assert(p);
for(size_t i = 0; i < p->count; i++)
if(block_arena_valid_pointer(p->arenas[i], v))
for (size_t i = 0; i < p->count; i++)
if (block_arena_valid_pointer(p->arenas[i], v))
return p->arenas[i]->blocksz;
abort();
return 0;
}

void *pool_realloc(pool_t *p, void *v, size_t length) {
assert(p);
if(STATISTICS)
if (STATISTICS)
p->relocations++;
if(!length) {
if (!length) {
pool_free(p, v);
return NULL;
}
if(!v)
if (!v)
return pool_malloc(p, length);
void *n = pool_malloc(p, length);
if(!n)
if (!n)
return NULL;
const size_t size = pool_block_size(p, v);
memcpy(n, v, size);
Expand All @@ -362,31 +378,31 @@ int block_tests(void) {
printf("arena = %p\n", (void*)&block_arena);
printf("arena.m = %p\n", block_arena.memory);
void *v1, *v2, *v3;
if(!(v1 = block_malloc(&block_arena, 12)))
if (!(v1 = block_malloc(&block_arena, 12)))
return -1;
printf("v1 = %p\n", v1);
if(!(v2 = block_malloc(&block_arena, 30)))
if (!(v2 = block_malloc(&block_arena, 30)))
return -2;
printf("v2 = %p\n", v2);
if(diff(v1, v2) != BLK_SIZE)
if (diff(v1, v2) != BLK_SIZE)
return -3;
block_free(&block_arena, v1);
v1 = block_malloc(&block_arena, 12);
printf("v1 = %p\n", v1);
if(!(v3 = block_malloc(&block_arena, 12)))
if (!(v3 = block_malloc(&block_arena, 12)))
return -4;
printf("v3 = %p\n", v3);
if(diff(v1, v3) != BLK_SIZE*2)
if (diff(v1, v3) != BLK_SIZE*2)
return -5;
block_free(&block_arena, v1);
block_free(&block_arena, v2);
block_free(&block_arena, v3);
size_t i = 0;
for(i = 0; i < (BLK_COUNT+1); i++)
if(!block_malloc(&block_arena, 1))
for (i = 0; i < (BLK_COUNT+1); i++)
if (!block_malloc(&block_arena, 1))
break;
printf("exhausted at = %u\n", (unsigned)i);
if(i != BLK_COUNT)
if (i != BLK_COUNT)
return -6;
printf("[DONE]\n\n");
return 0;
Expand Down
Loading

0 comments on commit 0917d7c

Please sign in to comment.