Kernel/heap - Fixed memory leak, clean up pointer arithmatic
[tpg/acess2.git] / KernelLand / Kernel / heap.c
1 /*
2  * Acess2 Kernel
3  * - By John Hodge (thePowersGang)
4  *
5  * heap.c
6  * - Dynamic memory allocation
7  */
8 #include <acess.h>
9 #include <mm_virt.h>
10 #include <heap_int.h>
11 #include <limits.h>
12
13 #define WARNINGS        1       // Warn and dump on heap errors
14 #define DEBUG_TRACE     0       // Enable tracing of allocations
15 #define VERBOSE_DUMP    0       // Set to 1 to enable a verbose dump when heap errors are encountered
16
17 // === CONSTANTS ===
18 #define HEAP_INIT_SIZE  0x8000  // 32 KiB
19 #define MIN_SIZE        (sizeof(void*))*8       // 8 Machine Words
20 #define POW2_SIZES      0
21 #define COMPACT_HEAP    0       // Use 4 byte header?
22 #define FIRST_FIT       0
23
24 //#define       MAGIC_FOOT      0x2ACE5505
25 //#define       MAGIC_FREE      0xACE55000
26 //#define       MAGIC_USED      0x862B0505      // MAGIC_FOOT ^ MAGIC_FREE
27 #define MAGIC_FOOT      0x544F4F46      // 'FOOT'
28 #define MAGIC_FREE      0x45455246      // 'FREE'
29 #define MAGIC_USED      0x44455355      // 'USED'
30
31 // === PROTOTYPES ===
32 void    Heap_Install(void);
33 void    *Heap_Extend(size_t Bytes);
34 void    *Heap_Merge(tHeapHead *Head);
35 //void  *Heap_Allocate(const char *File, int Line, size_t Bytes);
36 //void  *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
37 //void  *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
38 //void  Heap_Deallocate(const char *File, int Line, void *Ptr);
39 void    Heap_Dump(int bVerbose);
40 void    Heap_Stats(void);
41
42 // === GLOBALS ===
43 tMutex  glHeap;
44 tHeapHead       *gHeapStart;
45 tHeapHead       *gHeapEnd;
46
47 // === CODE ===
48 void Heap_Install(void)
49 {
50         gHeapStart = (void*)MM_KHEAP_BASE;
51         gHeapEnd   = gHeapStart;
52         Heap_Extend(HEAP_INIT_SIZE);
53 }
54
55 static inline tHeapHead *Heap_NextHead(tHeapHead *Head) {
56         return (void*)( (char*)Head + Head->Size );
57 }
58 static inline tHeapFoot *Heap_ThisFoot(tHeapHead *Head) {
59         return (void*)( (char*)Head + Head->Size - sizeof(tHeapFoot) );
60 }
61 static inline tHeapFoot *Heap_PrevFoot(tHeapHead *Head) {
62         //ASSERT(Head != gHeapStart);
63         return (void*)( (char*)Head - sizeof(tHeapFoot) );
64 }
65
66 /**
67  * \brief Extend the size of the heap
68  */
69 void *Heap_Extend(size_t Bytes)
70 {
71
72         Debug("Heap_Extend(0x%x)", Bytes);
73         
74         // Bounds Check
75         if( gHeapEnd == (tHeapHead*)MM_KHEAP_MAX ) {
76                 Log_Error("Heap", "Heap limit reached (%p)", (void*)MM_KHEAP_MAX);
77                 return NULL;
78         }
79         
80         if( Bytes == 0 ) {
81                 Log_Warning("Heap", "Heap_Extend called with Bytes=%i", Bytes);
82                 return NULL;
83         }
84         
85         const Uint      pages = (Bytes + 0xFFF) >> 12;
86         tHeapHead       *new_end = (void*)( (tVAddr)gHeapEnd + (pages << 12) );
87         // Bounds Check
88         if( new_end > (tHeapHead*)MM_KHEAP_MAX )
89         {
90                 Log_Error("Heap", "Heap limit exceeded (%p)", (void*)new_end);
91                 // TODO: Clip allocation to avaliable space, and have caller check returned block
92                 return NULL;
93         }
94         
95         // Heap expands in pages
96         for( Uint i = 0; i < pages; i ++ )
97         {
98                 if( !MM_Allocate( (tVAddr)gHeapEnd+(i<<12) ) )
99                 {
100                         Warning("OOM - Heap_Extend (%i bytes)");
101                         Heap_Dump(1);
102                         return NULL;
103                 }
104         }
105         
106         // Increase heap end
107         tHeapHead       *head = gHeapEnd;
108         gHeapEnd = new_end;
109         
110         // Create Block
111         head->Size = (Bytes+0xFFF)&~0xFFF;
112         head->Magic = MAGIC_FREE;
113         tHeapFoot *foot = Heap_ThisFoot(head);
114         foot->Head = head;
115         foot->Magic = MAGIC_FOOT;
116         
117         return Heap_Merge(head);        // Merge with previous block
118 }
119
120 /**
121  * \brief Merges two ajacent heap blocks
122  */
123 void *Heap_Merge(tHeapHead *Head)
124 {
125         tHeapFoot       *foot;
126         tHeapFoot       *thisFoot;
127         tHeapHead       *head;
128         
129         //Log("Heap_Merge: (Head=%p)", Head);
130         
131         thisFoot = Heap_ThisFoot(Head);
132         if( (void*)thisFoot > (void*)gHeapEnd )
133                 return NULL;
134         
135         // Merge Left (Down)
136         foot = Heap_PrevFoot(Head);
137         if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart)
138         && 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
142                 Head->Size = 0;
143                 Head = foot->Head;      // Save new head address
144                 foot->Head = NULL;      // Clear central footer
145                 foot->Magic = 0;
146         }
147         
148         // Merge Right (Upwards)
149         head = Heap_NextHead(Head);
150         if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE)
151         {
152                 Head->Size += head->Size;
153                 foot = Heap_ThisFoot(Head);
154                 foot->Head = Head;      // Update Backlink
155                 thisFoot->Head = NULL;  // Clear old footer
156                 thisFoot->Magic = 0;
157                 head->Size = 0;         // Clear old header
158                 head->Magic = 0;
159         }
160         
161         // Return new address
162         return Head;
163 }
164
165 /**
166  * \param File  Allocating source file
167  * \param Line  Source line
168  * \param __Bytes       Size of region to allocate
169  */
170 void *Heap_Allocate(const char *File, int Line, size_t __Bytes)
171 {
172         tHeapHead       *newhead;
173         tHeapFoot       *foot, *newfoot;
174         tHeapHead       *best = NULL;
175         Uint    bestSize = UINT_MAX;    // Speed hack
176         size_t  Bytes;
177
178         if( __Bytes == 0 ) {
179                 return NULL;    // TODO: Return a known un-mapped range.
180 //              return INVLPTR;
181         }
182         
183         // Get required size
184         #if POW2_SIZES
185         Bytes = __Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot);
186         Bytes = 1UUL << LOG2(__Bytes);
187         #else
188         Bytes = (__Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
189         #endif
190         
191         // Lock Heap
192         Mutex_Acquire(&glHeap);
193         
194         // Traverse Heap
195         for( tHeapHead *head = gHeapStart; head < gHeapEnd; head = Heap_NextHead(head) )
196         {
197                 // Alignment Check
198                 #if POW2_SIZES
199                 if( head->Size != 1UUL << LOG2(head->Size) ) {
200                 #else
201                 if( head->Size & (MIN_SIZE-1) ) {
202                 #endif
203                         Mutex_Release(&glHeap); // Release spinlock
204                         #if WARNINGS
205                         Log_Warning("Heap", "Size of heap address %p is invalid"
206                                 " - not aligned (0x%x) [at paddr 0x%x]",
207                                 head, head->Size, MM_GetPhysAddr(&head->Size));
208                         Heap_Dump(VERBOSE_DUMP);
209                         #endif
210                         return NULL;
211                 }
212                 if( head->Size < MIN_SIZE ) {
213                         Mutex_Release(&glHeap);
214                         Log_Warning("Heap", "Size of heap address %p is invalid"
215                                 " - Too small (0x%x) [at paddr 0x%x]",
216                                 head, head->Size, MM_GetPhysAddr(&head->Size));
217                         Heap_Dump(VERBOSE_DUMP);
218                         return NULL;
219                 }
220                 if( head->Size > (2<<30) ) {
221                         Mutex_Release(&glHeap);
222                         Log_Warning("Heap", "Size of heap address %p is invalid"
223                                 " - Over 2GiB (0x%x) [at paddr 0x%x]",
224                                 head, head->Size, MM_GetPhysAddr(&head->Size));
225                         Heap_Dump(VERBOSE_DUMP);
226                         return NULL;
227                 }
228                 
229                 // Check if allocated
230                 if(head->Magic == MAGIC_USED)   continue;
231                 // Error check
232                 if(head->Magic != MAGIC_FREE)   {
233                         Mutex_Release(&glHeap); // Release spinlock
234                         #if WARNINGS
235                         Log_Warning("Heap", "Magic of heap address %p is invalid (%p = 0x%x)",
236                                 head, &head->Magic, head->Magic);
237                         Heap_Dump(VERBOSE_DUMP);
238                         #endif
239                         return NULL;
240                 }
241                 
242                 // Size check
243                 if(head->Size < Bytes)  continue;
244                 
245                 // Perfect fit
246                 if(head->Size == Bytes) {
247                         head->Magic = MAGIC_USED;
248                         head->File = File;
249                         head->Line = Line;
250                         head->ValidSize = __Bytes;
251                         head->AllocateTime = now();
252                         Mutex_Release(&glHeap); // Release spinlock
253                         #if DEBUG_TRACE
254                         Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
255                                 head->Data, head->Size, File, Line);
256                         #endif
257                         return head->Data;
258                 }
259                 
260                 // Break out of loop
261                 #if FIRST_FIT
262                 best = head;
263                 bestSize = head->Size;
264                 break;
265                 #else
266                 // or check if the block is the best size
267                 if(bestSize > head->Size) {
268                         best = head;
269                         bestSize = head->Size;
270                 }
271                 #endif
272         }
273         
274         // If no block large enough is found, create one
275         if(!best)
276         {
277                 best = Heap_Extend( Bytes );
278                 // Check for errors
279                 if(!best) {
280                         Warning("OOM when allocating 0x%x bytes", Bytes);
281                         Mutex_Release(&glHeap); // Release spinlock
282                         return NULL;
283                 }
284                 // Check size
285                 if(best->Size == Bytes) {
286                         best->Magic = MAGIC_USED;       // Mark block as used
287                         best->File = File;
288                         best->Line = Line;
289                         best->ValidSize = __Bytes;
290                         best->AllocateTime = now();
291                         Mutex_Release(&glHeap); // Release spinlock
292                         #if DEBUG_TRACE
293                         Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
294                                 best->Data, best->Size, File, Line);
295                         #endif
296                         return best->Data;
297                 }
298         }
299         
300         // Split Block
301         // - Save size for new block
302         size_t  newsize = best->Size - Bytes;
303         // - Allocate beginning of old block
304         best->Size = Bytes;             // Update size in old header
305         best->ValidSize = __Bytes;
306         best->Magic = MAGIC_USED;       // Mark block as used
307         best->File = File;
308         best->Line = Line;
309         best->AllocateTime = now();
310         // - Create a new foot on old block
311         newfoot = Heap_ThisFoot(best);
312         newfoot->Head = best;   // Create new footer
313         newfoot->Magic = MAGIC_FOOT;
314         // - Create a new header after resized old
315         newhead = Heap_NextHead(best);
316         newhead->Size = newsize;
317         newhead->Magic = MAGIC_FREE;
318         newhead->ValidSize = 0;
319         newhead->File = NULL;
320         newhead->Line = 0;
321         // - Update footer
322         foot = Heap_ThisFoot(newhead);
323         foot->Head = newhead;
324         
325         Mutex_Release(&glHeap); // Release spinlock
326         #if DEBUG_TRACE
327         Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
328                 best->Data, best->Size, File, Line);
329         #endif
330         return best->Data;
331 }
332
333 /**
334  * \brief Free an allocated memory block
335  */
336 void Heap_Deallocate(const char *File, int Line, void *Ptr)
337 {
338         // INVLPTR is returned from Heap_Allocate when the allocation
339         // size is zero.
340         if( Ptr == INVLPTR )    return;
341         
342         // Alignment Check
343         if( (tVAddr)Ptr % sizeof(void*) != 0 ) {
344                 Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr);
345                 return;
346         }
347         
348         // Sanity check
349         if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd)
350         {
351                 Log_Warning("Heap", "free - Passed a non-heap address by %p (%p < %p < %p)",
352                         __builtin_return_address(0), gHeapStart, Ptr, gHeapEnd);
353                 return;
354         }
355         
356         // Check memory block - Header
357         tHeapHead *head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
358         if(head->Magic == MAGIC_FREE) {
359                 Log_Warning("Heap", "free - Passed a freed block (%p) by %s:%i (was freed by %s:%i)",
360                         head, File, Line,
361                         head->File, head->Line);
362                 return;
363         }
364         if(head->Magic != MAGIC_USED) {
365                 Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic);
366                 Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
367                 return;
368         }
369         
370         // Check memory block - Footer
371         tHeapFoot *foot = Heap_ThisFoot(head);
372         if(foot->Head != head) {
373                 Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head);
374                 Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
375                 return;
376         }
377         if(foot->Magic != MAGIC_FOOT) {
378                 Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)",
379                         head, &foot->Magic, foot->Magic);
380                 Log_Notice("Heap", "Allocated by %s:%i", head->File, head->Line);
381                 return;
382         }
383         
384         #if DEBUG_TRACE
385         Log_Debug("Heap", "free: %p freed by %s:%i (%i old)",
386                 Ptr, File, Line, now()-head->AllocateTime);
387         #endif
388         
389         // Lock
390         Mutex_Acquire( &glHeap );
391         
392         // Mark as free
393         head->Magic = MAGIC_FREE;
394         head->File = File;
395         head->Line = Line;
396         head->ValidSize = 0;
397         // Merge blocks
398         Heap_Merge( head );
399         
400         // Release
401         Mutex_Release( &glHeap );
402 }
403
404 /**
405  * \brief Increase/Decrease the size of an allocation
406  * \param File  Calling File
407  * \param Line  Calling Line
408  * \param __ptr Old memory
409  * \param __size        New Size
410  */
411 void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size)
412 {
413         tHeapHead       *head = (void*)( (Uint)__ptr-sizeof(tHeapHead) );
414         tHeapHead       *nextHead;
415         tHeapFoot       *foot;
416         Uint    newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1);
417         
418         // Check for reallocating NULL
419         if(__ptr == NULL)       return Heap_Allocate(File, Line, __size);
420         
421         // Check if resize is needed
422         if(newSize <= head->Size)       return __ptr;
423         
424         // Check if next block is free
425         nextHead = Heap_NextHead(head);
426         
427         // Extend into next block
428         if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
429         {
430                 Uint    size = nextHead->Size + head->Size;
431                 foot = Heap_ThisFoot(nextHead);
432                 // Exact Fit
433                 if(size == newSize) {
434                         head->Size = newSize;
435                         head->ValidSize = __size;
436                         head->File = File;
437                         head->Line = Line;
438                         foot->Head = head;
439                         nextHead->Magic = 0;
440                         nextHead->Size = 0;
441                         return __ptr;
442                 }
443                 // Create a new heap block
444                 // - Update old with new information
445                 head->Size = newSize;
446                 head->File = File;      // Kinda counts as a new allocation
447                 head->Line = Line;
448                 head->ValidSize = __size;
449                 // - Create new footer
450                 foot = Heap_ThisFoot(head);
451                 foot->Head = head;
452                 foot->Magic = MAGIC_FOOT;
453                 // - Create new header
454                 nextHead = Heap_NextHead(head);
455                 nextHead->Size = size - newSize;
456                 nextHead->Magic = MAGIC_FREE;
457                 // - Update old footer
458                 foot->Head = nextHead;
459                 return __ptr;
460         }
461         
462         // Extend downwards?
463         foot = Heap_PrevFoot(head);
464         nextHead = foot->Head;
465         if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
466         {
467                 Uint    size = nextHead->Size + head->Size;
468                 // Inexact fit, split things up
469                 if(size > newSize)
470                 {
471                         // TODO: Handle splitting of downwards blocks
472                         Warning("[Heap   ] TODO: Space efficient realloc when new size is smaller");
473                 }
474                 
475                 // Exact fit
476                 Uint    oldDataSize;
477                 // Set 1st (new/lower) header
478                 nextHead->Magic = MAGIC_USED;
479                 nextHead->Size = newSize;
480                 nextHead->File = File;
481                 nextHead->Line = Line;
482                 nextHead->ValidSize = __size;
483                 // Get 2nd (old) footer
484                 foot = Heap_ThisFoot(nextHead);
485                 foot->Head = nextHead;
486                 // Save old data size
487                 oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead);
488                 // Clear old header
489                 head->Size = 0;
490                 head->Magic = 0;
491                 // Copy data
492                 memmove(nextHead->Data, __ptr, oldDataSize);
493                 // Return
494                 return nextHead->Data;
495         }
496         
497         // Well, darn
498         nextHead = Heap_Allocate( File, Line, __size );
499         if(!nextHead)   return NULL;
500         nextHead -= 1;
501         nextHead->File = File;
502         nextHead->Line = Line;
503         nextHead->ValidSize = __size;
504         
505         memcpy(
506                 nextHead->Data,
507                 __ptr,
508                 head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead)
509                 );
510         
511         free(__ptr);
512         
513         return nextHead->Data;
514 }
515
516 /**
517  * \fn void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
518  * \brief Allocate and Zero a buffer in memory
519  * \param File  Allocating file
520  * \param Line  Line of allocation
521  * \param Bytes Size of the allocation
522  */
523 void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
524 {
525         void    *ret = Heap_Allocate(File, Line, Bytes);
526         if(ret == NULL) return NULL;
527         
528         memset( ret, 0, Bytes );
529         
530         return ret;
531 }
532
533 /**
534  * \fn int Heap_IsHeapAddr(void *Ptr)
535  * \brief Checks if an address is a heap pointer
536  */
537 int Heap_IsHeapAddr(void *Ptr)
538 {
539         tHeapHead       *head;
540         if((Uint)Ptr < (Uint)gHeapStart)        return 0;
541         if((Uint)Ptr > (Uint)gHeapEnd)  return 0;
542         if((Uint)Ptr & (sizeof(Uint)-1))        return 0;
543         
544         head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
545         if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE)
546                 return 0;
547         
548         return 1;
549 }
550
551 /**
552  */
553 void Heap_Validate(void)
554 {
555         // Call dump non-verbosely.
556         // - If a heap error is detected, it'll print
557         Heap_Dump(0);
558 }
559
560 void Heap_Dump(int bVerbose)
561 {
562         tHeapHead       *head, *badHead;
563         tHeapFoot       *foot = NULL;
564         static int      in_heap_dump;
565         
566         if( in_heap_dump )      return;
567
568         in_heap_dump = 1;
569
570         head = gHeapStart;
571         while( (Uint)head < (Uint)gHeapEnd )
572         {               
573                 foot = Heap_ThisFoot(head);
574                 
575                 if( bVerbose )
576                 {
577                         Log_Log("Heap", "%p (%P): 0x%08x (%i) %4C",
578                                 head, MM_GetPhysAddr(head), head->Size, head->ValidSize, &head->Magic);
579                         Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
580                         if(head->File) {
581                                 Log_Log("Heap", "%sowned by %s:%i",
582                                         (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
583                         }
584                 }
585                 
586                 // Sanity Check Header
587                 if(head->Size == 0) {
588                         Log_Warning("Heap", "HALTED - Size is zero");
589                         break;
590                 }
591                 if(head->Size & (MIN_SIZE-1)) {
592                         Log_Warning("Heap", "HALTED - Size is malaligned");
593                         break;
594                 }
595                 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
596                         Log_Warning("Heap", "HALTED - Head Magic is Bad");
597                         break;
598                 }
599                 
600                 // Check footer
601                 if(foot->Magic != MAGIC_FOOT) {
602                         Log_Warning("Heap", "HALTED - Foot Magic is Bad");
603                         break;
604                 }
605                 if(head != foot->Head) {
606                         Log_Warning("Heap", "HALTED - Footer backlink is invalid");
607                         break;
608                 }
609                 
610                 if( bVerbose )
611                 {
612                         Log_Log("Heap", "");
613                 }
614                 
615                 // All OK? Go to next
616                 head = foot->NextHead;
617         }
618         
619         // If the heap is valid, ok!
620         if( (tVAddr)head == (tVAddr)gHeapEnd ) {
621                 in_heap_dump = 0;
622                 return ;
623         }
624         
625         // Check for a bad return
626         if( (tVAddr)head >= (tVAddr)gHeapEnd ) {
627                 in_heap_dump = 0;
628                 return ;
629         }
630
631         // If not verbose, we need to dump the failing block
632         if( !bVerbose )
633         {
634                 Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
635                         head, MM_GetPhysAddr(head), head->Size, head->ValidSize, &head->Magic);
636                 if(foot)
637                         Log_Log("Heap", "Foot %p = {Head:%p,Magic:%4C}", foot, foot->Head, &foot->Magic);
638                 if(head->File) {
639                         Log_Log("Heap", "%sowned by %s:%i",
640                                 (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
641                 }
642                 Log_Log("Heap", "");
643         }
644         
645         
646         badHead = head;
647         
648         // Work backwards
649         foot = Heap_PrevFoot(gHeapEnd);
650         Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot);
651         head = foot->Head;
652         while( (tVAddr)head >= (tVAddr)badHead )
653         {
654                 Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
655                         head, MM_GetPhysAddr(head), head->Size, head->ValidSize, &head->Magic);
656                 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
657                 if(head->File)
658                         Log_Log("Heap", "%sowned by %s:%i",
659                                 (head->Magic!=MAGIC_USED?"was ":""),
660                                 head->File, head->Line);
661                 Log_Log("Heap", "");
662                 
663                 // Sanity Check Header
664                 if(head->Size == 0) {
665                         Log_Warning("Heap", "HALTED - Size is zero");
666                         break;
667                 }
668                 if(head->Size & (MIN_SIZE-1)) {
669                         Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1));
670                         break ;
671                 }
672                 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
673                         Log_Warning("Heap", "HALTED - Head Magic is Bad");
674                         break;
675                 }
676                 
677                 // Check footer
678                 if(foot->Magic != MAGIC_FOOT) {
679                         Log_Warning("Heap", "HALTED - Foot Magic is Bad");
680                         break;
681                 }
682                 if(head != foot->Head) {
683                         Log_Warning("Heap", "HALTED - Footer backlink is invalid");
684                         break;
685                 }
686                 
687                 if(head == badHead)     break;
688                 
689                 foot = Heap_PrevFoot(head);
690                 head = foot->Head;
691                 Log_Debug("Heap", "head=%p", head);
692         }
693         
694         Panic("Heap_Dump - Heap is corrupted, kernel panic!");
695 }
696
697 void Heap_Stats(void)
698 {
699          int    nBlocks = 0;
700          int    nFree = 0;
701          int    totalBytes = 0;
702          int    freeBytes = 0;
703          int    maxAlloc=0, minAlloc=-1;
704          int    avgAlloc, frag, overhead;
705         
706         for( tHeapHead *head = gHeapStart; head < gHeapEnd; head = Heap_NextHead(head) )
707         {       
708                 nBlocks ++;
709                 totalBytes += head->Size;
710                 if( head->Magic == MAGIC_FREE )
711                 {
712                         nFree ++;
713                         freeBytes += head->Size;
714                 }
715                 else if( head->Magic == MAGIC_USED) {
716                         if(maxAlloc < head->Size)       maxAlloc = head->Size;
717                         if(minAlloc == -1 || minAlloc > head->Size)
718                                 minAlloc = head->Size;
719                 }
720                 else {
721                         Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head);
722                         break;
723                 }
724                 
725                 // Print the block info?
726                 #if 1
727                 if( head->Magic == MAGIC_FREE )
728                         Log_Debug("Heap", "%p (%P) - 0x%x free",
729                                 head->Data, MM_GetPhysAddr(&head->Data), head->Size);
730                 else
731                         Log_Debug("Heap", "%p (%P) - 0x%x (%i) Owned by %s:%i (%lli ms old)",
732                                 head->Data, MM_GetPhysAddr(&head->Data), head->Size,
733                                 head->ValidSize, head->File, head->Line,
734                                 now() - head->AllocateTime
735                                 );
736                 #endif
737         }
738
739         Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes);
740         Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes);
741         if(nBlocks != 0)
742                 frag = (nFree-1)*10000/nBlocks;
743         else
744                 frag = 0;
745         Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100);
746         if(nBlocks <= nFree)
747                 avgAlloc = 0;
748         else
749                 avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree);
750         if(avgAlloc != 0)
751                 overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc;
752         else
753                 overhead = 0;
754         Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%",
755                 avgAlloc, overhead/100, overhead%100
756                 );
757         Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes", 
758                 minAlloc, maxAlloc);
759         
760         // Scan and get distribution
761         #if 1
762         if(nBlocks > 0)
763         {
764                 struct {
765                         Uint    Size;
766                         Uint    Count;
767                 }       sizeCounts[nBlocks];
768                  int    i;
769                 
770                 memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0]));
771                 
772                 for(tHeapHead *head = gHeapStart; head < gHeapEnd; head = (void*)( (tVAddr)head + head->Size ) )
773                 {
774                         for( i = 0; i < nBlocks; i ++ ) {
775                                 if( sizeCounts[i].Size == 0 )
776                                         break;
777                                 if( sizeCounts[i].Size == head->Size )
778                                         break;
779                         }
780                         // Should never reach this part (in a non-concurrent case)
781                         if( i == nBlocks )      continue;
782                         sizeCounts[i].Size = head->Size;
783                         sizeCounts[i].Count ++;
784                         #if 0
785                         Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head,
786                                 head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i
787                                 );
788                         Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i,
789                                 sizeCounts[i].Size, sizeCounts[i].Count);
790                         #endif
791                 }
792                 
793                 for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ )
794                 {
795                         Log("Heap_Stats: 0x%x - %i blocks",
796                                 sizeCounts[i].Size, sizeCounts[i].Count
797                                 );
798                 }
799         }
800         #endif
801 }
802
803 // === EXPORTS ===
804 EXPORT(Heap_Allocate);
805 EXPORT(Heap_AllocateZero);
806 EXPORT(Heap_Reallocate);
807 EXPORT(Heap_Deallocate);
808 EXPORT(Heap_IsHeapAddr);

UCC git Repository :: git.ucc.asn.au