2 * AcessOS Microkernel Version
11 #define VERBOSE_DUMP 0
14 #define HEAP_INIT_SIZE 0x8000 // 32 KiB
15 #define MIN_SIZE (sizeof(void*))*8 // 8 Machine Words
17 #define COMPACT_HEAP 0 // Use 4 byte header?
20 //#define MAGIC_FOOT 0x2ACE5505
21 //#define MAGIC_FREE 0xACE55000
22 //#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE
23 #define MAGIC_FOOT 0x544F4F46 // 'FOOT'
24 #define MAGIC_FREE 0x45455246 // 'FREE'
25 #define MAGIC_USED 0x44455355 // 'USED'
28 void Heap_Install(void);
29 void *Heap_Extend(int Bytes);
30 void *Heap_Merge(tHeapHead *Head);
31 void *Heap_Allocate(const char *File, int Line, size_t Bytes);
32 void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
33 void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
34 void Heap_Deallocate(void *Ptr);
36 void Heap_Stats(void);
44 void Heap_Install(void)
46 gHeapStart = (void*)MM_KHEAP_BASE;
47 gHeapEnd = (void*)MM_KHEAP_BASE;
48 Heap_Extend(HEAP_INIT_SIZE);
52 * \fn void *Heap_Extend(int Bytes)
53 * \brief Extend the size of the heap
55 void *Heap_Extend(int Bytes)
58 tHeapHead *head = gHeapEnd;
62 if( (tVAddr)gHeapEnd == MM_KHEAP_MAX )
66 if( (tVAddr)gHeapEnd + ((Bytes+0xFFF)&~0xFFF) > MM_KHEAP_MAX ) {
67 Bytes = MM_KHEAP_MAX - (tVAddr)gHeapEnd;
71 // Heap expands in pages
72 for(i=0;i<(Bytes+0xFFF)>>12;i++)
73 MM_Allocate( (tVAddr)gHeapEnd+(i<<12) );
79 head->Size = (Bytes+0xFFF)&~0xFFF;
80 head->Magic = MAGIC_FREE;
81 foot = (void*)( (Uint)gHeapEnd - sizeof(tHeapFoot) );
83 foot->Magic = MAGIC_FOOT;
85 return Heap_Merge(head); // Merge with previous block
89 * \fn void *Heap_Merge(tHeapHead *Head)
90 * \brief Merges two ajacent heap blocks
92 void *Heap_Merge(tHeapHead *Head)
98 //Log("Heap_Merge: (Head=%p)", Head);
100 thisFoot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
101 if((Uint)thisFoot > (Uint)gHeapEnd) return NULL;
104 foot = (void*)( (Uint)Head - sizeof(tHeapFoot) );
105 if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart)
106 && foot->Head->Magic == MAGIC_FREE) {
107 foot->Head->Size += Head->Size; // Increase size
108 thisFoot->Head = foot->Head; // Change backlink
109 Head->Magic = 0; // Clear old head
111 Head = foot->Head; // Save new head address
112 foot->Head = NULL; // Clear central footer
116 // Merge Right (Upwards)
117 head = (void*)( (Uint)Head + Head->Size );
118 if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE)
120 Head->Size += head->Size;
121 foot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
122 foot->Head = Head; // Update Backlink
123 thisFoot->Head = NULL; // Clear old footer
125 head->Size = 0; // Clear old header
129 // Return new address
134 * \brief Allocate memory from the heap
135 * \param File Allocating source file
136 * \param Line Source line
137 * \param Bytes Size of region to allocate
139 void *Heap_Allocate(const char *File, int Line, size_t __Bytes)
141 tHeapHead *head, *newhead;
142 tHeapFoot *foot, *newfoot;
143 tHeapHead *best = NULL;
144 Uint bestSize = 0; // Speed hack
149 Bytes = __Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot);
150 Bytes = 1UUL << LOG2(__Bytes);
152 Bytes = (__Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
156 Mutex_Acquire(&glHeap);
159 for( head = gHeapStart;
160 (Uint)head < (Uint)gHeapEnd;
161 head = (void*)((Uint)head + head->Size)
166 if( head->Size != 1UUL << LOG2(head->Size) ) {
168 if( head->Size & (MIN_SIZE-1) ) {
170 Mutex_Release(&glHeap); // Release spinlock
172 Log_Warning("Heap", "Size of heap address %p is invalid not aligned (0x%x)", head, head->Size);
178 // Check if allocated
179 if(head->Magic == MAGIC_USED) continue;
181 if(head->Magic != MAGIC_FREE) {
182 Mutex_Release(&glHeap); // Release spinlock
184 Log_Warning("Heap", "Magic of heap address %p is invalid (0x%x)", head, head->Magic);
191 if(head->Size < Bytes) continue;
194 if(head->Size == Bytes) {
195 head->Magic = MAGIC_USED;
198 Mutex_Release(&glHeap); // Release spinlock
200 Log("[Heap ] Malloc'd %p (%i bytes), returning to %p", head->Data, head->Size, __builtin_return_address(0));
208 bestSize = head->Size;
211 // or check if the block is the best size
212 if(bestSize > head->Size) {
214 bestSize = head->Size;
219 // If no block large enough is found, create one
222 best = Heap_Extend( Bytes );
225 Mutex_Release(&glHeap); // Release spinlock
229 if(best->Size == Bytes) {
230 best->Magic = MAGIC_USED; // Mark block as used
233 Mutex_Release(&glHeap); // Release spinlock
235 Log("[Heap ] Malloc'd %p (%i bytes), returning to %p", best->Data, best->Size, __builtin_return_address(0));
242 newhead = (void*)( (Uint)best + Bytes );
243 newfoot = (void*)( (Uint)best + Bytes - sizeof(tHeapFoot) );
244 foot = (void*)( (Uint)best + best->Size - sizeof(tHeapFoot) );
246 newfoot->Head = best; // Create new footer
247 newfoot->Magic = MAGIC_FOOT;
248 newhead->Size = best->Size - Bytes; // Create new header
249 newhead->Magic = MAGIC_FREE;
250 foot->Head = newhead; // Update backlink in old footer
251 best->Size = Bytes; // Update size in old header
252 best->ValidSize = __Bytes;
253 best->Magic = MAGIC_USED; // Mark block as used
257 Mutex_Release(&glHeap); // Release spinlock
259 Log_Debug("Heap", "newhead(%p)->Size = 0x%x", newhead, newhead->Size);
260 Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
261 best->Data, best->Size, File, Line);
267 * \fn void Heap_Deallocate(void *Ptr)
268 * \brief Free an allocated memory block
270 void Heap_Deallocate(void *Ptr)
276 Log_Log("Heap", "free: Ptr = %p", Ptr);
277 Log_Log("Heap", "free: Returns to %p", __builtin_return_address(0));
281 if( (Uint)Ptr & (sizeof(Uint)-1) ) {
282 Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr);
287 if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd)
289 Log_Warning("Heap", "free - Passed a non-heap address (%p < %p < %p)\n",
290 gHeapStart, Ptr, gHeapEnd);
294 // Check memory block - Header
295 head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
296 if(head->Magic == MAGIC_FREE) {
297 Log_Warning("Heap", "free - Passed a freed block (%p) by %p", head, __builtin_return_address(0));
300 if(head->Magic != MAGIC_USED) {
301 Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic);
302 Log_Notice("Heap", "Allocated %s:%i", head->File, head->Line);
306 // Check memory block - Footer
307 foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
308 if(foot->Head != head) {
309 Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head);
310 Log_Notice("Heap", "Allocated %s:%i", head->File, head->Line);
313 if(foot->Magic != MAGIC_FOOT) {
314 Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)", head, &foot->Magic, foot->Magic);
315 Log_Notice("Heap", "Allocated %s:%i", head->File, head->Line);
320 Mutex_Acquire( &glHeap );
323 head->Magic = MAGIC_FREE;
331 Mutex_Release( &glHeap );
335 * \brief Increase/Decrease the size of an allocation
336 * \param File Calling File
337 * \param Line Calling Line
338 * \param __ptr Old memory
339 * \param __size New Size
341 void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size)
343 tHeapHead *head = (void*)( (Uint)__ptr-sizeof(tHeapHead) );
346 Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1);
348 // Check for reallocating NULL
349 if(__ptr == NULL) return Heap_Allocate(File, Line, __size);
351 // Check if resize is needed
352 if(newSize <= head->Size) return __ptr;
354 // Check if next block is free
355 nextHead = (void*)( (Uint)head + head->Size );
357 // Extend into next block
358 if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
360 Uint size = nextHead->Size + head->Size;
361 foot = (void*)( (Uint)nextHead + nextHead->Size - sizeof(tHeapFoot) );
363 if(size == newSize) {
364 head->Size = newSize;
365 head->ValidSize = __size;
373 // Create a new heap block
374 nextHead = (void*)( (Uint)head + newSize );
375 nextHead->Size = size - newSize;
376 nextHead->Magic = MAGIC_FREE;
377 foot->Head = nextHead; // Edit 2nd footer
378 head->Size = newSize; // Edit first header
381 head->ValidSize = __size;
383 foot = (void*)( (Uint)head + newSize - sizeof(tHeapFoot) );
385 foot->Magic = MAGIC_FOOT;
390 foot = (void*)( (Uint)head - sizeof(tHeapFoot) );
391 nextHead = foot->Head;
392 if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
394 Uint size = nextHead->Size + head->Size;
395 // Inexact fit, split things up
399 Warning("[Heap ] TODO: Space efficient realloc when new size is smaller");
406 // Set 1st (new/lower) header
407 nextHead->Magic = MAGIC_USED;
408 nextHead->Size = newSize;
409 nextHead->File = File;
410 nextHead->Line = Line;
411 nextHead->ValidSize = __size;
412 // Get 2nd (old) footer
413 foot = (void*)( (Uint)nextHead + newSize );
414 foot->Head = nextHead;
415 // Save old data size
416 oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead);
421 memcpy(nextHead->Data, __ptr, oldDataSize);
423 return nextHead->Data;
425 // On to the expensive then
429 nextHead = Heap_Allocate( File, Line, __size );
431 nextHead->File = File;
432 nextHead->Line = Line;
433 nextHead->ValidSize = __size;
438 head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead)
443 return nextHead->Data;
447 * \fn void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
448 * \brief Allocate and Zero a buffer in memory
449 * \param File Allocating file
450 * \param Line Line of allocation
451 * \param Bytes Size of the allocation
453 void *Heap_AllocateZero(const char *File, int Line, size_t Bytes)
455 void *ret = Heap_Allocate(File, Line, Bytes);
456 if(ret == NULL) return NULL;
458 memset( ret, 0, Bytes );
464 * \fn int Heap_IsHeapAddr(void *Ptr)
465 * \brief Checks if an address is a heap pointer
467 int Heap_IsHeapAddr(void *Ptr)
470 if((Uint)Ptr < (Uint)gHeapStart) return 0;
471 if((Uint)Ptr > (Uint)gHeapEnd) return 0;
472 if((Uint)Ptr & (sizeof(Uint)-1)) return 0;
474 head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
475 if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE)
483 void Heap_Validate(void)
491 tHeapHead *head, *badHead;
492 tHeapFoot *foot = NULL;
495 while( (Uint)head < (Uint)gHeapEnd )
497 foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
499 Log_Log("Heap", "%p (0x%llx): 0x%08lx (%i) %4C",
500 head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
501 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
503 Log_Log("Heap", "%sowned by %s:%i",
504 (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
508 // Sanity Check Header
509 if(head->Size == 0) {
510 Log_Warning("Heap", "HALTED - Size is zero");
513 if(head->Size & (MIN_SIZE-1)) {
514 Log_Warning("Heap", "HALTED - Size is malaligned");
517 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
518 Log_Warning("Heap", "HALTED - Head Magic is Bad");
523 if(foot->Magic != MAGIC_FOOT) {
524 Log_Warning("Heap", "HALTED - Foot Magic is Bad");
527 if(head != foot->Head) {
528 Log_Warning("Heap", "HALTED - Footer backlink is invalid");
536 // All OK? Go to next
537 head = foot->NextHead;
540 // If the heap is valid, ok!
541 if( (tVAddr)head == (tVAddr)gHeapEnd )
544 // Check for a bad return
545 if( (tVAddr)head >= (tVAddr)gHeapEnd )
549 Log_Log("Heap", "%p (0x%llx): 0x%08lx (%i) %4C",
550 head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
551 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
553 Log_Log("Heap", "%sowned by %s:%i",
554 (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
563 foot = (void*)( (tVAddr)gHeapEnd - sizeof(tHeapFoot) );
564 Log_Log("Heap", "==== Going Backwards ==== (from %p)", foot);
566 while( (tVAddr)head >= (tVAddr)badHead )
568 Log_Log("Heap", "%p (0x%llx): 0x%08lx %i %4C",
569 head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
570 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
572 Log_Log("Heap", "%sowned by %s:%i",
573 (head->Magic!=MAGIC_USED?"was ":""),
574 head->File, head->Line);
577 // Sanity Check Header
578 if(head->Size == 0) {
579 Log_Warning("Heap", "HALTED - Size is zero");
582 if(head->Size & (MIN_SIZE-1)) {
583 Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1));
586 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
587 Log_Warning("Heap", "HALTED - Head Magic is Bad");
592 if(foot->Magic != MAGIC_FOOT) {
593 Log_Warning("Heap", "HALTED - Foot Magic is Bad");
596 if(head != foot->Head) {
597 Log_Warning("Heap", "HALTED - Footer backlink is invalid");
601 if(head == badHead) break;
603 foot = (void*)( (tVAddr)head - sizeof(tHeapFoot) );
605 Log_Debug("Heap", "head=%p", head);
608 Panic("Heap_Dump - Heap is corrupted, kernel panic!");
613 void Heap_Stats(void)
620 int maxAlloc=0, minAlloc=-1;
621 int avgAlloc, frag, overhead;
623 for(head = gHeapStart;
624 (Uint)head < (Uint)gHeapEnd;
625 head = (void*)( (Uint)head + head->Size )
629 totalBytes += head->Size;
630 if( head->Magic == MAGIC_FREE )
633 freeBytes += head->Size;
635 else if( head->Magic == MAGIC_USED) {
636 if(maxAlloc < head->Size) maxAlloc = head->Size;
637 if(minAlloc == -1 || minAlloc > head->Size)
638 minAlloc = head->Size;
641 Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head);
645 // Print the block info?
647 Log_Debug("Heap", "%p - 0x%x Owned by %s:%i",
648 head, head->Size, head->File, head->Line);
652 Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes);
653 Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes);
654 frag = (nFree-1)*10000/nBlocks;
655 Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100);
656 avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree);
657 overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc;
658 Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%",
659 avgAlloc, overhead/100, overhead%100
661 Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes",
664 // Scan and get distribution
670 } sizeCounts[nBlocks];
673 memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0]));
675 for(head = gHeapStart;
676 (Uint)head < (Uint)gHeapEnd;
677 head = (void*)( (Uint)head + head->Size )
680 for( i = 0; i < nBlocks; i ++ ) {
681 if( sizeCounts[i].Size == 0 )
683 if( sizeCounts[i].Size == head->Size )
686 // Should never reach this part (in a non-concurrent case)
687 if( i == nBlocks ) continue;
688 sizeCounts[i].Size = head->Size;
689 sizeCounts[i].Count ++;
691 //Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head,
692 // head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i
694 //Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i,
695 // sizeCounts[i].Size, sizeCounts[i].Count);
699 for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ )
701 Log("Heap_Stats: 0x%x - %i blocks",
702 sizeCounts[i].Size, sizeCounts[i].Count
711 EXPORT(Heap_Allocate);
712 EXPORT(Heap_AllocateZero);
713 EXPORT(Heap_Reallocate);
714 EXPORT(Heap_Deallocate);
715 EXPORT(Heap_IsHeapAddr);