3 * - By John Hodge (thePowersGang)
6 * - Dynamic memory allocation
12 #include <debug_hooks.h>
14 #define WARNINGS 1 // Warn and dump on heap errors
15 #define DEBUG_TRACE 0 // Enable tracing of allocations
16 #define VERBOSE_DUMP 0 // Set to 1 to enable a verbose dump when heap errors are encountered
17 #define VALIDATE_ON_ALLOC 1 // Set to 1 to enable validation of the heap on all malloc() calls
20 #define HEAP_INIT_SIZE 0x8000 // 32 KiB
21 #define MIN_SIZE (sizeof(void*))*8 // 8 Machine Words
23 #define COMPACT_HEAP 0 // Use 4 byte header?
26 //#define MAGIC_FOOT 0x2ACE5505
27 //#define MAGIC_FREE 0xACE55000
28 //#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE
29 #define MAGIC_FOOT 0x544F4F46 // 'FOOT'
30 #define MAGIC_FREE 0x45455246 // 'FREE'
31 #define MAGIC_USED 0x44455355 // 'USED'
34 void Heap_Install(void);
35 void *Heap_Extend(size_t Bytes);
36 void *Heap_Merge(tHeapHead *Head);
37 static const size_t Heap_int_GetBlockSize(size_t AllocSize);
38 //void *Heap_Allocate(const char *File, int Line, size_t Bytes);
39 //void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
40 //void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
41 //void Heap_Deallocate(const char *File, int Line, void *Ptr);
42 int Heap_int_ApplyWatchpont(void *Addr, bool Enabled);
43 void Heap_int_WatchBlock(tHeapHead *Head, bool Enabled);
44 //void Heap_Dump(void);
45 void Heap_ValidateDump(int bVerbose);
46 //void Heap_Stats(void);
50 tHeapHead *gHeapStart;
54 void Heap_Install(void)
56 gHeapStart = (void*)MM_KHEAP_BASE;
57 gHeapEnd = gHeapStart;
58 Heap_Extend(HEAP_INIT_SIZE);
61 static inline tHeapHead *Heap_NextHead(tHeapHead *Head) {
62 return (void*)( (char*)Head + Head->Size );
64 static inline tHeapFoot *Heap_ThisFoot(tHeapHead *Head) {
65 return (void*)( (char*)Head + Head->Size - sizeof(tHeapFoot) );
67 static inline tHeapFoot *Heap_PrevFoot(tHeapHead *Head) {
68 //ASSERT(Head != gHeapStart);
69 return (void*)( (char*)Head - sizeof(tHeapFoot) );
73 * \brief Extend the size of the heap
75 void *Heap_Extend(size_t Bytes)
77 //Debug("Heap_Extend(0x%x)", Bytes);
80 if( gHeapEnd == (tHeapHead*)MM_KHEAP_MAX ) {
81 Log_Error("Heap", "Heap limit reached (%p)", (void*)MM_KHEAP_MAX);
86 Log_Warning("Heap", "Heap_Extend called with Bytes=%i", Bytes);
90 const Uint pages = (Bytes + 0xFFF) >> 12;
91 tHeapHead *new_end = (void*)( (tVAddr)gHeapEnd + (pages << 12) );
93 if( new_end > (tHeapHead*)MM_KHEAP_MAX )
95 Log_Error("Heap", "Heap limit exceeded (%p)", (void*)new_end);
96 // TODO: Clip allocation to available space, and have caller check returned block
100 // Heap expands in pages
101 for( Uint i = 0; i < pages; i ++ )
103 if( !MM_Allocate( (tPage*)gHeapEnd + i ) )
105 Warning("OOM - Heap_Extend (%i bytes)");
112 tHeapHead *head = gHeapEnd;
116 head->Size = (Bytes+0xFFF)&~0xFFF;
117 head->Magic = MAGIC_FREE;
118 tHeapFoot *foot = Heap_ThisFoot(head);
120 foot->Magic = MAGIC_FOOT;
122 return Heap_Merge(head); // Merge with previous block
126 * \brief Merges two adjacent heap blocks
128 void *Heap_Merge(tHeapHead *Head)
130 //Log("Heap_Merge: (Head=%p)", Head);
131 tHeapFoot *thisFoot = Heap_ThisFoot(Head);
133 ASSERT( Heap_NextHead(Head) <= gHeapEnd );
136 tHeapFoot *foot = Heap_PrevFoot(Head);
137 if( Head > gHeapStart && foot->Head->Magic == MAGIC_FREE)
139 foot->Head->Size += Head->Size; // Increase size
140 thisFoot->Head = foot->Head; // Change backlink
141 Head->Magic = 0; // Clear old head
143 Head = foot->Head; // Save new head address
144 foot->Head = NULL; // Clear central footer
148 // Merge Right (Upwards)
149 tHeapHead *nexthead = Heap_NextHead(Head);
150 if(nexthead < gHeapEnd && nexthead->Magic == MAGIC_FREE)
152 Head->Size += nexthead->Size;
153 foot = Heap_ThisFoot(Head);
154 foot->Head = Head; // Update Backlink
155 thisFoot->Head = NULL; // Clear old footer
157 nexthead->Size = 0; // Clear old header
161 // Return new address
165 static const size_t Heap_int_GetBlockSize(size_t AllocSize)
169 Bytes = AllocSize + sizeof(tHeapHead) + sizeof(tHeapFoot);
170 Bytes = 1UUL << LOG2(Bytes);
172 Bytes = (AllocSize + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
178 * \param File Allocating source file
179 * \param Line Source line
180 * \param __Bytes Size of region to allocate
182 void *Heap_Allocate(const char *File, int Line, size_t __Bytes)
185 tHeapFoot *foot, *newfoot;
186 tHeapHead *best = NULL;
187 Uint bestSize = UINT_MAX; // Speed hack
191 return NULL; // TODO: Return a known un-mapped range.
195 #if VALIDATE_ON_ALLOC
200 Bytes = Heap_int_GetBlockSize(__Bytes);
203 Mutex_Acquire(&glHeap);
206 for( tHeapHead *head = gHeapStart; head < gHeapEnd; head = Heap_NextHead(head) )
210 if( head->Size != 1UUL << LOG2(head->Size) ) {
212 if( head->Size & (MIN_SIZE-1) ) {
214 Mutex_Release(&glHeap); // Release spinlock
216 Log_Warning("Heap", "Size of heap address %p is invalid"
217 " - not aligned (0x%x) [at paddr 0x%x]",
218 head, head->Size, MM_GetPhysAddr(&head->Size));
219 Heap_ValidateDump(VERBOSE_DUMP);
223 if( head->Size < MIN_SIZE ) {
224 Mutex_Release(&glHeap);
226 Log_Warning("Heap", "Size of heap address %p is invalid"
227 " - Too small (0x%x) [at paddr 0x%x]",
228 head, head->Size, MM_GetPhysAddr(&head->Size));
229 Heap_ValidateDump(VERBOSE_DUMP);
233 if( head->Size > (2<<30) ) {
234 Mutex_Release(&glHeap);
236 Log_Warning("Heap", "Size of heap address %p is invalid"
237 " - Over 2GiB (0x%x) [at paddr 0x%x]",
238 head, head->Size, MM_GetPhysAddr(&head->Size));
239 Heap_ValidateDump(VERBOSE_DUMP);
244 // Check if allocated
245 if(head->Magic == MAGIC_USED) continue;
247 if(head->Magic != MAGIC_FREE) {
248 Mutex_Release(&glHeap); // Release spinlock
250 Log_Warning("Heap", "Magic of heap address %p is invalid (%p = 0x%x)",
251 head, &head->Magic, head->Magic);
252 Heap_ValidateDump(VERBOSE_DUMP);
258 if(head->Size < Bytes) continue;
261 if(head->Size == Bytes) {
262 head->Magic = MAGIC_USED;
265 head->ValidSize = __Bytes;
266 head->AllocateTime = now();
267 Mutex_Release(&glHeap); // Release spinlock
269 Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
270 head->Data, head->Size, File, Line);
278 bestSize = head->Size;
281 // or check if the block is the best size
282 if(bestSize > head->Size) {
284 bestSize = head->Size;
289 // If no block large enough is found, create one
292 best = Heap_Extend( Bytes );
295 Warning("OOM when allocating 0x%x bytes", Bytes);
296 Mutex_Release(&glHeap); // Release spinlock
300 if(best->Size == Bytes) {
301 best->Magic = MAGIC_USED; // Mark block as used
304 best->ValidSize = __Bytes;
305 best->AllocateTime = now();
306 Mutex_Release(&glHeap); // Release spinlock
308 Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
309 best->Data, best->Size, File, Line);
316 // - Save size for new block
317 size_t newsize = best->Size - Bytes;
318 // - Allocate beginning of old block
319 best->Size = Bytes; // Update size in old header
320 best->ValidSize = __Bytes;
321 best->Magic = MAGIC_USED; // Mark block as used
324 best->AllocateTime = now();
325 // - Create a new foot on old block
326 newfoot = Heap_ThisFoot(best);
327 newfoot->Head = best; // Create new footer
328 newfoot->Magic = MAGIC_FOOT;
329 // - Create a new header after resized old
330 newhead = Heap_NextHead(best);
331 newhead->Size = newsize;
332 newhead->Magic = MAGIC_FREE;
333 newhead->ValidSize = 0;
334 newhead->File = NULL;
337 foot = Heap_ThisFoot(newhead);
338 foot->Head = newhead;
340 Mutex_Release(&glHeap); // Release spinlock
342 Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
343 best->Data, best->Size, File, Line);
349 * \brief Free an allocated memory block
351 void Heap_Deallocate(const char *File, int Line, void *Ptr)
353 // INVLPTR is returned from Heap_Allocate when the allocation
355 if( Ptr == INVLPTR ) return;
356 // free(NULL) is a no-op
357 if( Ptr == NULL ) return;
360 if( (tVAddr)Ptr % sizeof(void*) != 0 ) {
361 Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr);
366 if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd)
368 Log_Warning("Heap", "free - Passed a non-heap address by %p (%p < %p < %p)",
369 __builtin_return_address(0), gHeapStart, Ptr, gHeapEnd);
373 // Check memory block - Header
374 tHeapHead *head = (tHeapHead*)Ptr - 1;
375 if(head->Magic == MAGIC_FREE) {
376 Log_Warning("Heap", "free - Passed a freed block (%p) by %s:%i (was freed by %s:%i)",
378 head->File, head->Line);
379 Proc_PrintBacktrace();
382 if(head->Magic != MAGIC_USED) {
383 Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic);
384 Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
388 // Check memory block - Footer
389 tHeapFoot *foot = Heap_ThisFoot(head);
390 if(foot->Head != head) {
391 Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head);
392 Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
395 if(foot->Magic != MAGIC_FOOT) {
396 Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)",
397 head, &foot->Magic, foot->Magic);
398 Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
403 Log_Debug("Heap", "free: %p freed by %s:%i (%i old)",
404 Ptr, File, Line, now()-head->AllocateTime);
408 Mutex_Acquire( &glHeap );
410 Heap_int_WatchBlock(head, false);
413 head->Magic = MAGIC_FREE;
421 Mutex_Release( &glHeap );
425 * \brief Increase/Decrease the size of an allocation
426 * \param File Calling File
427 * \param Line Calling Line
428 * \param __ptr Old memory
429 * \param __size New Size
431 void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size)
433 tHeapHead *head = (tHeapHead*)__ptr - 1;
436 Uint newSize = Heap_int_GetBlockSize(__size);
438 // Check for reallocating NULL
440 return Heap_Allocate(File, Line, __size);
442 if( !Heap_IsHeapAddr(__ptr)) {
443 Log_Error("Heap", "%s:%i passed non-heap address %p when reallocating to %zi",
444 File, Line, __ptr, __size);
448 // Check if resize is needed
449 // TODO: Reduce size of block if needed
450 if(newSize <= head->Size) {
452 Log_Debug("Heap", "realloc maintain %p (0x%x >= 0x%x), returning to %s:%i",
453 head->Data, head->Size, newSize, File, Line);
458 #if VALIDATE_ON_ALLOC
462 Heap_int_WatchBlock(head, false);
464 // Check if next block is free
465 nextHead = Heap_NextHead(head);
467 // Extend into next block
468 if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
471 Log_Debug("Heap", "realloc expand %p (0x%x to 0x%x), returning to %s:%i",
472 head->Data, head->Size, newSize, File, Line);
474 Uint size = nextHead->Size + head->Size;
475 foot = Heap_ThisFoot(nextHead);
477 if(size == newSize) {
478 head->Size = newSize;
479 head->ValidSize = __size;
487 // Create a new heap block
488 // - Update old with new information
489 head->Size = newSize;
490 head->File = File; // Kinda counts as a new allocation
492 head->ValidSize = __size;
493 // - Create new footer
494 foot = Heap_ThisFoot(head);
496 foot->Magic = MAGIC_FOOT;
497 // - Create new header
498 nextHead = Heap_NextHead(head);
499 nextHead->Size = size - newSize;
500 nextHead->Magic = MAGIC_FREE;
501 // - Update old footer
502 Heap_ThisFoot(nextHead)->Head = nextHead;
507 foot = Heap_PrevFoot(head);
508 nextHead = foot->Head;
509 if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
511 Uint size = nextHead->Size + head->Size;
512 // Inexact fit, split things up
515 // TODO: Handle splitting of downwards blocks
516 Warning("[Heap ] TODO: Space efficient realloc when new size is smaller");
520 Log_Debug("Heap", "realloc expand down %p (0x%x to 0x%x), returning to %s:%i",
521 head->Data, head->Size, newSize, File, Line);
526 // Set 1st (new/lower) header
527 nextHead->Magic = MAGIC_USED;
528 nextHead->Size = newSize;
529 nextHead->File = File;
530 nextHead->Line = Line;
531 nextHead->ValidSize = __size;
532 // Get 2nd (old) footer
533 foot = Heap_ThisFoot(nextHead);
534 foot->Head = nextHead;
535 // Save old data size
536 oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead);
541 memmove(nextHead->Data, __ptr, oldDataSize);
543 return nextHead->Data;
547 nextHead = Heap_Allocate( File, Line, __size );
548 if(!nextHead) return NULL;
550 nextHead->File = File;
551 nextHead->Line = Line;
552 nextHead->ValidSize = __size;
554 ASSERTC(head->Size, <, nextHead->Size);
555 ASSERTC(__ptr, ==, head->Data);
559 head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead)
565 return nextHead->Data;
569 * \fn void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
570 * \brief Allocate and Zero a buffer in memory
571 * \param File Allocating file
572 * \param Line Line of allocation
573 * \param Bytes Size of the allocation
575 void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
577 void *ret = Heap_Allocate(File, Line, Bytes);
578 if(ret == NULL) return NULL;
580 memset( ret, 0, Bytes );
586 * \fn int Heap_IsHeapAddr(void *Ptr)
587 * \brief Checks if an address is a heap pointer
589 int Heap_IsHeapAddr(void *Ptr)
592 if((Uint)Ptr < (Uint)gHeapStart) return 0;
593 if((Uint)Ptr > (Uint)gHeapEnd) return 0;
594 if((Uint)Ptr & (sizeof(Uint)-1)) return 0;
596 head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
597 if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE)
603 int Heap_int_ApplyWatchpont(void *Word, bool enabled)
606 static void *active_wps[4];
608 for( dr = 2; dr < 4; dr ++ )
610 if( (enabled && active_wps[dr] == NULL) || active_wps[dr] == Word)
618 active_wps[dr] = Word;
621 //case 0: ASM("mov %0, %%dr0" : : "r" (Word)); break;
622 //case 1: ASM("mov %0, %%dr1" : : "r" (Word)); break;
623 case 2: ASM("mov %0, %%dr2" : : "r" (Word)); break;
624 case 3: ASM("mov %0, %%dr3" : : "r" (Word)); break;
625 default: ASSERTC(dr,<,4); return 1;
630 active_wps[dr] = NULL;
633 ASM("MOV %%dr7, %0" : "=r" (dr7flag));
634 dr7flag &= ~(0x2 << (dr*2));
635 dr7flag &= ~(0xF000 << (dr*4));
637 dr7flag |= 0x2 << (dr*2);
638 dr7flag |= 0xD000 << (dr*4); // 4 bytes, write
639 Debug("Heap_int_ApplyWatchpont: Watchpoint #%i %p ENABLED", dr, Word);
642 Debug("Heap_int_ApplyWatchpont: Watchpoint #%i %p disabled", dr, Word);
644 ASM("MOV %0, %%dr7" : : "r" (dr7flag));
651 void Heap_int_WatchBlock(tHeapHead *Head, bool Enabled)
654 rv = Heap_int_ApplyWatchpont( &Head->Size, Enabled );
655 //rv = Heap_int_ApplyWatchpont( &Head->Magic, Enabled );
656 rv = rv + Heap_int_ApplyWatchpont( &Heap_ThisFoot(Head)->Head, Enabled );
658 Warning("Can't apply watch on %p", Head);
662 int Heap_WatchBlock(void *Ptr)
664 //Heap_int_ApplyWatchpont();
666 if((Uint)Ptr < (Uint)gHeapStart) return 0;
667 if((Uint)Ptr > (Uint)gHeapEnd) return 0;
668 if((Uint)Ptr & (sizeof(Uint)-1)) return 0;
670 head = (tHeapHead*)Ptr - 1;
672 Heap_int_WatchBlock( head, true );
679 void Heap_Validate(void)
681 // Call dump non-verbosely.
682 // - If a heap error is detected, it'll print
683 Heap_ValidateDump(0);
688 Heap_ValidateDump(1);
691 void Heap_ValidateDump(int bVerbose)
693 tHeapHead *head, *badHead;
694 tHeapFoot *foot = NULL;
695 static int in_heap_dump;
697 if( in_heap_dump ) return;
702 while( (Uint)head < (Uint)gHeapEnd )
704 foot = Heap_ThisFoot(head);
708 Log_Log("Heap", "%p (%P): 0x%08x (%i) %4C",
709 head, MM_GetPhysAddr(head), head->Size, head->ValidSize, &head->Magic);
710 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
712 Log_Log("Heap", "%sowned by %s:%i",
713 (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
717 // Sanity Check Header
718 if(head->Size == 0) {
719 Log_Warning("Heap", "HALTED - Size is zero");
722 if(head->Size & (MIN_SIZE-1)) {
723 Log_Warning("Heap", "HALTED - Size is malaligned");
726 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
727 Log_Warning("Heap", "HALTED - Head Magic is Bad");
732 if(foot->Magic != MAGIC_FOOT) {
733 Log_Warning("Heap", "HALTED - Foot Magic is Bad");
736 if(head != foot->Head) {
737 Log_Warning("Heap", "HALTED - Footer backlink is invalid");
746 // All OK? Go to next
747 head = foot->NextHead;
750 // If the heap is valid, ok!
751 if( (tVAddr)head == (tVAddr)gHeapEnd ) {
756 // Check for a bad return
757 if( (tVAddr)head >= (tVAddr)gHeapEnd ) {
762 // If not verbose, we need to dump the failing block
765 Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
766 head, MM_GetPhysAddr(head), head->Size, head->ValidSize, &head->Magic);
768 Log_Log("Heap", "Foot %p = {Head:%p,Magic:%4C}", foot, foot->Head, &foot->Magic);
770 Log_Log("Heap", "%sowned by %s:%i",
771 (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
779 foot = Heap_PrevFoot(gHeapEnd);
780 Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot);
782 while( (tVAddr)head >= (tVAddr)badHead )
784 Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
785 head, MM_GetPhysAddr(head), head->Size, head->ValidSize, &head->Magic);
786 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
788 Log_Log("Heap", "%sowned by %s:%i",
789 (head->Magic!=MAGIC_USED?"was ":""),
790 head->File, head->Line);
793 // Sanity Check Header
794 if(head->Size == 0) {
795 Log_Warning("Heap", "HALTED - Size is zero");
798 if(head->Size & (MIN_SIZE-1)) {
799 Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1));
802 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
803 Log_Warning("Heap", "HALTED - Head Magic is Bad");
808 if(foot->Magic != MAGIC_FOOT) {
809 Log_Warning("Heap", "HALTED - Foot Magic is Bad");
812 if(head != foot->Head) {
813 Log_Warning("Heap", "HALTED - Footer backlink is invalid");
817 if(head == badHead) break;
819 foot = Heap_PrevFoot(head);
821 Log_Debug("Heap", "head=%p", head);
824 Panic("Heap_Dump - Heap is corrupted, kernel panic! (%p)", badHead);
827 void Heap_Stats(void)
833 int maxAlloc=0, minAlloc=-1;
834 int avgAlloc, frag, overhead;
836 for( tHeapHead *head = gHeapStart; head < gHeapEnd; head = Heap_NextHead(head) )
839 totalBytes += head->Size;
840 if( head->Magic == MAGIC_FREE )
843 freeBytes += head->Size;
845 else if( head->Magic == MAGIC_USED) {
846 if(maxAlloc < head->Size) maxAlloc = head->Size;
847 if(minAlloc == -1 || minAlloc > head->Size)
848 minAlloc = head->Size;
851 Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head);
855 // Print the block info?
857 if( head->Magic == MAGIC_FREE )
858 Log_Debug("Heap", "%p (%P) - 0x%x free",
859 head->Data, MM_GetPhysAddr(&head->Data), head->Size);
861 Log_Debug("Heap", "%p (%P) - 0x%x (%i) Owned by %s:%i (%lli ms old)",
862 head->Data, MM_GetPhysAddr(&head->Data), head->Size,
863 head->ValidSize, head->File, head->Line,
864 now() - head->AllocateTime
869 Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes);
870 Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes);
872 frag = (nFree-1)*10000/nBlocks;
875 Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100);
879 avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree);
881 overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc;
884 Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%",
885 avgAlloc, overhead/100, overhead%100
887 Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes",
890 // Scan and get distribution
897 } sizeCounts[nBlocks];
900 memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0]));
902 for(tHeapHead *head = gHeapStart; head < gHeapEnd; head = (void*)( (tVAddr)head + head->Size ) )
904 for( i = 0; i < nBlocks; i ++ ) {
905 if( sizeCounts[i].Size == 0 )
907 if( sizeCounts[i].Size == head->Size )
910 // Should never reach this part (in a non-concurrent case)
911 if( i == nBlocks ) continue;
912 sizeCounts[i].Size = head->Size;
913 sizeCounts[i].Count ++;
915 Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head,
916 head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i
918 Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i,
919 sizeCounts[i].Size, sizeCounts[i].Count);
923 for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ )
925 Log("Heap_Stats: 0x%x - %i blocks",
926 sizeCounts[i].Size, sizeCounts[i].Count
934 EXPORT(Heap_Allocate);
935 EXPORT(Heap_AllocateZero);
936 EXPORT(Heap_Reallocate);
937 EXPORT(Heap_Deallocate);
938 EXPORT(Heap_IsHeapAddr);