2 * AcessOS Microkernel Version
13 #define HEAP_INIT_SIZE 0x8000 // 32 KiB
14 #define MIN_SIZE (sizeof(void*))*8 // 8 Machine Words
16 #define COMPACT_HEAP 0 // Use 4 byte header?
19 //#define MAGIC_FOOT 0x2ACE5505
20 //#define MAGIC_FREE 0xACE55000
21 //#define MAGIC_USED 0x862B0505 // MAGIC_FOOT ^ MAGIC_FREE
22 #define MAGIC_FOOT 0x544F4F46 // 'FOOT'
23 #define MAGIC_FREE 0x45455246 // 'FREE'
24 #define MAGIC_USED 0x44455355 // 'USED'
27 void Heap_Install(void);
28 void *Heap_Extend(int Bytes);
29 void *Heap_Merge(tHeapHead *Head);
30 void *Heap_Allocate(const char *File, int Line, size_t Bytes);
31 void *Heap_AllocateZero(const char *File, int Line, size_t Bytes);
32 void *Heap_Reallocate(const char *File, int Line, void *Ptr, size_t Bytes);
33 void Heap_Deallocate(void *Ptr);
35 void Heap_Stats(void);
43 void Heap_Install(void)
45 gHeapStart = (void*)MM_KHEAP_BASE;
46 gHeapEnd = (void*)MM_KHEAP_BASE;
47 Heap_Extend(HEAP_INIT_SIZE);
51 * \fn void *Heap_Extend(int Bytes)
52 * \brief Extend the size of the heap
54 void *Heap_Extend(int Bytes)
57 tHeapHead *head = gHeapEnd;
61 if( (tVAddr)gHeapEnd == MM_KHEAP_MAX )
65 if( (tVAddr)gHeapEnd + ((Bytes+0xFFF)&~0xFFF) > MM_KHEAP_MAX ) {
66 Bytes = MM_KHEAP_MAX - (tVAddr)gHeapEnd;
70 // Heap expands in pages
71 for(i=0;i<(Bytes+0xFFF)>>12;i++)
72 MM_Allocate( (tVAddr)gHeapEnd+(i<<12) );
78 head->Size = (Bytes+0xFFF)&~0xFFF;
79 head->Magic = MAGIC_FREE;
80 foot = (void*)( (Uint)gHeapEnd - sizeof(tHeapFoot) );
82 foot->Magic = MAGIC_FOOT;
84 return Heap_Merge(head); // Merge with previous block
88 * \fn void *Heap_Merge(tHeapHead *Head)
89 * \brief Merges two ajacent heap blocks
91 void *Heap_Merge(tHeapHead *Head)
97 //Log("Heap_Merge: (Head=%p)", Head);
99 thisFoot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
100 if((Uint)thisFoot > (Uint)gHeapEnd) return NULL;
103 foot = (void*)( (Uint)Head - sizeof(tHeapFoot) );
104 if( ((Uint)foot < (Uint)gHeapEnd && (Uint)foot > (Uint)gHeapStart)
105 && foot->Head->Magic == MAGIC_FREE) {
106 foot->Head->Size += Head->Size; // Increase size
107 thisFoot->Head = foot->Head; // Change backlink
108 Head->Magic = 0; // Clear old head
110 Head = foot->Head; // Save new head address
111 foot->Head = NULL; // Clear central footer
115 // Merge Right (Upwards)
116 head = (void*)( (Uint)Head + Head->Size );
117 if((Uint)head < (Uint)gHeapEnd && head->Magic == MAGIC_FREE)
119 Head->Size += head->Size;
120 foot = (void*)( (Uint)Head + Head->Size - sizeof(tHeapFoot) );
121 foot->Head = Head; // Update Backlink
122 thisFoot->Head = NULL; // Clear old footer
124 head->Size = 0; // Clear old header
128 // Return new address
133 * \brief Allocate memory from the heap
134 * \param File Allocating source file
135 * \param Line Source line
136 * \param Bytes Size of region to allocate
138 void *Heap_Allocate(const char *File, int Line, size_t Bytes)
140 tHeapHead *head, *newhead;
141 tHeapFoot *foot, *newfoot;
142 tHeapHead *best = NULL;
143 Uint bestSize = 0; // Speed hack
147 Bytes = Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot);
148 Bytes = 1UUL << LOG2(Bytes);
150 Bytes = (Bytes + sizeof(tHeapHead) + sizeof(tHeapFoot) + MIN_SIZE-1) & ~(MIN_SIZE-1);
154 Mutex_Acquire(&glHeap);
157 for( head = gHeapStart;
158 (Uint)head < (Uint)gHeapEnd;
159 head = (void*)((Uint)head + head->Size)
164 if( head->Size != 1UUL << LOG2(head->Size) ) {
166 if( head->Size & (MIN_SIZE-1) ) {
168 Mutex_Release(&glHeap); // Release spinlock
170 Log_Warning("Heap", "Size of heap address %p is invalid not aligned (0x%x)", head, head->Size);
176 // Check if allocated
177 if(head->Magic == MAGIC_USED) continue;
179 if(head->Magic != MAGIC_FREE) {
180 Mutex_Release(&glHeap); // Release spinlock
182 Log_Warning("Heap", "Magic of heap address %p is invalid (0x%x)", head, head->Magic);
189 if(head->Size < Bytes) continue;
192 if(head->Size == Bytes) {
193 head->Magic = MAGIC_USED;
196 Mutex_Release(&glHeap); // Release spinlock
198 Log("[Heap ] Malloc'd %p (%i bytes), returning to %p", head->Data, head->Size, __builtin_return_address(0));
206 bestSize = head->Size;
209 // or check if the block is the best size
210 if(bestSize > head->Size) {
212 bestSize = head->Size;
217 // If no block large enough is found, create one
220 best = Heap_Extend( Bytes );
223 Mutex_Release(&glHeap); // Release spinlock
227 if(best->Size == Bytes) {
228 best->Magic = MAGIC_USED; // Mark block as used
231 Mutex_Release(&glHeap); // Release spinlock
233 Log("[Heap ] Malloc'd %p (%i bytes), returning to %p", best->Data, best->Size, __builtin_return_address(0));
240 newhead = (void*)( (Uint)best + Bytes );
241 newfoot = (void*)( (Uint)best + Bytes - sizeof(tHeapFoot) );
242 foot = (void*)( (Uint)best + best->Size - sizeof(tHeapFoot) );
244 newfoot->Head = best; // Create new footer
245 newfoot->Magic = MAGIC_FOOT;
246 newhead->Size = best->Size - Bytes; // Create new header
247 newhead->Magic = MAGIC_FREE;
248 foot->Head = newhead; // Update backlink in old footer
249 best->Size = Bytes; // Update size in old header
250 best->Magic = MAGIC_USED; // Mark block as used
254 Mutex_Release(&glHeap); // Release spinlock
256 Log_Debug("Heap", "newhead(%p)->Size = 0x%x", newhead, newhead->Size);
257 Log_Debug("Heap", "Malloc'd %p (0x%x bytes), returning to %s:%i",
258 best->Data, best->Size, File, Line);
264 * \fn void Heap_Deallocate(void *Ptr)
265 * \brief Free an allocated memory block
267 void Heap_Deallocate(void *Ptr)
273 Log_Log("Heap", "free: Ptr = %p", Ptr);
274 Log_Log("Heap", "free: Returns to %p", __builtin_return_address(0));
278 if( (Uint)Ptr & (sizeof(Uint)-1) ) {
279 Log_Warning("Heap", "free - Passed a non-aligned address (%p)", Ptr);
284 if((Uint)Ptr < (Uint)gHeapStart || (Uint)Ptr > (Uint)gHeapEnd)
286 Log_Warning("Heap", "free - Passed a non-heap address (%p < %p < %p)\n",
287 gHeapStart, Ptr, gHeapEnd);
291 // Check memory block - Header
292 head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
293 if(head->Magic == MAGIC_FREE) {
294 Log_Warning("Heap", "free - Passed a freed block (%p) by %p", head, __builtin_return_address(0));
297 if(head->Magic != MAGIC_USED) {
298 Log_Warning("Heap", "free - Magic value is invalid (%p, 0x%x)", head, head->Magic);
299 Log_Notice("Heap", "Allocated %s:%i", head->File, head->Line);
303 // Check memory block - Footer
304 foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
305 if(foot->Head != head) {
306 Log_Warning("Heap", "free - Footer backlink is incorrect (%p, 0x%x)", head, foot->Head);
307 Log_Notice("Heap", "Allocated %s:%i", head->File, head->Line);
310 if(foot->Magic != MAGIC_FOOT) {
311 Log_Warning("Heap", "free - Footer magic is invalid (%p, %p = 0x%x)", head, &foot->Magic, foot->Magic);
312 Log_Notice("Heap", "Allocated %s:%i", head->File, head->Line);
317 Mutex_Acquire( &glHeap );
320 head->Magic = MAGIC_FREE;
328 Mutex_Release( &glHeap );
332 * \brief Increase/Decrease the size of an allocation
333 * \param File Calling File
334 * \param Line Calling Line
335 * \param __ptr Old memory
336 * \param __size New Size
338 void *Heap_Reallocate(const char *File, int Line, void *__ptr, size_t __size)
340 tHeapHead *head = (void*)( (Uint)__ptr-8 );
343 Uint newSize = (__size + sizeof(tHeapFoot)+sizeof(tHeapHead)+MIN_SIZE-1)&~(MIN_SIZE-1);
345 // Check for reallocating NULL
346 if(__ptr == NULL) return Heap_Allocate(File, Line, __size);
348 // Check if resize is needed
349 if(newSize <= head->Size) return __ptr;
351 // Check if next block is free
352 nextHead = (void*)( (Uint)head + head->Size );
354 // Extend into next block
355 if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
357 Uint size = nextHead->Size + head->Size;
358 foot = (void*)( (Uint)nextHead + nextHead->Size - sizeof(tHeapFoot) );
360 if(size == newSize) {
361 head->Size = newSize;
362 head->ValidSize = __size;
370 // Create a new heap block
371 nextHead = (void*)( (Uint)head + newSize );
372 nextHead->Size = size - newSize;
373 nextHead->Magic = MAGIC_FREE;
374 foot->Head = nextHead; // Edit 2nd footer
375 head->Size = newSize; // Edit first header
378 head->ValidSize = __size;
380 foot = (void*)( (Uint)head + newSize - sizeof(tHeapFoot) );
382 foot->Magic = MAGIC_FOOT;
387 foot = (void*)( (Uint)head - sizeof(tHeapFoot) );
388 nextHead = foot->Head;
389 if(nextHead->Magic == MAGIC_FREE && nextHead->Size+head->Size >= newSize)
391 Uint size = nextHead->Size + head->Size;
392 // Inexact fit, split things up
396 Warning("[Heap ] TODO: Space efficient realloc when new size is smaller");
403 // Set 1st (new/lower) header
404 nextHead->Magic = MAGIC_USED;
405 nextHead->Size = newSize;
406 nextHead->File = File;
407 nextHead->Line = Line;
408 nextHead->ValidSize = __size;
409 // Get 2nd (old) footer
410 foot = (void*)( (Uint)nextHead + newSize );
411 foot->Head = nextHead;
412 // Save old data size
413 oldDataSize = head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead);
418 memcpy(nextHead->Data, __ptr, oldDataSize);
420 return nextHead->Data;
422 // On to the expensive then
426 nextHead = Heap_Allocate( File, Line, __size );
428 nextHead->File = File;
429 nextHead->Line = Line;
430 nextHead->ValidSize = __size;
435 head->Size - sizeof(tHeapFoot) - sizeof(tHeapHead)
440 return nextHead->Data;
444 * \fn void *Heap_AllocateZero(const char *File, int Line, size_t size)
445 * \brief Allocate and Zero a buffer in memory
446 * \param File Allocating file
447 * \param Line Line of allocation
448 * \param size Size of the allocation
450 void *Heap_AllocateZero(const char *File, int Line, size_t size)
452 void *ret = Heap_Allocate(File, Line, size);
453 if(ret == NULL) return NULL;
455 memset( ret, 0, size );
461 * \fn int Heap_IsHeapAddr(void *Ptr)
462 * \brief Checks if an address is a heap pointer
464 int Heap_IsHeapAddr(void *Ptr)
467 if((Uint)Ptr < (Uint)gHeapStart) return 0;
468 if((Uint)Ptr > (Uint)gHeapEnd) return 0;
469 if((Uint)Ptr & (sizeof(Uint)-1)) return 0;
471 head = (void*)( (Uint)Ptr - sizeof(tHeapHead) );
472 if(head->Magic != MAGIC_USED && head->Magic != MAGIC_FREE)
481 tHeapHead *head, *badHead;
485 while( (Uint)head < (Uint)gHeapEnd )
487 foot = (void*)( (Uint)head + head->Size - sizeof(tHeapFoot) );
488 Log_Log("Heap", "%p (0x%llx): 0x%08lx (%i) %4C",
489 head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
490 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
492 Log_Log("Heap", "%sowned by %s:%i",
493 (head->Magic==MAGIC_FREE?"was ":""), head->File, head->Line);
497 // Sanity Check Header
498 if(head->Size == 0) {
499 Log_Warning("Heap", "HALTED - Size is zero");
502 if(head->Size & (MIN_SIZE-1)) {
503 Log_Warning("Heap", "HALTED - Size is malaligned");
506 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
507 Log_Warning("Heap", "HALTED - Head Magic is Bad");
512 if(foot->Magic != MAGIC_FOOT) {
513 Log_Warning("Heap", "HALTED - Foot Magic is Bad");
516 if(head != foot->Head) {
517 Log_Warning("Heap", "HALTED - Footer backlink is invalid");
521 // All OK? Go to next
522 head = foot->NextHead;
525 // Check for a bad return
526 if( (tVAddr)head >= (tVAddr)gHeapEnd )
531 Log_Log("Heap", "==== Going Backwards ==== (from %p)", badHead);
534 foot = (void*)( (tVAddr)gHeapEnd - sizeof(tHeapFoot) );
536 while( (tVAddr)head >= (tVAddr)badHead )
538 Log_Log("Heap", "%p (0x%llx): 0x%08lx %i %4C",
539 head, MM_GetPhysAddr((Uint)head), head->Size, head->ValidSize, &head->Magic);
540 Log_Log("Heap", "%p %4C", foot->Head, &foot->Magic);
542 Log_Log("Heap", "%sowned by %s:%i",
543 (head->Magic!=MAGIC_USED?"was ":""),
544 head->File, head->Line);
547 // Sanity Check Header
548 if(head->Size == 0) {
549 Log_Warning("Heap", "HALTED - Size is zero");
552 if(head->Size & (MIN_SIZE-1)) {
553 Log_Warning("Heap", " - Size is malaligned (&0x%x)", ~(MIN_SIZE-1));
556 if(head->Magic != MAGIC_FREE && head->Magic != MAGIC_USED) {
557 Log_Warning("Heap", "HALTED - Head Magic is Bad");
562 if(foot->Magic != MAGIC_FOOT) {
563 Log_Warning("Heap", "HALTED - Foot Magic is Bad");
566 if(head != foot->Head) {
567 Log_Warning("Heap", "HALTED - Footer backlink is invalid");
571 if(head == badHead) break;
573 foot = (void*)( (tVAddr)head - sizeof(tHeapFoot) );
575 Log_Debug("Heap", "head=%p", head);
581 void Heap_Stats(void)
588 int maxAlloc=0, minAlloc=-1;
589 int avgAlloc, frag, overhead;
591 for(head = gHeapStart;
592 (Uint)head < (Uint)gHeapEnd;
593 head = (void*)( (Uint)head + head->Size )
597 totalBytes += head->Size;
598 if( head->Magic == MAGIC_FREE )
601 freeBytes += head->Size;
603 else if( head->Magic == MAGIC_USED) {
604 if(maxAlloc < head->Size) maxAlloc = head->Size;
605 if(minAlloc == -1 || minAlloc > head->Size)
606 minAlloc = head->Size;
609 Log_Warning("Heap", "Magic on %p invalid, skipping remainder of heap", head);
613 // Print the block info?
615 Log_Debug("Heap", "%p - 0x%x Owned by %s:%i",
616 head, head->Size, head->File, head->Line);
620 Log_Log("Heap", "%i blocks (0x%x bytes)", nBlocks, totalBytes);
621 Log_Log("Heap", "%i free blocks (0x%x bytes)", nFree, freeBytes);
622 frag = (nFree-1)*10000/nBlocks;
623 Log_Log("Heap", "%i.%02i%% Heap Fragmentation", frag/100, frag%100);
624 avgAlloc = (totalBytes-freeBytes)/(nBlocks-nFree);
625 overhead = (sizeof(tHeapFoot)+sizeof(tHeapHead))*10000/avgAlloc;
626 Log_Log("Heap", "Average allocation: %i bytes, Average Overhead: %i.%02i%%",
627 avgAlloc, overhead/100, overhead%100
629 Log_Log("Heap", "Smallest Block: %i bytes, Largest: %i bytes",
632 // Scan and get distribution
638 } sizeCounts[nBlocks];
641 memset(sizeCounts, 0, nBlocks*sizeof(sizeCounts[0]));
643 for(head = gHeapStart;
644 (Uint)head < (Uint)gHeapEnd;
645 head = (void*)( (Uint)head + head->Size )
648 for( i = 0; i < nBlocks; i ++ ) {
649 if( sizeCounts[i].Size == 0 )
651 if( sizeCounts[i].Size == head->Size )
654 // Should never reach this part (in a non-concurrent case)
655 if( i == nBlocks ) continue;
656 sizeCounts[i].Size = head->Size;
657 sizeCounts[i].Count ++;
659 //Log("Heap_Stats: %i %p - 0x%x bytes (%s) (%i)", nBlocks, head,
660 // head->Size, (head->Magic==MAGIC_FREE?"FREE":"used"), i
662 //Log("Heap_Stats: sizeCounts[%i] = {Size:0x%x, Count: %i}", i,
663 // sizeCounts[i].Size, sizeCounts[i].Count);
667 for( i = 0; i < nBlocks && sizeCounts[i].Count; i ++ )
669 Log("Heap_Stats: 0x%x - %i blocks",
670 sizeCounts[i].Size, sizeCounts[i].Count
679 EXPORT(Heap_Allocate);
680 EXPORT(Heap_AllocateZero);
681 EXPORT(Heap_Reallocate);
682 EXPORT(Heap_Deallocate);
683 EXPORT(Heap_IsHeapAddr);