Skip to content

Instantly share code, notes, and snippets.

@splbio
Created March 5, 2013 22:45
Show Gist options
  • Save splbio/5095049 to your computer and use it in GitHub Desktop.
Save splbio/5095049 to your computer and use it in GitHub Desktop.
lame arena allocator for libz during kernel crashes. allows memory allocation from a hunk of DATA and does minimal "best fit" management of a freelist. This is quick and dirty because the system is about to go down anyhow so any complexity is likely to be wasted time here.
#include <stdio.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/queue.h>
#ifndef roundup
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)
#endif
/*
* This arena allocator is very bad, but its only used during a
* crash so it should suffice.
*/
/*
* In practice it seems like 281kb is typical usage for large core
* dumps, so over allocate to 1MB to avoid losing a dump.
*/
#define GZ_STATIC_ALLOC_BYTES (1024*1024)
static char gz_arena[GZ_STATIC_ALLOC_BYTES];
struct gz_arena_item {
int fi_size;
TAILQ_ENTRY(gz_arena_item) fi_lnk;
uintptr_t fi_data;
};
static int gz_arena_sbrk = 0; /* pointer to end of heap */
static TAILQ_HEAD(, gz_arena_item) gz_arena_freelist =
TAILQ_HEAD_INITIALIZER(gz_arena_freelist);
static TAILQ_HEAD(, gz_arena_item) gz_arena_alloclist =
TAILQ_HEAD_INITIALIZER(gz_arena_alloclist);
static void *
gz_arena_alloc(void *notused __unused, u_int items, u_int size)
{
struct gz_arena_item *fi, *fi_bestfit;
size_t actual_alloc;
size_t realsize = size*items;
/*
* Walk the freelist and try to find the smallest
* piece that will fit "size"
*/
fi_bestfit = NULL;
TAILQ_FOREACH(fi, &gz_arena_freelist, fi_lnk) {
/* exact fit */
if (fi->fi_size == realsize) {
fi_bestfit = fi;
break;
}
if (fi->fi_size < realsize)
continue;
if (fi_bestfit == NULL)
fi_bestfit = fi;
else if (fi_bestfit->fi_size < fi->fi_size)
fi_bestfit = fi;
}
if (fi_bestfit != NULL) {
fprintf(stderr, "found @%p\n", fi_bestfit);
TAILQ_REMOVE(&gz_arena_freelist, fi_bestfit, fi_lnk);
TAILQ_INSERT_HEAD(&gz_arena_alloclist, fi_bestfit, fi_lnk);
return (&fi_bestfit->fi_data);
}
/*
* Ok we couldn't find a chunk to reuse, expand the heap.. if we can.
*/
actual_alloc = 0;
actual_alloc = roundup(realsize + sizeof(struct gz_arena_item),
sizeof(uintptr_t)));
if (gz_arena_sbrk + actual_alloc >= GZ_STATIC_ALLOC_BYTES) {
printf("out of memory in gz_arena_alloc!\n");
return NULL;
}
fi = (struct gz_arena_item *)&(gz_arena[gz_arena_sbrk]);
fi->fi_size = realsize;
TAILQ_INSERT_HEAD(&gz_arena_alloclist, fi, fi_lnk);
fprintf(stderr, "created @%p size %d, actualalloc %d\n", fi,
(int)realsize, (int)actual_alloc);
gz_arena_sbrk += actual_alloc;
return (&fi->fi_data);
}
static void
gz_arena_free(void *opaque __unused, void *ptr)
{
struct gz_arena_item *fi, *fi_guess;
fprintf(stderr, "free: ptr = @%p\n", ptr);
fi_guess = (struct gz_arena_item *)(((char *)ptr) -
offsetof(struct gz_arena_item, fi_data));
fprintf(stderr, "free: guess = @%p\n", fi_guess);
TAILQ_FOREACH(fi, &gz_arena_alloclist, fi_lnk) {
if (fi == fi_guess)
break;
}
if (fi == NULL) {
printf("free of non-allocated memory\n");
return;
}
TAILQ_REMOVE(&gz_arena_alloclist, fi, fi_lnk);
TAILQ_INSERT_HEAD(&gz_arena_freelist, fi, fi_lnk);
}
int
main(void)
{
void *p1, *p2, *p3, *p4;
p1 = gz_arena_alloc(NULL, 10, 5);
p2 = gz_arena_alloc(NULL, 10, 5);
p3 = gz_arena_alloc(NULL, 10, 5);
p4 = gz_arena_alloc(NULL, 10, 5);
printf("%p %p %p %p\n", p1, p2, p3, p4);
gz_arena_free(NULL, p1);
gz_arena_free(NULL, p2);
gz_arena_free(NULL, p3);
gz_arena_free(NULL, p4);
p1 = gz_arena_alloc(NULL, 10, 5);
p2 = gz_arena_alloc(NULL, 10, 5);
p3 = gz_arena_alloc(NULL, 10, 5);
p4 = gz_arena_alloc(NULL, 10, 5);
printf("%p %p %p %p\n", p1, p2, p3, p4);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment