forked from Mirrors/liballoc
commit
78f44b72d5
@ -0,0 +1,26 @@
|
||||
|
||||
This is a step-by-step instruction guide detailing how to
|
||||
implement the library on your own system.
|
||||
|
||||
|
||||
1. Copy the "liballoc.c" and "liballoc.h" files into
|
||||
your working directory. (Whatever project you want
|
||||
to use it in).
|
||||
|
||||
2. Create the hooks that the library needs:
|
||||
|
||||
Make a new file called "liballoc_hooks.c" (or
|
||||
whatever) and implement the functions detailed
|
||||
in the README and explained in "liballoc.h"
|
||||
|
||||
Look at "linux.c" for an example. It implements
|
||||
the hooks for a Linux system.
|
||||
|
||||
3. Be sure to include the "liballoc.h" header
|
||||
into your C/C++ files that are using the
|
||||
malloc/free/memory operations. (So that
|
||||
malloc/free/etc are declared.. obviously.)
|
||||
|
||||
4. Compile as per your normal method and test.
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
|
||||
This code is released into the public domain. Use this code at your own
|
||||
risk. Feel free to use it for whatever purpose you want. I take no responsibilty or
|
||||
whatever if anything goes wrong. Use it at your own risk.
|
||||
|
||||
If you have any fixes or patches, please email me.
|
||||
|
||||
Durand Miller <clutter@djm.co.za>
|
||||
|
||||
|
@ -0,0 +1,54 @@
|
||||
# Please see LICENSE for licensing information.
|
||||
|
||||
|
||||
# --------- FLAGS AND VARIABLES --------------------
|
||||
|
||||
CFLAGS=-O2 -nostdlib -nodefaultlibs -fno-builtin -fPIC -Wall
|
||||
HEADERPATH=-I./
|
||||
|
||||
|
||||
|
||||
# --------- GENERIC MAKE RULES --------------------
|
||||
|
||||
all:
|
||||
@echo "Makefile for the liballoc library."
|
||||
@echo "Please see LICENSE for licensing information."
|
||||
@echo
|
||||
@echo "Output should be: liballoc.a "
|
||||
@echo " liballoc.so"
|
||||
@echo
|
||||
@echo "Usage: make [ compile | clean | <platform> ] "
|
||||
@echo
|
||||
@echo "Currently supported platforms:"
|
||||
@echo
|
||||
@echo " linux"
|
||||
@echo " linux_debug"
|
||||
@echo
|
||||
@echo
|
||||
@echo "Please see the README for example usage"
|
||||
|
||||
|
||||
clean:
|
||||
rm -f ./*.o
|
||||
rm -f ./*.a
|
||||
rm -f ./*.so
|
||||
|
||||
compile:
|
||||
gcc $(HEADERPATH) $(CFLAGS) -static -c liballoc.c
|
||||
ar -rcv liballoc.a *.o
|
||||
gcc $(HEADERPATH) $(CFLAGS) -shared liballoc.c -o liballoc.so
|
||||
|
||||
|
||||
linux:
|
||||
gcc $(HEADERPATH) $(CFLAGS) -static -c liballoc.c linux.c
|
||||
ar -rcv liballoc.a *.o
|
||||
gcc $(HEADERPATH) $(CFLAGS) -shared liballoc.c linux.c -o liballoc.so
|
||||
|
||||
|
||||
linux_debug:
|
||||
gcc -DDEBUG $(HEADERPATH) $(CFLAGS) -static -c liballoc.c linux.c
|
||||
ar -rcv liballoc.a *.o
|
||||
gcc -DDEBUG $(HEADERPATH) $(CFLAGS) -shared liballoc.c linux.c -o liballoc.so
|
||||
|
||||
|
||||
|
@ -0,0 +1,58 @@
|
||||
|
||||
|
||||
|
||||
There are 4 functions which you need to implement on your system:
|
||||
|
||||
int liballoc_lock();
|
||||
int liballoc_unlock();
|
||||
void* liballoc_alloc(int);
|
||||
int liballoc_free(void*,int);
|
||||
|
||||
1) Have a look at liballoc.h for information about what each function is
|
||||
supposed to do.
|
||||
|
||||
|
||||
2) Have a look at linux.c for an example of how to implement the library
|
||||
on linux.
|
||||
|
||||
|
||||
NOTE: There are two ways to build the library.
|
||||
|
||||
1) Compile the library with a new system file. For example, I've
|
||||
left linux.c with the default distribution. It gets compiled
|
||||
directly into the liballoc_linux.so file.
|
||||
|
||||
2) Implement the functions in your application and then just
|
||||
link against the default liballoc.so library when you compile
|
||||
your app.
|
||||
|
||||
|
||||
QUICK START
|
||||
-------------
|
||||
|
||||
You can simply type: "make linux" to build the linux shared
|
||||
library. Thereafter, you can link it directly into your applications
|
||||
during build or afterwards by export the LD_PRELOAD environment
|
||||
variable.
|
||||
|
||||
|
||||
To run bash with the library, for example:
|
||||
|
||||
LD_PRELOAD=/full/path/to/liballoc.so bash
|
||||
|
||||
|
||||
The above command will pre-link the library into the application,
|
||||
essentially replacing the default malloc/free calls at runtime. It's
|
||||
quite cool.
|
||||
|
||||
|
||||
|
||||
Durand Miller
|
||||
|
||||
clutter@djm.co.za
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,534 @@
|
||||
#include <liballoc.h>
|
||||
|
||||
/** Durand's Ridiculously Amazing Super Duper Memory functions. */
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define LIBALLOC_MAGIC 0xc001c0de
|
||||
#define MAXCOMPLETE 5
|
||||
#define MAXEXP 32
|
||||
#define MINEXP 8
|
||||
|
||||
#define MODE_BEST 0
|
||||
#define MODE_INSTANT 1
|
||||
|
||||
#define MODE MODE_BEST
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
|
||||
struct boundary_tag* l_freePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks
|
||||
int l_completePages[MAXEXP]; //< Allowing for 2^MAXEXP blocks
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
unsigned int l_allocated = 0; //< The real amount of memory allocated.
|
||||
unsigned int l_inuse = 0; //< The amount of memory in use (malloc'ed).
|
||||
#endif
|
||||
|
||||
|
||||
static int l_initialized = 0; //< Flag to indicate initialization.
|
||||
static int l_pageSize = 4096; //< Individual page size
|
||||
static int l_pageCount = 16; //< Minimum number of pages to allocate.
|
||||
|
||||
|
||||
// *********** HELPER FUNCTIONS *******************************
|
||||
|
||||
/** Returns the exponent required to manage 'size' amount of memory.
|
||||
*
|
||||
* Returns n where 2^n <= size < 2^(n+1)
|
||||
*/
|
||||
static inline int getexp( unsigned int size )
|
||||
{
|
||||
if ( size < (1<<MINEXP) )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("getexp returns -1 for %i less than MINEXP\n", size );
|
||||
#endif
|
||||
return -1; // Smaller than the quantum.
|
||||
}
|
||||
|
||||
|
||||
int shift = MINEXP;
|
||||
|
||||
while ( shift < MAXEXP )
|
||||
{
|
||||
if ( (1<<shift) > size ) break;
|
||||
shift += 1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("getexp returns %i (%i bytes) for %i size\n", shift - 1, (1<<(shift -1)), size );
|
||||
#endif
|
||||
|
||||
return shift - 1;
|
||||
}
|
||||
|
||||
|
||||
static void* liballoc_memset(void* s, int c, size_t n)
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < n ; i++)
|
||||
((char*)s)[i] = c;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void* liballoc_memcpy(void* s1, const void* s2, size_t n)
|
||||
{
|
||||
char *cdest;
|
||||
char *csrc;
|
||||
unsigned int *ldest = (unsigned int*)s1;
|
||||
unsigned int *lsrc = (unsigned int*)s2;
|
||||
|
||||
while ( n >= sizeof(unsigned int) )
|
||||
{
|
||||
*ldest++ = *lsrc++;
|
||||
n -= sizeof(unsigned int);
|
||||
}
|
||||
|
||||
cdest = (char*)ldest;
|
||||
csrc = (char*)lsrc;
|
||||
|
||||
while ( n > 0 )
|
||||
{
|
||||
*cdest++ = *csrc++;
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
return s1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
static void dump_array()
|
||||
{
|
||||
int i = 0;
|
||||
struct boundary_tag *tag = NULL;
|
||||
|
||||
printf("------ Free pages array ---------\n");
|
||||
printf("System memory allocated: %i\n", l_allocated );
|
||||
printf("Memory in used (malloc'ed): %i\n", l_inuse );
|
||||
|
||||
for ( i = 0; i < MAXEXP; i++ )
|
||||
{
|
||||
printf("%.2i(%i): ",i, l_completePages[i] );
|
||||
|
||||
tag = l_freePages[ i ];
|
||||
while ( tag != NULL )
|
||||
{
|
||||
if ( tag->split_left != NULL ) printf("*");
|
||||
printf("%i", tag->real_size );
|
||||
if ( tag->split_right != NULL ) printf("*");
|
||||
|
||||
printf(" ");
|
||||
tag = tag->next;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("'*' denotes a split to the left/right of a tag\n");
|
||||
fflush( stdout );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static inline void insert_tag( struct boundary_tag *tag, int index )
|
||||
{
|
||||
int realIndex;
|
||||
|
||||
if ( index < 0 )
|
||||
{
|
||||
realIndex = getexp( tag->real_size - sizeof(struct boundary_tag) );
|
||||
if ( realIndex < MINEXP ) realIndex = MINEXP;
|
||||
}
|
||||
else
|
||||
realIndex = index;
|
||||
|
||||
tag->index = realIndex;
|
||||
|
||||
if ( l_freePages[ realIndex ] != NULL )
|
||||
{
|
||||
l_freePages[ realIndex ]->prev = tag;
|
||||
tag->next = l_freePages[ realIndex ];
|
||||
}
|
||||
|
||||
l_freePages[ realIndex ] = tag;
|
||||
}
|
||||
|
||||
static inline void remove_tag( struct boundary_tag *tag )
|
||||
{
|
||||
if ( l_freePages[ tag->index ] == tag ) l_freePages[ tag->index ] = tag->next;
|
||||
|
||||
if ( tag->prev != NULL ) tag->prev->next = tag->next;
|
||||
if ( tag->next != NULL ) tag->next->prev = tag->prev;
|
||||
|
||||
tag->next = NULL;
|
||||
tag->prev = NULL;
|
||||
tag->index = -1;
|
||||
}
|
||||
|
||||
|
||||
static inline struct boundary_tag* melt_left( struct boundary_tag *tag )
|
||||
{
|
||||
struct boundary_tag *left = tag->split_left;
|
||||
|
||||
left->real_size += tag->real_size;
|
||||
left->split_right = tag->split_right;
|
||||
|
||||
if ( tag->split_right != NULL ) tag->split_right->split_left = left;
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
|
||||
static inline struct boundary_tag* absorb_right( struct boundary_tag *tag )
|
||||
{
|
||||
struct boundary_tag *right = tag->split_right;
|
||||
|
||||
remove_tag( right ); // Remove right from free pages.
|
||||
|
||||
tag->real_size += right->real_size;
|
||||
|
||||
tag->split_right = right->split_right;
|
||||
if ( right->split_right != NULL )
|
||||
right->split_right->split_left = tag;
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
static inline struct boundary_tag* split_tag( struct boundary_tag* tag )
|
||||
{
|
||||
unsigned int remainder = tag->real_size - sizeof(struct boundary_tag) - tag->size;
|
||||
|
||||
struct boundary_tag *new_tag =
|
||||
(struct boundary_tag*)((unsigned int)tag + sizeof(struct boundary_tag) + tag->size);
|
||||
|
||||
new_tag->magic = LIBALLOC_MAGIC;
|
||||
new_tag->real_size = remainder;
|
||||
|
||||
new_tag->next = NULL;
|
||||
new_tag->prev = NULL;
|
||||
|
||||
new_tag->split_left = tag;
|
||||
new_tag->split_right = tag->split_right;
|
||||
|
||||
if (new_tag->split_right != NULL) new_tag->split_right->split_left = new_tag;
|
||||
tag->split_right = new_tag;
|
||||
|
||||
tag->real_size -= new_tag->real_size;
|
||||
|
||||
insert_tag( new_tag, -1 );
|
||||
|
||||
return new_tag;
|
||||
}
|
||||
|
||||
|
||||
// ***************************************************************
|
||||
|
||||
|
||||
|
||||
|
||||
static struct boundary_tag* allocate_new_tag( unsigned int size )
|
||||
{
|
||||
unsigned int pages;
|
||||
unsigned int usage;
|
||||
struct boundary_tag *tag;
|
||||
|
||||
// This is how much space is required.
|
||||
usage = size + sizeof(struct boundary_tag);
|
||||
|
||||
// Perfect amount of space
|
||||
pages = usage / l_pageSize;
|
||||
if ( (usage % l_pageSize) != 0 ) pages += 1;
|
||||
|
||||
// Make sure it's >= the minimum size.
|
||||
if ( pages < l_pageCount ) pages = l_pageCount;
|
||||
|
||||
tag = (struct boundary_tag*)liballoc_alloc( pages );
|
||||
|
||||
if ( tag == NULL ) return NULL; // uh oh, we ran out of memory.
|
||||
|
||||
tag->magic = LIBALLOC_MAGIC;
|
||||
tag->size = size;
|
||||
tag->real_size = pages * l_pageSize;
|
||||
tag->index = -1;
|
||||
|
||||
tag->next = NULL;
|
||||
tag->prev = NULL;
|
||||
tag->split_left = NULL;
|
||||
tag->split_right = NULL;
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Resource allocated %x of %i pages (%i bytes) for %i size.\n", tag, pages, pages * l_pageSize, size );
|
||||
|
||||
l_allocated += pages * l_pageSize;
|
||||
|
||||
printf("Total memory usage = %i KB\n", (int)((l_allocated / (1024))) );
|
||||
#endif
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *malloc(size_t size)
|
||||
{
|
||||
int index;
|
||||
void *ptr;
|
||||
struct boundary_tag *tag = NULL;
|
||||
|
||||
liballoc_lock();
|
||||
|
||||
if ( l_initialized == 0 )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("%s\n","liballoc initializing.");
|
||||
#endif
|
||||
for ( index = 0; index < MAXEXP; index++ )
|
||||
{
|
||||
l_freePages[index] = NULL;
|
||||
l_completePages[index] = 0;
|
||||
}
|
||||
l_initialized = 1;
|
||||
}
|
||||
|
||||
index = getexp( size ) + MODE;
|
||||
if ( index < MINEXP ) index = MINEXP;
|
||||
|
||||
|
||||
// Find one big enough.
|
||||
tag = l_freePages[ index ]; // Start at the front of the list.
|
||||
while ( tag != NULL )
|
||||
{
|
||||
// If there's enough space in this tag.
|
||||
if ( (tag->real_size - sizeof(struct boundary_tag))
|
||||
>= (size + sizeof(struct boundary_tag) ) )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Tag search found %i >= %i\n",(tag->real_size - sizeof(struct boundary_tag)), (size + sizeof(struct boundary_tag) ) );
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
tag = tag->next;
|
||||
}
|
||||
|
||||
|
||||
// No page found. Make one.
|
||||
if ( tag == NULL )
|
||||
{
|
||||
if ( (tag = allocate_new_tag( size )) == NULL )
|
||||
{
|
||||
liballoc_unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
index = getexp( tag->real_size - sizeof(struct boundary_tag) );
|
||||
}
|
||||
else
|
||||
{
|
||||
remove_tag( tag );
|
||||
|
||||
if ( (tag->split_left == NULL) && (tag->split_right == NULL) )
|
||||
l_completePages[ index ] -= 1;
|
||||
}
|
||||
|
||||
// We have a free page. Remove it from the free pages list.
|
||||
|
||||
tag->size = size;
|
||||
|
||||
// Removed... see if we can re-use the excess space.
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Found tag with %i bytes available (requested %i bytes, leaving %i), which has exponent: %i (%i bytes)\n", tag->real_size - sizeof(struct boundary_tag), size, tag->real_size - size - sizeof(struct boundary_tag), index, 1<<index );
|
||||
#endif
|
||||
|
||||
unsigned int remainder = tag->real_size - size - sizeof( struct boundary_tag ) * 2; // Support a new tag + remainder
|
||||
|
||||
if ( ((int)(remainder) > 0) /*&& ( (tag->real_size - remainder) >= (1<<MINEXP))*/ )
|
||||
{
|
||||
int childIndex = getexp( remainder );
|
||||
|
||||
if ( childIndex >= 0 )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Seems to be splittable: %i >= 2^%i .. %i\n", remainder, childIndex, (1<<childIndex) );
|
||||
#endif
|
||||
|
||||
struct boundary_tag *new_tag = split_tag( tag );
|
||||
|
||||
new_tag = new_tag; // Get around the compiler warning about unused variables.
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Old tag has become %i bytes, new tag is now %i bytes (%i exp)\n", tag->real_size, new_tag->real_size, new_tag->index );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ptr = (void*)((unsigned int)tag + sizeof( struct boundary_tag ) );
|
||||
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
l_inuse += size;
|
||||
printf("malloc: %x, %i, %i\n", ptr, (int)l_inuse / 1024, (int)l_allocated / 1024 );
|
||||
dump_array();
|
||||
#endif
|
||||
|
||||
|
||||
liballoc_unlock();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void free(void *ptr)
|
||||
{
|
||||
int index;
|
||||
struct boundary_tag *tag;
|
||||
|
||||
if ( ptr == NULL ) return;
|
||||
|
||||
liballoc_lock();
|
||||
|
||||
|
||||
tag = (struct boundary_tag*)((unsigned int)ptr - sizeof( struct boundary_tag ));
|
||||
|
||||
if ( tag->magic != LIBALLOC_MAGIC )
|
||||
{
|
||||
liballoc_unlock(); // release the lock
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
l_inuse -= tag->size;
|
||||
printf("free: %x, %i, %i\n", ptr, (int)l_inuse / 1024, (int)l_allocated / 1024 );
|
||||
#endif
|
||||
|
||||
|
||||
// MELT LEFT...
|
||||
while ( (tag->split_left != NULL) && (tag->split_left->index >= 0) )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Melting tag left into available memory. Left was %i, becomes %i (%i)\n", tag->split_left->real_size, tag->split_left->real_size + tag->real_size, tag->split_left->real_size );
|
||||
#endif
|
||||
tag = melt_left( tag );
|
||||
remove_tag( tag );
|
||||
}
|
||||
|
||||
// MELT RIGHT...
|
||||
while ( (tag->split_right != NULL) && (tag->split_right->index >= 0) )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Melting tag right into available memory. This was was %i, becomes %i (%i)\n", tag->real_size, tag->split_right->real_size + tag->real_size, tag->split_right->real_size );
|
||||
#endif
|
||||
tag = absorb_right( tag );
|
||||
}
|
||||
|
||||
|
||||
// Where is it going back to?
|
||||
index = getexp( tag->real_size - sizeof(struct boundary_tag) );
|
||||
if ( index < MINEXP ) index = MINEXP;
|
||||
|
||||
// A whole, empty block?
|
||||
if ( (tag->split_left == NULL) && (tag->split_right == NULL) )
|
||||
{
|
||||
|
||||
if ( l_completePages[ index ] == MAXCOMPLETE )
|
||||
{
|
||||
// Too many standing by to keep. Free this one.
|
||||
unsigned int pages = tag->real_size / l_pageSize;
|
||||
|
||||
if ( (tag->real_size % l_pageSize) != 0 ) pages += 1;
|
||||
if ( pages < l_pageCount ) pages = l_pageCount;
|
||||
|
||||
liballoc_free( tag, pages );
|
||||
|
||||
#ifdef DEBUG
|
||||
l_allocated -= pages * l_pageSize;
|
||||
printf("Resource freeing %x of %i pages\n", tag, pages );
|
||||
dump_array();
|
||||
#endif
|
||||
|
||||
liballoc_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
l_completePages[ index ] += 1; // Increase the count of complete pages.
|
||||
}
|
||||
|
||||
|
||||
// ..........
|
||||
|
||||
|
||||
insert_tag( tag, index );
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Returning tag with %i bytes (requested %i bytes), which has exponent: %i\n", tag->real_size, tag->size, index );
|
||||
dump_array();
|
||||
#endif
|
||||
|
||||
liballoc_unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void* calloc(size_t nobj, size_t size)
|
||||
{
|
||||
int real_size;
|
||||
void *p;
|
||||
|
||||
real_size = nobj * size;
|
||||
|
||||
p = malloc( real_size );
|
||||
|
||||
liballoc_memset( p, 0, real_size );
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void* realloc(void *p, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
struct boundary_tag *tag;
|
||||
int real_size;
|
||||
|
||||
if ( size == 0 )
|
||||
{
|
||||
free( p );
|
||||
return NULL;
|
||||
}
|
||||
if ( p == NULL ) return malloc( size );
|
||||
|
||||
if ( liballoc_lock != NULL ) liballoc_lock(); // lockit
|
||||
tag = (struct boundary_tag*)((unsigned int)p - sizeof( struct boundary_tag ));
|
||||
real_size = tag->size;
|
||||
if ( liballoc_unlock != NULL ) liballoc_unlock();
|
||||
|
||||
if ( real_size > size ) real_size = size;
|
||||
|
||||
ptr = malloc( size );
|
||||
liballoc_memcpy( ptr, p, real_size );
|
||||
free( p );
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,99 @@
|
||||
#ifndef _LIBALLOC_H
|
||||
#define _LIBALLOC_H
|
||||
|
||||
|
||||
|
||||
// If we are told to not define our own size_t, then we
|
||||
// skip the define.
|
||||
#ifndef _ALLOC_SKIP_DEFINE
|
||||
|
||||
#ifndef _HAVE_SIZE_T
|
||||
#define _HAVE_SIZE_T
|
||||
typedef unsigned int size_t;
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/** This is a boundary tag which is prepended to the
|
||||
* page or section of a page which we have allocated. It is
|
||||
* used to identify valid memory blocks that the
|
||||
* application is trying to free.
|
||||
*/
|
||||
struct boundary_tag
|
||||
{
|
||||
unsigned int magic; //< It's a kind of ...
|
||||
unsigned int size; //< Requested size.
|
||||
unsigned int real_size; //< Actual size.
|
||||
int index; //< Location in the page table.
|
||||
|
||||
struct boundary_tag *split_left; //< Linked-list info for broken pages.
|
||||
struct boundary_tag *split_right; //< The same.
|
||||
|
||||
struct boundary_tag *next; //< Linked list info.
|
||||
struct boundary_tag *prev; //< Linked list info.
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** This function is supposed to lock the memory data structures. It
|
||||
* could be as simple as disabling interrupts or acquiring a spinlock.
|
||||
* It's up to you to decide.
|
||||
*
|
||||
* \return 0 if the lock was acquired successfully. Anything else is
|
||||
* failure.
|
||||
*/
|
||||
extern int liballoc_lock();
|
||||
|
||||
/** This function unlocks what was previously locked by the liballoc_lock
|
||||
* function. If it disabled interrupts, it enables interrupts. If it
|
||||
* had acquiried a spinlock, it releases the spinlock. etc.
|
||||
*
|
||||
* \return 0 if the lock was successfully released.
|
||||
*/
|
||||
extern int liballoc_unlock();
|
||||
|
||||
/** This is the hook into the local system which allocates pages. It
|
||||
* accepts an integer parameter which is the number of pages
|
||||
* required. The page size was set up in the liballoc_init function.
|
||||
*
|
||||
* \return NULL if the pages were not allocated.
|
||||
* \return A pointer to the allocated memory.
|
||||
*/
|
||||
extern void* liballoc_alloc(int);
|
||||
|
||||
/** This frees previously allocated memory. The void* parameter passed
|
||||
* to the function is the exact same value returned from a previous
|
||||
* liballoc_alloc call.
|
||||
*
|
||||
* The integer value is the number of pages to free.
|
||||
*
|
||||
* \return 0 if the memory was successfully freed.
|
||||
*/
|
||||
extern int liballoc_free(void*,int);
|
||||
|
||||
|
||||
|
||||
void *malloc(size_t); //< The standard function.
|
||||
void *realloc(void *, size_t); //< The standard function.
|
||||
void *calloc(size_t, size_t); //< The standard function.
|
||||
void free(void *); //< The standard function.
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -0,0 +1,64 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
|
||||
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
|
||||
# define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
#if !defined(MAP_FAILED)
|
||||
# define MAP_FAILED ((char*)-1)
|
||||
#endif
|
||||
|
||||
#ifndef MAP_NORESERVE
|
||||
# ifdef MAP_AUTORESRV
|
||||
# define MAP_NORESERVE MAP_AUTORESRV
|
||||
# else
|
||||
# define MAP_NORESERVE 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int page_size = -1;
|
||||
|
||||
|
||||
int liballoc_lock()
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int liballoc_unlock()
|
||||
{
|
||||
pthread_mutex_unlock( &mutex );
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* liballoc_alloc( int pages )
|
||||
{
|
||||
if ( page_size < 0 ) page_size = getpagesize();
|
||||
unsigned int size = pages * page_size;
|
||||
|
||||
char *p2 = (char*)mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE|MAP_ANONYMOUS, -1, 0);
|
||||
if ( p2 == MAP_FAILED) return NULL;
|
||||
|
||||
if(mprotect(p2, size, PROT_READ|PROT_WRITE) != 0)
|
||||
{
|
||||
munmap(p2, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p2;
|
||||
}
|
||||
|
||||
int liballoc_free( void* ptr, int pages )
|
||||
{
|
||||
return munmap( ptr, pages * page_size );
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
|
||||
FILES=main.cpp malloc_test.cpp
|
||||
|
||||
|
||||
all:
|
||||
@echo "Hello! This is an arbitrary test."
|
||||
@echo
|
||||
@echo "Usage: make [ clean | linux ]"
|
||||
@echo
|
||||
|
||||
|
||||
clean:
|
||||
rm -f ./*.o
|
||||
rm -f ./mtest
|
||||
|
||||
linux:
|
||||
g++ -I../ $(FILES) -o mtest
|
||||
rm -rf ./*.o
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
|
||||
Usage:
|
||||
|
||||
make linux
|
||||
LD_PRELOAD=../liballoc.so ./mtest
|
||||
|
||||
.. and then just ..
|
||||
|
||||
./mtest
|
||||
|
||||
... to compare the results between liballoc and
|
||||
the default malloc/free implementations ...
|
||||
|
||||
|
||||
|
||||
|
||||
This is an arbitrary test which does not simulate real-life
|
||||
events enough to fully guage the performance of the library.
|
||||
However, it's primarily used to test correctness.
|
||||
|
||||
|
@ -0,0 +1,24 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
|
||||
extern int malloc_test( int verbose );
|
||||
|
||||
|
||||
int main( int argc, char *argv )
|
||||
{
|
||||
int verbose = 0;
|
||||
if ( argc > 1 ) verbose = 1;
|
||||
|
||||
printf("%s\n","memory testing application" );
|
||||
|
||||
malloc_test( verbose );
|
||||
|
||||
printf("%s\n","all tests passed!");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,195 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#define MAX_BLOCKS 100
|
||||
#define MAX_SIZE (1024 * 1024)
|
||||
#define MAX_TIME ( 1 * 60 )
|
||||
|
||||
|
||||
/** A testing block to hold all allocated data. */
|
||||
struct block
|
||||
{
|
||||
unsigned char *data;
|
||||
int size;
|
||||
unsigned char key;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** The testing blocks. */
|
||||
static struct block blocks[ MAX_BLOCKS ];
|
||||
static long long totalMemory = 0;
|
||||
static int totalBlocks = 0;
|
||||
|
||||
|
||||
static int g_verbose = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
static int malloc_random( int verbose )
|
||||
{
|
||||
g_verbose = verbose;
|
||||
totalMemory = 0;
|
||||
totalBlocks = 0;
|
||||
|
||||
printf("malloc_random: this will take %i minute...\n", MAX_TIME/ 60 );
|
||||
|
||||
|
||||
for ( int i = 0; i < MAX_BLOCKS; i++ )
|
||||
{
|
||||
blocks[ i ].data = NULL;
|
||||
blocks[ i ].size = 0;
|
||||
blocks[ i ].key = 0;
|
||||
}
|
||||
|
||||
int transactions = 0;
|
||||
time_t start_time = time(NULL);
|
||||
|
||||
// Random madness.
|
||||
while (1==1)
|
||||
{
|
||||
int position = rand() % MAX_BLOCKS;
|
||||
|
||||
int diff = time(NULL) - start_time;
|
||||
if ( diff > ( MAX_TIME ) ) break;
|
||||
|
||||
int tps = (++transactions) / (diff + 1);
|
||||
|
||||
|
||||
|
||||
if ( blocks[position].data == NULL )
|
||||
{
|
||||
blocks[position].size = rand() % MAX_SIZE;
|
||||
blocks[position].data = (unsigned char*)malloc( blocks[position].size );
|
||||
blocks[position].key = rand() % 256;
|
||||
|
||||
if ( g_verbose != 0 )
|
||||
printf("%i left, %i tps : %i, %i : %i: allocating %i bytes with %i key\n",
|
||||
( MAX_TIME - diff ),
|
||||
tps,
|
||||
totalBlocks * 100 / MAX_BLOCKS,
|
||||
(int)(totalMemory / (1024)),
|
||||
position,
|
||||
blocks[position].size,
|
||||
blocks[position].key );
|
||||
|
||||
if ( blocks[position].data != NULL )
|
||||
{
|
||||
totalMemory += blocks[position].size;
|
||||
totalBlocks += 1;
|
||||
|
||||
for ( int j = 0; j < blocks[position].size; j++ )
|
||||
blocks[position].data[j] = blocks[position].key;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int j = 0; j < blocks[position].size; j++ )
|
||||
if ( blocks[position].data[j] != blocks[position].key )
|
||||
{
|
||||
printf( "%i: %x (%i bytes, position %i) %i != %i: ERROR! Memory not consistent",
|
||||
position,
|
||||
blocks[position].data,
|
||||
blocks[position].size,
|
||||
j,
|
||||
blocks[position].data[j],
|
||||
blocks[position].key );
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
if ( g_verbose != 0 )
|
||||
printf("%i left, %i tps : %i, %i : %i: freeing %i bytes with %i key\n",
|
||||
( MAX_TIME - diff ),
|
||||
tps,
|
||||
totalBlocks * 100 / MAX_BLOCKS,
|
||||
(int)(totalMemory / (1024)),
|
||||
position,
|
||||
blocks[position].size,
|
||||
blocks[position].key );
|
||||
|
||||
free( blocks[position].data );
|
||||
blocks[position].data = NULL;
|
||||
|
||||
totalMemory -= blocks[position].size;
|
||||
totalBlocks -= 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Dump the memory map here.
|
||||
|
||||
|
||||
// Free.
|
||||
for ( int i = 0; i < MAX_BLOCKS; i++ )
|
||||
{
|
||||
if ( blocks[ i ].data != NULL ) free( blocks[ i ].data );
|
||||
blocks[ i ].size = 0;
|
||||
blocks[ i ].key = 0;
|
||||
}
|
||||
|
||||
|
||||
// Final results.
|
||||
printf("%i TPS, %i%s USAGE\n", transactions / MAX_TIME, totalBlocks * 100 / MAX_BLOCKS, "%" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int malloc_large( int verbose )
|
||||
{
|
||||
g_verbose = verbose;
|
||||
|
||||
printf("malloc_large: going to exhaust the memory...\n" );
|
||||
|
||||
for ( int i = 0; i < MAX_BLOCKS; i++ )
|
||||
blocks[ i ].data = NULL;
|
||||
|
||||
int transactions = 0;
|
||||
time_t start_time = time(NULL);
|
||||
|
||||
for ( int i = 0; i < MAX_BLOCKS; i++ )
|
||||
{
|
||||
blocks[ i ].data = (unsigned char*)malloc( MAX_SIZE );
|
||||
if ( blocks[i].data == NULL ) break;
|
||||
|
||||
transactions += 1;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < MAX_BLOCKS; i++ )
|
||||
if ( blocks[ i ].data != NULL ) free( blocks[ i ].data );
|
||||
|
||||
|
||||
// Final results.
|
||||
printf("%i blocks of %i size = %i MB, %i seconds\n",
|
||||
transactions,
|
||||
MAX_SIZE,
|
||||
(transactions * MAX_SIZE) / (1024 * 1024),
|
||||
time(NULL) - start_time
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int malloc_test( int verbose )
|
||||
{
|
||||
malloc_random( verbose );
|
||||
malloc_random( verbose );
|
||||
malloc_random( verbose );
|
||||
malloc_large( verbose );
|
||||
malloc_large( verbose );
|
||||
malloc_large( verbose );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in new issue