#include "vg.h"
#include "vg_stdint.h"
#include "vg_platform.h"
+#include "vg_log.h"
#include <stdlib.h>
#include <malloc.h>
-#define VG_MAX_ALLOCATIONS 64
+static void vg_print_backtrace(void);
+
+#define VG_MAX_ALLOCATIONS 128
#define VG_FUZZ_ALLOCATIONS
typedef struct vg_linear_allocator vg_linear_allocator;
typedef struct vg_allocation_meta vg_allocation_meta;
-struct
-{
+struct{
void *rtmemory,
*scratch;
struct vg_allocation_meta
{
+ const char *name;
void *data;
- enum allocation_type
- {
- k_allocation_type_block = 0,
+ u32 size;
+ enum allocation_type{
+ k_allocation_type_block = 0,
k_allocation_type_linear = 1
}
type;
struct vg_linear_allocator
{
u32 size;
- /* */
- /* */
- /* */
u32 cur;
- /* */
- /* */
- /* */
u16 allocation_count;
- /* */
u16 flags;
- /* */
u32 last_alloc_size;
- /* */
- /* */
- /* */
void *last_alloc;
- /* */
- /* */
- /* */
- /* */
- /* */
- /* */
- /* */
vg_allocation_meta *alloc_table;
- /* */
- /* */
- /* */
- /* */
- /* */
- /* */
- /* */
#ifdef _WIN32
/* 32 bit pointers! */
};
#pragma pack(pop)
-VG_STATIC void vg_fatal_exit_loop( const char *error );
-VG_STATIC void vg_error(const char *fmt, ...);
-VG_STATIC void vg_info(const char *fmt, ...);
+static u32 vg_align8( u32 s );
+static u32 vg_align4( u32 s );
+
+/* allocate something from a linear allocator */
+__attribute__((warn_unused_result))
+static void *_vg_linear_alloc( void *buffer, u32 size,
+ const char *constr_name );
+
+/* resize latest block of memory from linear */
+__attribute__((warn_unused_result))
+static void *vg_linear_resize( void *buffer, void *data, u32 newsize );
+
+/* its possible to delete just the last item */
+static void vg_linear_del( void *buffer, void *data );
+
+/* extend latest block of memory from linear */
+__attribute__((warn_unused_result))
+static void *_vg_linear_extend( void *buffer, void *data, u32 extra,
+ const char *constr_name );
+
+/* get the current usage of allocator */
+static u32 vg_linear_get_cur( void *buffer );
+
+/* get the capacity of allocator. */
+static u32 vg_linear_get_capacity( void *buffer );
+
+/* get the remaining size of the allocator */
+static u32 vg_linear_remaining( void *buffer );
+
+/* yeet all memory from linear allocator */
+static void vg_linear_clear( void *buffer );
+
+/* request all the memory we need in advance */
+static void vg_set_mem_quota( u32 size );
+
+/* essentially init() */
+static void vg_alloc_quota(void);
+
+/* print out tree of current memory used. only works with libc mode */
+static void vg_mem_log( void *lin_alloc, int depth, const char *name );
+
+#define VG_MEM_MCSTR(S) VG_MEM_MCSTR2(S)
+#define VG_MEM_MCSTR2(S) #S
+
+#define vg_linear_alloc(...) \
+ _vg_linear_alloc( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
+#define vg_linear_extend(...) \
+ _vg_linear_extend( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
+#define vg_create_linear_allocator(...) \
+ _vg_create_linear_allocator( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
+
+/* implementation
+ * ----------------------------------------
+ */
+
+static void vg_fatal_error( const char *fmt, ... );
-VG_STATIC u32 vg_align8( u32 s )
+#if 0
+static void vg_error(const char *fmt, ...);
+static void vg_info(const char *fmt, ...);
+#endif
+
+static u32 vg_align8( u32 s )
{
u32 m = (s + 7) >> 3;
return m << 3;
}
-VG_STATIC u32 vg_align4( u32 s )
+static u32 vg_align4( u32 s )
{
u32 m = (s + 3) >> 2;
return m << 2;
/* allocate something from a linear allocator */
__attribute__((warn_unused_result))
-VG_STATIC void *vg_linear_alloc( void *buffer, u32 size )
+static void *_vg_linear_alloc( void *buffer, u32 size,
+ const char *constr_name )
{
- if( size % 8 )
- {
+ if( size % 8 ){
vg_error( "alloc(%u) is not 8 byte aligned\n", size );
+ vg_print_backtrace();
size = vg_align8( size );
}
+ if( ((u64)buffer) % 8 ){
+ vg_fatal_error( "unaligned buffer (%p)", buffer );
+ }
vg_linear_allocator *alloc = vg_linear_header( buffer );
- if( (alloc->cur + size) > alloc->size )
- {
- vg_error( "%u + %u > %u\n", alloc->cur, size, alloc->size );
- vg_fatal_exit_loop( "linear allocator overflow" );
+ if( (alloc->cur + size) > alloc->size ){
+ vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n",
+ alloc->cur, size, alloc->size );
}
if( alloc->flags & VG_MEMORY_SYSTEM )
if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS )
- vg_fatal_exit_loop( "Max linear allocations reached" );
+ vg_fatal_error( "Max linear allocations reached" );
void *data;
- if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
- {
+ if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
data = malloc( size );
vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ];
meta->type = k_allocation_type_block;
meta->data = data;
+ meta->size = size;
+ meta->name = constr_name;
}
- else
- {
+ else{
data = buffer + alloc->cur;
}
+ u8 *bytes = data;
+ for( u32 i=0; i<size; i++ ){
+ bytes[i] = 0xfe;
+ }
+
alloc->allocation_count ++;
alloc->last_alloc = data;
alloc->last_alloc_size = size;
alloc->cur += size;
+ if( ((u64)data) % 8 ){
+ vg_fatal_error( "unaligned" );
+ }
+
return data;
}
-
/* resize latest block of memory from linear */
__attribute__((warn_unused_result))
-VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize )
+static void *vg_linear_resize( void *buffer, void *data, u32 newsize )
{
vg_linear_allocator *alloc = vg_linear_header( buffer );
+ if( newsize % 8 ){
+ vg_error( "alloc(%u) is not 8 byte aligned\n", newsize );
+ vg_print_backtrace();
+ newsize = vg_align8( newsize );
+ }
+
if( alloc->last_alloc != data )
- vg_fatal_exit_loop( "This block has been fixed!" );
+ vg_fatal_error( "This block has been fixed!" );
if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
- vg_fatal_exit_loop( "Cannot resize, overflow" );
+ vg_fatal_error( "Cannot resize, overflow" );
alloc->cur -= alloc->last_alloc_size;
alloc->cur += newsize;
alloc->last_alloc_size = newsize;
- if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
- {
+ if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
data = realloc( data, newsize );
if( !data )
- vg_fatal_exit_loop( "realloc failed" );
+ vg_fatal_error( "realloc failed" );
alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
alloc->last_alloc = data;
return data;
}
- else
- {
+ else{
return data;
}
}
/* its possible to delete just the last item */
-VG_STATIC void vg_linear_del( void *buffer, void *data )
+static void vg_linear_del( void *buffer, void *data )
{
vg_linear_allocator *alloc = vg_linear_header( buffer );
- if( alloc->last_alloc != data )
- vg_fatal_exit_loop( "This block has been fixed!" );
+ if( alloc->last_alloc != data ){
+ vg_fatal_error( "This block has been fixed! Last alloc: %p, this: %p\n",
+ alloc->last_alloc, data );
+ }
- if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
- {
+ if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
if( meta->type == k_allocation_type_linear )
- vg_fatal_exit_loop( "Cannot free a linear allocator in this conext" );
+ vg_fatal_error( "Cannot free a linear allocator in this conext" );
free( data );
}
/* extend latest block of memory from linear */
__attribute__((warn_unused_result))
-VG_STATIC void *vg_linear_extend( void *buffer, void *data, u32 extra )
+static void *_vg_linear_extend( void *buffer, void *data, u32 extra,
+ const char *constr_name )
{
+ if( !data )
+ return _vg_linear_alloc( buffer, vg_align8(extra), constr_name );
+
vg_linear_allocator *alloc = vg_linear_header( buffer );
if( alloc->last_alloc != data )
- vg_fatal_exit_loop( "This block has been fixed!" );
+ vg_fatal_error( "This block has been fixed!" );
u32 new_size = alloc->last_alloc_size + extra;
- return vg_linear_resize( buffer, data, new_size );
+ return vg_linear_resize( buffer, data, vg_align8(new_size) );
}
/* get the current usage of allocator */
-VG_STATIC u32 vg_linear_get_cur( void *buffer )
+static u32 vg_linear_get_cur( void *buffer )
{
vg_linear_allocator *alloc = vg_linear_header( buffer );
return alloc->cur;
}
/* get the capacity of allocator. */
-VG_STATIC u32 vg_linear_get_capacity( void *buffer )
+static u32 vg_linear_get_capacity( void *buffer )
{
vg_linear_allocator *alloc = vg_linear_header( buffer );
return alloc->size;
}
/* get the remaining size of the allocator */
-VG_STATIC u32 vg_linear_remaining( void *buffer )
+static u32 vg_linear_remaining( void *buffer )
{
vg_linear_allocator *alloc = vg_linear_header( buffer );
return alloc->size - alloc->cur;
}
/* yeet all memory from linear allocator */
-VG_STATIC void vg_linear_clear( void *buffer )
+static void vg_linear_clear( void *buffer )
{
vg_linear_allocator *alloc = vg_linear_header( buffer );
/* libc mode we recursively free any allocations made */
- if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) )
- {
- for( u32 i=0; i<alloc->allocation_count; i++ )
- {
+ if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
+ for( u32 i=0; i<alloc->allocation_count; i++ ){
vg_allocation_meta *meta = &alloc->alloc_table[i];
- if( meta->type == k_allocation_type_block )
- {
+ if( meta->type == k_allocation_type_block ){
free( meta->data );
}
- else
- {
+ else{
vg_linear_clear( meta->data );
vg_linear_allocator *sub = vg_linear_header( meta->data );
alloc->cur = 0;
}
-/* allocate a FIXED SIZE linear allocator */
-VG_STATIC void *vg_create_linear_allocator( void *lin_alloc, u32 size,
- u16 flags )
+/* allocate a FIXED SIZE linear allocator
+ *
+ * FIXME: there was a bug in vg's code that caused a race condition between
+ * two system allocations. make this IMPOSSIBLE by requiring a lock
+ * on the allocater to be passed between threads.
+ *
+ * luckily that bug only exists when using development tools, but still!
+ *
+ * this should then only be checked and turned on in debugging.
+ *
+ */
+static void *_vg_create_linear_allocator( void *lin_alloc, u32 size,
+ u16 flags, const char *constr_name)
{
assert( sizeof( vg_linear_allocator ) == 32 );
u32 block_size = size + sizeof(vg_linear_allocator);
/* Creating it inside an existing one */
- if( lin_alloc )
- {
+ if( lin_alloc ){
vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
if( alloc->cur + block_size > alloc->size )
- vg_fatal_exit_loop( "Out of memory" );
+ vg_fatal_error( "Out of memory" );
if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
- vg_fatal_exit_loop( "Max allocations in linear allocator" );
+ vg_fatal_error( "Max allocations in linear allocator" );
if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
- vg_fatal_exit_loop( "Cannot declare realtime allocator inside systems"
+ vg_fatal_error( "Cannot declare realtime allocator inside systems"
" allocator" );
- if( vg_mem.use_libc_malloc )
- {
+ if( vg_mem.use_libc_malloc ){
vg_allocation_meta *meta =
&alloc->alloc_table[ alloc->allocation_count ];
meta->data = header+1;
meta->type = k_allocation_type_linear;
+ meta->size = size;
+ meta->name = constr_name;
}
- else
- {
+ else{
header = lin_alloc + alloc->cur;
}
alloc->cur += block_size;
- alloc->last_alloc = NULL; /* cant resize this block! */
+ alloc->last_alloc = header;
alloc->last_alloc_size = block_size;
-
alloc->allocation_count ++;
}
- else
- {
+ else{
if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
header = malloc( sizeof(vg_linear_allocator) );
else
header->size = size;
header->flags = flags;
- if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
- {
+ if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ){
u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS;
header->alloc_table = malloc( table_size );
}
}
/* request all the memory we need in advance */
-VG_STATIC void vg_set_mem_quota( u32 size )
+static void vg_set_mem_quota( u32 size )
{
vg_mem.quota = size;
}
-VG_STATIC void vg_alloc_quota(void)
+static void vg_alloc_quota(void)
{
u32 size_scratch = 10*1024*1024;
u32 size = VG_MAX( vg_mem.quota, size_scratch );
- vg_mem.rtmemory = vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM );
- vg_mem.scratch = vg_create_linear_allocator( vg_mem.rtmemory,
- size_scratch,
- VG_MEMORY_SYSTEM );
+ vg_mem.rtmemory = _vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM,
+ "VG Root" );
+ vg_mem.scratch = _vg_create_linear_allocator( vg_mem.rtmemory,
+ size_scratch,
+ VG_MEMORY_SYSTEM,
+ "Scratch buffer" );
+}
+
+static void vg_mem_log( void *lin_alloc, int depth, const char *name )
+{
+ if( vg_mem.use_libc_malloc ){
+ vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
+
+ u32 s = alloc->size;
+ f32 p = ((float)alloc->cur / (float)alloc->size) * 100.0f;
+
+ for( int i=0; i<depth; i++ ) printf( " " );
+ printf( "LA(%s): %u bytes, %f%% used\n", name, s, p );
+
+ if( alloc->flags & VG_MEMORY_SYSTEM ){
+ for( u32 i=0; i<alloc->allocation_count; i++ ){
+ vg_allocation_meta *meta = &alloc->alloc_table[i];
+
+ if( meta->type == k_allocation_type_block ){
+ for( int i=0; i<depth+1; i++ ) printf( " " );
+ printf( "B(%s): %u bytes\n", meta->name, meta->size );
+ }
+ else{
+ vg_mem_log( meta->data, depth +1, meta->name );
+ }
+ }
+ }
+ else{
+ for( int i=0; i<depth+1; i++ ) printf( " " );
+ printf( "<opaque memory> (UNTRACKED)\n" );
+ }
+ }
+ else{
+ vg_error( "allocations are not tracked (turn on libc mode)\n" );
+ }
}
#endif /* VG_MEM_H */