memlog
[vg.git] / vg_mem.h
1 #ifndef VG_MEM_H
2 #define VG_MEM_H
3
4 #include "vg.h"
5 #include "vg_stdint.h"
6 #include "vg_platform.h"
7
8 #include <stdlib.h>
9 #include <malloc.h>
10
11 VG_STATIC void vg_print_backtrace(void);
12
13 #define VG_MAX_ALLOCATIONS 128
14 #define VG_FUZZ_ALLOCATIONS
15
16 typedef struct vg_linear_allocator vg_linear_allocator;
17 typedef struct vg_allocation_meta vg_allocation_meta;
18
19 struct{
20 void *rtmemory,
21 *scratch;
22
23 int use_libc_malloc;
24 u32 quota;
25 }
26 static vg_mem;
27
28 struct vg_allocation_meta
29 {
30 const char *name;
31 void *data;
32 u32 size;
33 enum allocation_type{
34 k_allocation_type_block = 0,
35 k_allocation_type_linear = 1
36 }
37 type;
38 };
39
40 #define VG_MEMORY_SYSTEM 0x1 /* systems memory, slow and low counts */
41 #define VG_MEMORY_REALTIME 0x2 /* per-frame. no max allocs, only size. fast */
42
43 /* systems memory cannot be declared inside realtime memory regions */
44
45 /*
46 * Stored just behind the array. 32 bytes.
47 */
48 #pragma pack(push,1)
49 struct vg_linear_allocator
50 {
51 u32 size;
52 u32 cur;
53 u16 allocation_count;
54 u16 flags;
55 u32 last_alloc_size;
56 void *last_alloc;
57 vg_allocation_meta *alloc_table;
58
59 #ifdef _WIN32
60 /* 32 bit pointers! */
61 u8 padding[ 8 ];
62 #endif
63 };
64 #pragma pack(pop)
65
66 VG_STATIC void vg_fatal_error( const char *fmt, ... );
67 VG_STATIC void vg_error(const char *fmt, ...);
68 VG_STATIC void vg_info(const char *fmt, ...);
69
70 VG_STATIC u32 vg_align8( u32 s )
71 {
72 u32 m = (s + 7) >> 3;
73 return m << 3;
74 }
75
76 VG_STATIC u32 vg_align4( u32 s )
77 {
78 u32 m = (s + 3) >> 2;
79 return m << 2;
80 }
81
82 /* Returns allocator structure from data pointer */
83 static vg_linear_allocator *vg_linear_header( void *data )
84 {
85 vg_linear_allocator *ptr = data;
86 ptr --;
87
88 return ptr;
89 }
90
91 /* allocate something from a linear allocator */
92 __attribute__((warn_unused_result))
93 VG_STATIC void *_vg_linear_alloc( void *buffer, u32 size,
94 const char *constr_name )
95 {
96 if( size % 8 ){
97 vg_error( "alloc(%u) is not 8 byte aligned\n", size );
98 vg_print_backtrace();
99 size = vg_align8( size );
100 }
101 #ifdef _WIN32
102 if( ((u32)buffer) % 8 ){
103 #else
104 if( ((u64)buffer) % 8 ){
105 #endif
106 vg_fatal_error( "unaligned buffer (%p)", buffer );
107 }
108
109 vg_linear_allocator *alloc = vg_linear_header( buffer );
110
111 if( (alloc->cur + size) > alloc->size ){
112 vg_fatal_error( "linear allocator overflow (%u + %u > %u)\n",
113 alloc->cur, size, alloc->size );
114 }
115
116 if( alloc->flags & VG_MEMORY_SYSTEM )
117 if( (alloc->allocation_count + 1) > VG_MAX_ALLOCATIONS )
118 vg_fatal_error( "Max linear allocations reached" );
119
120 void *data;
121
122 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
123 data = malloc( size );
124
125 vg_allocation_meta *meta = &alloc->alloc_table[ alloc->allocation_count ];
126 meta->type = k_allocation_type_block;
127 meta->data = data;
128 meta->size = size;
129 meta->name = constr_name;
130 }
131 else{
132 data = buffer + alloc->cur;
133 }
134
135 u8 *bytes = data;
136 for( u32 i=0; i<size; i++ ){
137 bytes[i] = 0xfe;
138 }
139
140 alloc->allocation_count ++;
141 alloc->last_alloc = data;
142 alloc->last_alloc_size = size;
143 alloc->cur += size;
144
145 #ifdef _WIN32
146 if( ((u32)data) % 8 ){
147 #else
148 if( ((u64)data) % 8 ){
149 #endif
150 vg_fatal_error( "unaligned" );
151 }
152
153 return data;
154 }
155
156 /* resize latest block of memory from linear */
157 __attribute__((warn_unused_result))
158 VG_STATIC void *vg_linear_resize( void *buffer, void *data, u32 newsize )
159 {
160 vg_linear_allocator *alloc = vg_linear_header( buffer );
161
162 if( newsize % 8 ){
163 vg_error( "alloc(%u) is not 8 byte aligned\n", newsize );
164 vg_print_backtrace();
165 newsize = vg_align8( newsize );
166 }
167
168 if( alloc->last_alloc != data )
169 vg_fatal_error( "This block has been fixed!" );
170
171 if( (alloc->cur - alloc->last_alloc_size + newsize) > alloc->size )
172 vg_fatal_error( "Cannot resize, overflow" );
173
174 alloc->cur -= alloc->last_alloc_size;
175 alloc->cur += newsize;
176 alloc->last_alloc_size = newsize;
177
178 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
179 data = realloc( data, newsize );
180 if( !data )
181 vg_fatal_error( "realloc failed" );
182
183 alloc->alloc_table[ alloc->allocation_count-1 ].data = data;
184 alloc->last_alloc = data;
185 return data;
186 }
187 else{
188 return data;
189 }
190 }
191
192 /* its possible to delete just the last item */
193 VG_STATIC void vg_linear_del( void *buffer, void *data )
194 {
195 vg_linear_allocator *alloc = vg_linear_header( buffer );
196
197 if( alloc->last_alloc != data ){
198 vg_fatal_error( "This block has been fixed! Last alloc: %p, this: %p\n",
199 alloc->last_alloc, data );
200 }
201
202 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
203 vg_allocation_meta *meta = &alloc->alloc_table[alloc->allocation_count-1];
204 if( meta->type == k_allocation_type_linear )
205 vg_fatal_error( "Cannot free a linear allocator in this conext" );
206
207 free( data );
208 }
209
210 alloc->allocation_count --;
211 alloc->cur -= alloc->last_alloc_size;
212 alloc->last_alloc = NULL;
213 alloc->last_alloc_size = 0;
214 }
215
216 /* extend latest block of memory from linear */
217 __attribute__((warn_unused_result))
218 VG_STATIC void *_vg_linear_extend( void *buffer, void *data, u32 extra,
219 const char *constr_name )
220 {
221 if( !data )
222 return _vg_linear_alloc( buffer, extra, constr_name );
223
224 vg_linear_allocator *alloc = vg_linear_header( buffer );
225
226 if( alloc->last_alloc != data )
227 vg_fatal_error( "This block has been fixed!" );
228
229 u32 new_size = alloc->last_alloc_size + extra;
230 return vg_linear_resize( buffer, data, new_size );
231 }
232
233 /* get the current usage of allocator */
234 VG_STATIC u32 vg_linear_get_cur( void *buffer )
235 {
236 vg_linear_allocator *alloc = vg_linear_header( buffer );
237 return alloc->cur;
238 }
239
240 /* get the capacity of allocator. */
241 VG_STATIC u32 vg_linear_get_capacity( void *buffer )
242 {
243 vg_linear_allocator *alloc = vg_linear_header( buffer );
244 return alloc->size;
245 }
246
247 /* get the remaining size of the allocator */
248 VG_STATIC u32 vg_linear_remaining( void *buffer )
249 {
250 vg_linear_allocator *alloc = vg_linear_header( buffer );
251 return alloc->size - alloc->cur;
252 }
253
254 /* yeet all memory from linear allocator */
255 VG_STATIC void vg_linear_clear( void *buffer )
256 {
257 vg_linear_allocator *alloc = vg_linear_header( buffer );
258
259 /* libc mode we recursively free any allocations made */
260 if( vg_mem.use_libc_malloc && (alloc->flags & VG_MEMORY_SYSTEM) ){
261 for( u32 i=0; i<alloc->allocation_count; i++ ){
262 vg_allocation_meta *meta = &alloc->alloc_table[i];
263
264 if( meta->type == k_allocation_type_block ){
265 free( meta->data );
266 }
267 else{
268 vg_linear_clear( meta->data );
269 vg_linear_allocator *sub = vg_linear_header( meta->data );
270
271 free( sub->alloc_table );
272 free( sub );
273 }
274 }
275 }
276
277 alloc->last_alloc = NULL;
278 alloc->last_alloc_size = 0;
279 alloc->allocation_count = 0;
280 alloc->cur = 0;
281 }
282
283 /* allocate a FIXED SIZE linear allocator */
284 VG_STATIC void *_vg_create_linear_allocator( void *lin_alloc, u32 size,
285 u16 flags, const char *constr_name)
286 {
287 assert( sizeof( vg_linear_allocator ) == 32 );
288
289 vg_linear_allocator *header;
290 u32 block_size = size + sizeof(vg_linear_allocator);
291
292 /* Creating it inside an existing one */
293 if( lin_alloc ){
294 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
295
296 if( alloc->cur + block_size > alloc->size )
297 vg_fatal_error( "Out of memory" );
298
299 if( alloc->allocation_count + 1 > VG_MAX_ALLOCATIONS )
300 vg_fatal_error( "Max allocations in linear allocator" );
301
302 if( (flags && VG_MEMORY_SYSTEM) && (alloc->flags & VG_MEMORY_REALTIME) )
303 vg_fatal_error( "Cannot declare realtime allocator inside systems"
304 " allocator" );
305
306 if( vg_mem.use_libc_malloc ){
307 vg_allocation_meta *meta =
308 &alloc->alloc_table[ alloc->allocation_count ];
309
310 if( flags & VG_MEMORY_REALTIME )
311 header = malloc( block_size );
312 else
313 header = malloc( sizeof(vg_linear_allocator) );
314
315 meta->data = header+1;
316 meta->type = k_allocation_type_linear;
317 meta->size = size;
318 meta->name = constr_name;
319 }
320 else{
321 header = lin_alloc + alloc->cur;
322 }
323
324 alloc->cur += block_size;
325 alloc->last_alloc = header;
326 alloc->last_alloc_size = block_size;
327 alloc->allocation_count ++;
328 }
329 else{
330 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) )
331 header = malloc( sizeof(vg_linear_allocator) );
332 else
333 header = malloc( block_size );
334 }
335
336 header->allocation_count = 0;
337 header->cur = 0;
338 header->last_alloc = NULL;
339 header->last_alloc_size = 0;
340 header->size = size;
341 header->flags = flags;
342
343 if( vg_mem.use_libc_malloc && (flags & VG_MEMORY_SYSTEM) ){
344 u32 table_size = sizeof(vg_allocation_meta)*VG_MAX_ALLOCATIONS;
345 header->alloc_table = malloc( table_size );
346 }
347 else
348 header->alloc_table = NULL;
349
350 return header+1;
351 }
352
353 /* request all the memory we need in advance */
354 VG_STATIC void vg_set_mem_quota( u32 size )
355 {
356 vg_mem.quota = size;
357 }
358
359 VG_STATIC void vg_alloc_quota(void)
360 {
361 u32 size_scratch = 10*1024*1024;
362 u32 size = VG_MAX( vg_mem.quota, size_scratch );
363
364 vg_mem.rtmemory = _vg_create_linear_allocator( NULL, size, VG_MEMORY_SYSTEM,
365 "VG Root" );
366 vg_mem.scratch = _vg_create_linear_allocator( vg_mem.rtmemory,
367 size_scratch,
368 VG_MEMORY_SYSTEM,
369 "Scratch buffer" );
370 }
371
372 VG_STATIC void vg_mem_log( void *lin_alloc, int depth, const char *name )
373 {
374 if( vg_mem.use_libc_malloc ){
375 vg_linear_allocator *alloc = vg_linear_header( lin_alloc );
376
377 u32 s = alloc->size;
378 f32 p = ((float)alloc->cur / (float)alloc->size) * 100.0f;
379
380 for( int i=0; i<depth; i++ ) printf( " " );
381 printf( "LA(%s): %u bytes, %f%% used\n", name, s, p );
382
383 if( alloc->flags & VG_MEMORY_SYSTEM ){
384 for( u32 i=0; i<alloc->allocation_count; i++ ){
385 vg_allocation_meta *meta = &alloc->alloc_table[i];
386
387 if( meta->type == k_allocation_type_block ){
388 for( int i=0; i<depth+1; i++ ) printf( " " );
389 printf( "B(%s): %u bytes\n", meta->name, meta->size );
390 }
391 else{
392 vg_mem_log( meta->data, depth +1, meta->name );
393 }
394 }
395 }
396 else{
397 for( int i=0; i<depth+1; i++ ) printf( " " );
398 printf( "<opaque memory> (UNTRACKED)\n" );
399 }
400 }
401 else{
402 vg_error( "allocations are not tracked (turn on libc mode)\n" );
403 }
404 }
405
406 #define VG_MEM_MCSTR(S) VG_MEM_MCSTR2(S)
407 #define VG_MEM_MCSTR2(S) #S
408
409 #define vg_linear_alloc(...) \
410 _vg_linear_alloc( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
411 #define vg_linear_extend(...) \
412 _vg_linear_extend( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
413 #define vg_create_linear_allocator(...) \
414 _vg_create_linear_allocator( __VA_ARGS__, __FILE__":"VG_MEM_MCSTR(__LINE__) )
415
416
417 #endif /* VG_MEM_H */