+
+ // If the heap is valid, ok!
+ if( (tVAddr)head == (tVAddr)gHeapEnd )
+ return ;
+
+ // Check for a bad return
+ if( (tVAddr)head >= (tVAddr)gHeapEnd )
+ return ;
+
+ #if !VERBOSE_DUMP
+ Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
+ head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
+ if(foot)
+ Log_Log("Heap", "Foot %p = {Head:%p,Magic:%4C}", foot, foot->Head, &foot->Magic);
+ if(head->File) {
+ Log_Log("Heap", "%sowned by %s:%i",
+ (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
+ }
+ Log_Log("Heap", "");
+ #endif
+
+
+ badHead = head;
+
+ // Work backwards
+ foot = (void*)( (tVAddr)gHeapEnd - sizeof(tHeapFoot) );
+ Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot);
+ head = foot->Head;
+ while( (tVAddr)head >= (tVAddr)badHead )
+ {
+ Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
+ head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
+ Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
+ if(head->File)
+ Log_Log("Heap", "%sowned by %s:%i",
+ (head->Magic!=MAGIC_USED?"was ":""),
+ head->File, head->Line);
+ Log_Log("Heap", "");
+
+ // Sanity Check Header
+ if(head->Size == 0) {
+ Log_Warning("Heap", "HALTED - Size is zero");
+ break;
+ }
+ if(head->Size & (MIN_SIZE-1)) {
+ Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1));
+ break ;
+ }
+ if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
+ Log_Warning("Heap", "HALTED - Head Magic is Bad");
+ break;
+ }
+
+ // Check footer
+ if(foot->Magic != MAGIC_FOOT) {
+ Log_Warning("Heap", "HALTED - Foot Magic is Bad");
+ break;
+ }
+ if(head != foot->Head) {
+ Log_Warning("Heap", "HALTED - Footer backlink is invalid");
+ break;
+ }
+
+ if(head == badHead) break;
+
+ foot = (void*)( (tVAddr)head - sizeof(tHeapFoot) );
+ head = foot->Head;
+ Log_Debug("Heap", "head=%p", head);
+ }
+
+ Panic("Heap_Dump - Heap is corrupted, kernel panic!");
+}
+#endif
+
+#if 1
+void Heap_Stats(void)
+{
+ tHeapHead *head;
+ int nBlocks = 0;
+ int nFree = 0;
+ int totalBytes = 0;
+ int freeBytes = 0;
+ int maxAlloc=0, minAlloc=-1;
+ int avgAlloc, frag, overhead;
+
+ for(head = gHeapStart;
+ (Uint)head < (Uint)gHeapEnd;
+ head = (void*)( (Uint)head + head->Size )
+ )
+ {
+ nBlocks ++;
+ totalBytes += head->Size;
+ if( head->Magic == MAGIC_FREE )
+ {
+ nFree ++;
+ freeBytes += head->Size;
+ }
+ else if( head->Magic == MAGIC_USED) {
+ if(maxAlloc < head->Size) maxAlloc = head->Size;
+ if(minAlloc == -1 || minAlloc > head->Size)
+ minAlloc = head->Size;
+ }
+ else {
+ Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head);
+ break;
+ }
+
+ // Print the block info?
+ #if 1
+ if( head->Magic == MAGIC_FREE )
+ Log_Debug("Heap", "%p (%P) - 0x%x free",
+ head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size);
+ else
+ Log_Debug("Heap", "%p (%P) - 0x%x (%i) Owned by %s:%i (%lli ms old)",
+ head->Data, MM_GetPhysAddr((tVAddr)&head->Data), head->Size, head->ValidSize, head->File, head->Line,
+ now() - head->AllocateTime
+ );
+ #endif
+ }
+
+ Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes);
+ Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes);
+ if(nBlocks != 0)
+ frag = (nFree-1)*10000/nBlocks;
+ else
+ frag = 0;
+ Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100);
+ if(nBlocks <= nFree)
+ avgAlloc = 0;
+ else
+ avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree);
+ if(avgAlloc != 0)
+ overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc;
+ else
+ overhead = 0;
+ Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%",
+ avgAlloc, overhead/100, overhead%100
+ );
+ Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes",
+ minAlloc, maxAlloc);
+
+ // Scan and get distribution
+ #if 1
+ if(nBlocks > 0)
+ {
+ struct {
+ Uint Size;
+ Uint Count;
+ } sizeCounts[nBlocks];
+ int i;
+
+ memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0]));
+
+ for(head = gHeapStart;
+ (Uint)head < (Uint)gHeapEnd;
+ head = (void*)( (Uint)head + head->Size )
+ )
+ {
+ for( i = 0; i < nBlocks; i ++ ) {
+ if( sizeCounts[i].Size == 0 )
+ break;
+ if( sizeCounts[i].Size == head->Size )
+ break;
+ }
+ // Should never reach this part (in a non-concurrent case)
+ if( i == nBlocks ) continue;
+ sizeCounts[i].Size = head->Size;
+ sizeCounts[i].Count ++;
+ #if 1
+ //Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head,
+ // head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i
+ // );
+ //Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i,
+ // sizeCounts[i].Size, sizeCounts[i].Count);
+ #endif
+ }
+
+ for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ )
+ {
+ Log("Heap_Stats: 0x%x - %i blocks",
+ sizeCounts[i].Size, sizeCounts[i].Count
+ );
+ }
+ }
+ #endif