copy on git
This commit is contained in:
commit
42653de246
205 changed files with 7459 additions and 0 deletions
419
main.c
Normal file
419
main.c
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
** These match your allocator’s thresholds
|
||||
** (only used here for shaping size distributions)
|
||||
*/
|
||||
#define TINY_MAX 64
|
||||
#define SMALL_MAX 4096
|
||||
|
||||
#define ARRAY_SIZE 10000 // number of live slots we manage
|
||||
#define RANDOM_ITERS 200000 // number of random operations
|
||||
#define PATTERN_BASE 0xA5
|
||||
#define MAX_LARGE_SIZE (SMALL_MAX * 8) // up to 32k-ish
|
||||
|
||||
typedef struct s_block
|
||||
{
|
||||
void *ptr;
|
||||
size_t size;
|
||||
unsigned char pattern;
|
||||
int is_calloc;
|
||||
} t_block;
|
||||
|
||||
static t_block g_blocks[ARRAY_SIZE];
|
||||
|
||||
/* Fill a block with a simple pattern byte */
|
||||
static void fill_pattern(void *ptr, size_t size, unsigned char pattern)
|
||||
{
|
||||
if (!ptr || size == 0)
|
||||
return;
|
||||
memset(ptr, pattern, size);
|
||||
}
|
||||
|
||||
/* Check that a block still has the expected pattern */
|
||||
static int check_pattern(void *ptr, size_t size, unsigned char pattern)
|
||||
{
|
||||
if (!ptr || size == 0)
|
||||
return 1;
|
||||
unsigned char *p = (unsigned char *)ptr;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if (p[i] != pattern)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[ERROR] Pattern mismatch at %p (offset %zu): "
|
||||
"expected 0x%02X, got 0x%02X\n",
|
||||
ptr, i, (unsigned)pattern, (unsigned)p[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check that a calloc'd block is zeroed */
|
||||
static int check_zero(void *ptr, size_t size)
|
||||
{
|
||||
if (!ptr || size == 0)
|
||||
return 1;
|
||||
unsigned char *p = (unsigned char *)ptr;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
if (p[i] != 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[ERROR] Calloc block not zeroed at %p (offset %zu): got 0x%02X\n",
|
||||
ptr, i, (unsigned)p[i]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Generate a pseudo-random size skewed across tiny/small/large */
|
||||
static size_t random_size(void)
|
||||
{
|
||||
int r = rand() % 100;
|
||||
|
||||
if (r < 40)
|
||||
{
|
||||
/* TINY range [1..TINY_MAX] */
|
||||
return (size_t)(1 + rand() % TINY_MAX);
|
||||
}
|
||||
else if (r < 85)
|
||||
{
|
||||
/* SMALL range [TINY_MAX+1 .. SMALL_MAX] */
|
||||
return (size_t)(TINY_MAX + 1 + rand() % (SMALL_MAX - TINY_MAX));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* LARGE range [SMALL_MAX+1 .. MAX_LARGE_SIZE] */
|
||||
return (size_t)(SMALL_MAX + 1 + rand() % (MAX_LARGE_SIZE - SMALL_MAX - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* Tear down all blocks cleanly (for the end of tests) */
|
||||
static void free_all_blocks(void)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE; ++i)
|
||||
{
|
||||
if (g_blocks[i].ptr)
|
||||
{
|
||||
free(g_blocks[i].ptr);
|
||||
g_blocks[i].ptr = NULL;
|
||||
g_blocks[i].size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple deterministic sanity tests */
|
||||
static void basic_tests(void)
|
||||
{
|
||||
printf("== basic_tests ==\n");
|
||||
|
||||
/* TINY */
|
||||
void *a = malloc(10);
|
||||
void *b = malloc(TINY_MAX);
|
||||
fill_pattern(a, 10, 0x11);
|
||||
fill_pattern(b, TINY_MAX, 0x22);
|
||||
check_pattern(a, 10, 0x11);
|
||||
check_pattern(b, TINY_MAX, 0x22);
|
||||
free(a);
|
||||
free(b);
|
||||
|
||||
/* SMALL */
|
||||
void *c = malloc(SMALL_MAX / 2);
|
||||
fill_pattern(c, SMALL_MAX / 2, 0x33);
|
||||
check_pattern(c, SMALL_MAX / 2, 0x33);
|
||||
c = realloc(c, SMALL_MAX / 2 + 100); // grow within SMALL
|
||||
check_pattern(c, SMALL_MAX / 2, 0x33);
|
||||
free(c);
|
||||
|
||||
/* LARGE */
|
||||
size_t large_sz = SMALL_MAX * 4;
|
||||
void *d = malloc(large_sz);
|
||||
fill_pattern(d, large_sz, 0x44);
|
||||
check_pattern(d, large_sz, 0x44);
|
||||
d = realloc(d, large_sz * 2); // grow LARGE
|
||||
check_pattern(d, large_sz, 0x44);
|
||||
d = realloc(d, large_sz / 2); // shrink LARGE
|
||||
check_pattern(d, large_sz / 2, 0x44);
|
||||
free(d);
|
||||
|
||||
/* CALLOC */
|
||||
void *e = calloc(1000, 1);
|
||||
check_zero(e, 1000);
|
||||
memset(e, 0xAB, 1000);
|
||||
e = realloc(e, 2000);
|
||||
check_pattern(e, 1000, 0xAB);
|
||||
free(e);
|
||||
|
||||
printf("basic_tests: OK\n\n");
|
||||
}
|
||||
|
||||
static void random_stress(void)
|
||||
{
|
||||
printf("== random_stress ==\n");
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE; ++i)
|
||||
{
|
||||
g_blocks[i].ptr = NULL;
|
||||
g_blocks[i].size = 0;
|
||||
g_blocks[i].pattern = 0;
|
||||
g_blocks[i].is_calloc = 0;
|
||||
}
|
||||
|
||||
for (int iter = 0; iter < RANDOM_ITERS; ++iter)
|
||||
{
|
||||
int idx = rand() % ARRAY_SIZE;
|
||||
t_block *blk = &g_blocks[idx];
|
||||
int op = rand() % 4; /* 0 = alloc/new, 1 = free, 2 = realloc up, 3 = realloc down */
|
||||
|
||||
/* Invariant: for non-NULL blocks we always keep full [0..size) filled with pattern */
|
||||
if (blk->ptr && !blk->is_calloc)
|
||||
{
|
||||
if (!check_pattern(blk->ptr, blk->size, blk->pattern))
|
||||
{
|
||||
fprintf(stderr, "[FATAL] pattern mismatch before op; idx=%d\n", idx);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (op == 0)
|
||||
{
|
||||
/* allocate new if empty, otherwise grow via realloc */
|
||||
if (blk->ptr == NULL)
|
||||
{
|
||||
size_t sz = random_size();
|
||||
int use_calloc = (rand() % 4) == 0; /* 25% calloc */
|
||||
|
||||
if (use_calloc)
|
||||
{
|
||||
blk->ptr = calloc(sz, 1);
|
||||
blk->is_calloc = 1;
|
||||
blk->size = sz;
|
||||
if (!blk->ptr)
|
||||
{
|
||||
fprintf(stderr, "[ERROR] calloc returned NULL\n");
|
||||
continue;
|
||||
}
|
||||
/* we only check zero once; then we switch to pattern mode */
|
||||
if (!check_zero(blk->ptr, blk->size))
|
||||
fprintf(stderr, "[ERROR] calloc block not zeroed (idx=%d)\n", idx);
|
||||
blk->pattern = (unsigned char)((PATTERN_BASE + idx) & 0xFF);
|
||||
fill_pattern(blk->ptr, blk->size, blk->pattern);
|
||||
blk->is_calloc = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
blk->ptr = malloc(sz);
|
||||
blk->is_calloc = 0;
|
||||
blk->size = sz;
|
||||
if (!blk->ptr)
|
||||
{
|
||||
fprintf(stderr, "[ERROR] malloc returned NULL\n");
|
||||
continue;
|
||||
}
|
||||
blk->pattern = (unsigned char)((PATTERN_BASE + idx) & 0xFF);
|
||||
fill_pattern(blk->ptr, blk->size, blk->pattern);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* grow via realloc */
|
||||
size_t old_size = blk->size;
|
||||
size_t new_sz = old_size + 1 + (rand() % (old_size + 1));
|
||||
unsigned char pattern = blk->pattern;
|
||||
|
||||
void *new_ptr = realloc(blk->ptr, new_sz);
|
||||
if (!new_ptr)
|
||||
{
|
||||
fprintf(stderr, "[ERROR] realloc (grow) returned NULL\n");
|
||||
continue; /* keep old block as-is */
|
||||
}
|
||||
blk->ptr = new_ptr;
|
||||
blk->size = new_sz;
|
||||
blk->pattern = pattern;
|
||||
|
||||
/* Only the first old_size bytes must be preserved */
|
||||
if (!check_pattern(blk->ptr, old_size, pattern))
|
||||
{
|
||||
fprintf(stderr, "[ERROR] pattern mismatch after realloc grow (idx=%d)\n", idx);
|
||||
abort();
|
||||
}
|
||||
/* Now enforce our invariant for the future */
|
||||
fill_pattern(blk->ptr, blk->size, blk->pattern);
|
||||
}
|
||||
}
|
||||
else if (op == 1)
|
||||
{
|
||||
/* free */
|
||||
if (blk->ptr)
|
||||
{
|
||||
free(blk->ptr);
|
||||
blk->ptr = NULL;
|
||||
blk->size = 0;
|
||||
blk->is_calloc = 0;
|
||||
}
|
||||
}
|
||||
else if (op == 2)
|
||||
{
|
||||
/* explicit realloc up (if exists) */
|
||||
if (blk->ptr)
|
||||
{
|
||||
size_t old_size = blk->size;
|
||||
size_t new_sz = old_size + 1 + (rand() % (old_size + 1));
|
||||
unsigned char pattern = blk->pattern;
|
||||
|
||||
void *new_ptr = realloc(blk->ptr, new_sz);
|
||||
if (!new_ptr)
|
||||
{
|
||||
fprintf(stderr, "[ERROR] realloc (grow2) returned NULL\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
blk->ptr = new_ptr;
|
||||
blk->size = new_sz;
|
||||
blk->pattern = pattern;
|
||||
|
||||
if (!check_pattern(blk->ptr, old_size, pattern))
|
||||
{
|
||||
fprintf(stderr, "[ERROR] pattern mismatch after realloc grow2 (idx=%d)\n", idx);
|
||||
abort();
|
||||
}
|
||||
fill_pattern(blk->ptr, blk->size, blk->pattern);
|
||||
}
|
||||
}
|
||||
else /* op == 3, realloc down */
|
||||
{
|
||||
if (blk->ptr && blk->size > 1)
|
||||
{
|
||||
size_t old_size = blk->size;
|
||||
size_t new_sz = 1 + (rand() % blk->size);
|
||||
unsigned char pattern = blk->pattern;
|
||||
|
||||
void *new_ptr = realloc(blk->ptr, new_sz);
|
||||
if (!new_ptr)
|
||||
{
|
||||
fprintf(stderr, "[ERROR] realloc (shrink) returned NULL\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
blk->ptr = new_ptr;
|
||||
blk->size = new_sz;
|
||||
blk->pattern = pattern;
|
||||
|
||||
/* For shrink, we only expect the first new_sz bytes to still match */
|
||||
if (!check_pattern(blk->ptr, blk->size, blk->pattern))
|
||||
{
|
||||
fprintf(stderr, "[ERROR] pattern mismatch after realloc shrink (idx=%d)\n", idx);
|
||||
abort();
|
||||
}
|
||||
/* Still keep invariant explicitly (optional here) */
|
||||
fill_pattern(blk->ptr, blk->size, blk->pattern);
|
||||
}
|
||||
}
|
||||
|
||||
if ((iter % 50000) == 0 && iter != 0)
|
||||
printf(" random_stress progress: %d / %d\n", iter, RANDOM_ITERS);
|
||||
}
|
||||
|
||||
free_all_blocks();
|
||||
printf("random_stress: DONE\n\n");
|
||||
}
|
||||
|
||||
/* Fragmentation test: allocate many small then free some, then big allocs, etc. */
|
||||
static void fragmentation_test(void)
|
||||
{
|
||||
printf("== fragmentation_test ==\n");
|
||||
|
||||
const int count = 10000;
|
||||
void **arr = malloc(sizeof(void *) * count);
|
||||
size_t *sizes = malloc(sizeof(size_t) * count);
|
||||
if (!arr || !sizes)
|
||||
{
|
||||
fprintf(stderr, "[ERROR] fragmentation_test: malloc failed\n");
|
||||
free(arr);
|
||||
free(sizes);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Step 1: allocate random small blocks */
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
size_t sz = 1 + (rand() % SMALL_MAX);
|
||||
arr[i] = malloc(sz);
|
||||
sizes[i] = sz;
|
||||
if (!arr[i])
|
||||
{
|
||||
fprintf(stderr, "[ERROR] fragmentation_test: malloc failed at i=%d\n", i);
|
||||
continue;
|
||||
}
|
||||
memset(arr[i], 0x5A, sz);
|
||||
}
|
||||
|
||||
/* Step 2: free every other block */
|
||||
for (int i = 0; i < count; i += 2)
|
||||
{
|
||||
if (arr[i])
|
||||
{
|
||||
free(arr[i]);
|
||||
arr[i] = NULL;
|
||||
sizes[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 3: allocate a bunch of bigger blocks, try to reuse fragmentation */
|
||||
const int big_count = 1000;
|
||||
void **big = malloc(sizeof(void *) * big_count);
|
||||
size_t *big_sz = malloc(sizeof(size_t) * big_count);
|
||||
if (!big || !big_sz)
|
||||
{
|
||||
fprintf(stderr, "[ERROR] fragmentation_test: big malloc failed\n");
|
||||
free(big);
|
||||
free(big_sz);
|
||||
free(arr);
|
||||
free(sizes);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < big_count; ++i)
|
||||
{
|
||||
size_t sz = SMALL_MAX + (rand() % (SMALL_MAX * 4));
|
||||
big[i] = malloc(sz);
|
||||
big_sz[i] = sz;
|
||||
if (!big[i])
|
||||
{
|
||||
fprintf(stderr, "[ERROR] fragmentation_test: big malloc failed at i=%d\n", i);
|
||||
continue;
|
||||
}
|
||||
memset(big[i], 0x6B, sz);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
for (int i = 0; i < count; ++i)
|
||||
free(arr[i]);
|
||||
for (int i = 0; i < big_count; ++i)
|
||||
free(big[i]);
|
||||
free(arr);
|
||||
free(sizes);
|
||||
free(big);
|
||||
free(big_sz);
|
||||
|
||||
printf("fragmentation_test: DONE\n\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
srand((unsigned int)time(NULL));
|
||||
|
||||
basic_tests();
|
||||
random_stress();
|
||||
fragmentation_test();
|
||||
|
||||
printf("ALL TESTS DONE\n");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue