1 /****************************************************************************
3 * Copyright (C) 2005 - 2011 by Vivante Corp.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the license, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *****************************************************************************/
26 ** gckHEAP object for kernel HAL layer. The heap implemented here is an arena-
27 ** based memory allocation. An arena-based memory heap allocates data quickly
28 ** from specified arenas and reduces memory fragmentation.
31 #include "gc_hal_kernel_precomp.h"
33 #define _GC_OBJ_ZONE gcvZONE_HEAP
35 /*******************************************************************************
36 ***** Structures ***************************************************************
37 *******************************************************************************/
39 #define gcdIN_USE ((gcskNODE_PTR) ~0)
41 typedef struct _gcskNODE
* gcskNODE_PTR
;
42 typedef struct _gcskNODE
44 /* Number of byets in node. */
47 /* Pointer to next free node, or gcvNULL to mark the node as freed, or
48 ** gcdIN_USE to mark the node as used. */
51 #if gcmIS_DEBUG(gcdDEBUG_CODE)
52 /* Time stamp of allocation. */
58 typedef struct _gcskHEAP
* gcskHEAP_PTR
;
59 typedef struct _gcskHEAP
69 gcskNODE_PTR freeList
;
78 /* Pointer to a gckOS object. */
84 /* Allocation parameters. */
85 gctSIZE_T allocationSize
;
89 #if gcmIS_DEBUG(gcdDEBUG_CODE)
93 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
94 /* Profile information. */
97 gctUINT64 allocBytesMax
;
98 gctUINT64 allocBytesTotal
;
100 gctUINT32 heapCountMax
;
101 gctUINT64 heapMemory
;
102 gctUINT64 heapMemoryMax
;
106 /*******************************************************************************
107 ***** Static Support Functions *************************************************
108 *******************************************************************************/
110 #if gcmIS_DEBUG(gcdDEBUG_CODE)
117 gctSIZE_T leaked
= 0;
119 /* Start at first node. */
122 /* Convert the pointer. */
123 gcskNODE_PTR node
= (gcskNODE_PTR
) p
;
125 /* Check if this is a used node. */
126 if (node
->next
== gcdIN_USE
)
128 /* Print the leaking node. */
129 gcmkTRACE_ZONE(gcvLEVEL_WARNING
, gcvZONE_HEAP
,
130 "Detected leaking: node=0x%x bytes=%lu timeStamp=%llu "
132 node
, node
->bytes
, node
->timeStamp
,
133 ((gctUINT32_PTR
) (node
+ 1))[0],
134 gcmPRINTABLE(((gctUINT8_PTR
) (node
+ 1))[0]),
135 gcmPRINTABLE(((gctUINT8_PTR
) (node
+ 1))[1]),
136 gcmPRINTABLE(((gctUINT8_PTR
) (node
+ 1))[2]),
137 gcmPRINTABLE(((gctUINT8_PTR
) (node
+ 1))[3]));
139 /* Add leaking byte count. */
140 leaked
+= node
->bytes
;
143 /* Test for end of heap. */
144 if (node
->bytes
== 0)
151 /* Move to next node. */
152 p
= (gctUINT8_PTR
) node
+ node
->bytes
;
156 /* Return the number of leaked bytes. */
166 gcskHEAP_PTR heap
, next
;
168 gcskHEAP_PTR freeList
= gcvNULL
;
170 gcmkHEADER_ARG("Heap=0x%x", Heap
);
172 /* Walk all the heaps. */
173 for (heap
= Heap
->heap
; heap
!= gcvNULL
; heap
= next
)
175 gcskNODE_PTR lastFree
= gcvNULL
;
177 /* Zero out the free list. */
178 heap
->freeList
= gcvNULL
;
180 /* Start at the first node. */
181 for (p
= (gctUINT8_PTR
) (heap
+ 1);;)
183 /* Convert the pointer. */
184 gcskNODE_PTR node
= (gcskNODE_PTR
) p
;
186 gcmkASSERT(p
<= (gctPOINTER
) ((gctUINT8_PTR
) (heap
+ 1) + heap
->size
));
188 /* Test if this node not used. */
189 if (node
->next
!= gcdIN_USE
)
191 /* Test if this is the end of the heap. */
192 if (node
->bytes
== 0)
197 /* Test of this is the first free node. */
198 else if (lastFree
== gcvNULL
)
200 /* Initialzie the free list. */
201 heap
->freeList
= node
;
207 /* Test if this free node is contiguous with the previous
209 if ((gctUINT8_PTR
) lastFree
+ lastFree
->bytes
== p
)
211 /* Just increase the size of the previous free node. */
212 lastFree
->bytes
+= node
->bytes
;
216 /* Add to linked list. */
217 lastFree
->next
= node
;
223 /* Move to next node. */
224 p
= (gctUINT8_PTR
) node
+ node
->bytes
;
227 /* Mark the end of the chain. */
228 if (lastFree
!= gcvNULL
)
230 lastFree
->next
= gcvNULL
;
236 /* Check if the entire heap is free. */
237 if ((heap
->freeList
!= gcvNULL
)
238 && (heap
->freeList
->bytes
== heap
->size
- gcmSIZEOF(gcskNODE
))
241 /* Remove the heap from the linked list. */
242 if (heap
->prev
== gcvNULL
)
248 heap
->prev
->next
= next
;
251 if (heap
->next
!= gcvNULL
)
253 heap
->next
->prev
= heap
->prev
;
256 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
257 /* Update profiling. */
258 Heap
->heapCount
-= 1;
259 Heap
->heapMemory
-= heap
->size
+ gcmSIZEOF(gcskHEAP
);
262 /* Add this heap to the list of heaps that need to be freed. */
263 heap
->next
= freeList
;
268 if (freeList
!= gcvNULL
)
270 /* Release the mutex, remove any chance for a dead lock. */
272 gckOS_ReleaseMutex(Heap
->os
, Heap
->mutex
));
274 /* Free all heaps in the free list. */
275 for (heap
= freeList
; heap
!= gcvNULL
; heap
= next
)
277 /* Get pointer to the next heap. */
281 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_HEAP
,
282 "Freeing heap 0x%x (%lu bytes)",
283 heap
, heap
->size
+ gcmSIZEOF(gcskHEAP
));
284 gcmkVERIFY_OK(gckOS_FreeMemory(Heap
->os
, heap
));
287 /* Acquire the mutex again. */
289 gckOS_AcquireMutex(Heap
->os
, Heap
->mutex
, gcvINFINITE
));
297 /*******************************************************************************
298 ***** gckHEAP API Code *********************************************************
299 *******************************************************************************/
301 /*******************************************************************************
305 ** Construct a new gckHEAP object.
310 ** Pointer to a gckOS object.
312 ** gctSIZE_T AllocationSize
313 ** Minimum size per arena.
318 ** Pointer to a variable that will hold the pointer to the gckHEAP
324 IN gctSIZE_T AllocationSize
,
329 gckHEAP heap
= gcvNULL
;
330 gctPOINTER pointer
= gcvNULL
;
332 gcmkHEADER_ARG("Os=0x%x AllocationSize=%lu", Os
, AllocationSize
);
334 /* Verify the arguments. */
335 gcmkVERIFY_OBJECT(Os
, gcvOBJ_OS
);
336 gcmkVERIFY_ARGUMENT(Heap
!= gcvNULL
);
338 /* Allocate the gckHEAP object. */
339 gcmkONERROR(gckOS_AllocateMemory(Os
,
340 gcmSIZEOF(struct _gckHEAP
),
345 /* Initialize the gckHEAP object. */
346 heap
->object
.type
= gcvOBJ_HEAP
;
348 heap
->allocationSize
= AllocationSize
;
349 heap
->heap
= gcvNULL
;
350 #if gcmIS_DEBUG(gcdDEBUG_CODE)
354 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
355 /* Zero the counters. */
356 heap
->allocCount
= 0;
357 heap
->allocBytes
= 0;
358 heap
->allocBytesMax
= 0;
359 heap
->allocBytesTotal
= 0;
361 heap
->heapCountMax
= 0;
362 heap
->heapMemory
= 0;
363 heap
->heapMemoryMax
= 0;
366 /* Create the mutex. */
367 gcmkONERROR(gckOS_CreateMutex(Os
, &heap
->mutex
));
369 /* Return the pointer to the gckHEAP object. */
373 gcmkFOOTER_ARG("*Heap=0x%x", *Heap
);
380 /* Free the heap structure. */
381 gcmkVERIFY_OK(gckOS_FreeMemory(Os
, heap
));
384 /* Return the status. */
389 /*******************************************************************************
393 ** Destroy a gckHEAP object.
398 ** Pointer to a gckHEAP object to destroy.
410 #if gcmIS_DEBUG(gcdDEBUG_CODE)
411 gctSIZE_T leaked
= 0;
414 gcmkHEADER_ARG("Heap=0x%x", Heap
);
416 for (heap
= Heap
->heap
; heap
!= gcvNULL
; heap
= Heap
->heap
)
418 /* Unlink heap from linked list. */
419 Heap
->heap
= heap
->next
;
421 #if gcmIS_DEBUG(gcdDEBUG_CODE)
422 /* Check for leaked memory. */
423 leaked
+= _DumpHeap(heap
);
427 gcmkVERIFY_OK(gckOS_FreeMemory(Heap
->os
, heap
));
430 /* Free the mutex. */
431 gcmkVERIFY_OK(gckOS_DeleteMutex(Heap
->os
, Heap
->mutex
));
433 /* Free the heap structure. */
434 gcmkVERIFY_OK(gckOS_FreeMemory(Heap
->os
, Heap
));
437 #if gcmIS_DEBUG(gcdDEBUG_CODE)
438 gcmkFOOTER_ARG("leaked=%lu", leaked
);
445 /*******************************************************************************
449 ** Allocate data from the heap.
454 ** Pointer to a gckHEAP object.
456 ** IN gctSIZE_T Bytes
457 ** Number of byte to allocate.
461 ** gctPOINTER * Memory
462 ** Pointer to a variable that will hold the address of the allocated
469 OUT gctPOINTER
* Memory
472 gctBOOL acquired
= gcvFALSE
;
476 gcskNODE_PTR node
, used
, prevFree
= gcvNULL
;
477 gctPOINTER memory
= gcvNULL
;
479 gcmkHEADER_ARG("Heap=0x%x Bytes=%lu", Heap
, Bytes
);
481 /* Verify the arguments. */
482 gcmkVERIFY_OBJECT(Heap
, gcvOBJ_HEAP
);
483 gcmkVERIFY_ARGUMENT(Bytes
> 0);
484 gcmkVERIFY_ARGUMENT(Memory
!= gcvNULL
);
486 /* Determine number of bytes required for a node. */
487 bytes
= gcmALIGN(Bytes
+ gcmSIZEOF(gcskNODE
), 8);
489 /* Acquire the mutex. */
491 gckOS_AcquireMutex(Heap
->os
, Heap
->mutex
, gcvINFINITE
));
495 /* Check if this allocation is bigger than the default allocation size. */
496 if (bytes
> Heap
->allocationSize
- gcmSIZEOF(gcskHEAP
) - gcmSIZEOF(gcskNODE
))
498 /* Adjust allocation size. */
499 Heap
->allocationSize
= bytes
* 2;
502 else if (Heap
->heap
!= gcvNULL
)
506 /* 2 retries, since we might need to compact. */
507 for (i
= 0; i
< 2; ++i
)
509 /* Walk all the heaps. */
510 for (heap
= Heap
->heap
; heap
!= gcvNULL
; heap
= heap
->next
)
512 /* Check if this heap has enough bytes to hold the request. */
513 if (bytes
<= heap
->size
- gcmSIZEOF(gcskNODE
))
517 /* Walk the chain of free nodes. */
518 for (node
= heap
->freeList
;
523 gcmkASSERT(node
->next
!= gcdIN_USE
);
525 /* Check if this free node has enough bytes. */
526 if (node
->bytes
>= bytes
)
532 /* Save current free node for linked list management. */
540 /* Compact the heap. */
541 gcmkVERIFY_OK(_CompactKernelHeap(Heap
));
543 #if gcmIS_DEBUG(gcdDEBUG_CODE)
544 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
545 "===== KERNEL HEAP =====");
546 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
547 "Number of allocations : %12u",
549 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
550 "Number of bytes allocated : %12llu",
552 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
553 "Maximum allocation size : %12llu",
554 Heap
->allocBytesMax
);
555 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
556 "Total number of bytes allocated : %12llu",
557 Heap
->allocBytesTotal
);
558 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
559 "Number of heaps : %12u",
561 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
562 "Heap memory in bytes : %12llu",
564 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
565 "Maximum number of heaps : %12u",
567 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE
, gcvZONE_HEAP
,
568 "Maximum heap memory in bytes : %12llu",
569 Heap
->heapMemoryMax
);
575 /* Release the mutex. */
577 gckOS_ReleaseMutex(Heap
->os
, Heap
->mutex
));
581 /* Allocate a new heap. */
583 gckOS_AllocateMemory(Heap
->os
,
584 Heap
->allocationSize
,
587 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_HEAP
,
588 "Allocated heap 0x%x (%lu bytes)",
589 memory
, Heap
->allocationSize
);
591 /* Acquire the mutex. */
593 gckOS_AcquireMutex(Heap
->os
, Heap
->mutex
, gcvINFINITE
));
597 /* Use the allocated memory as the heap. */
598 heap
= (gcskHEAP_PTR
) memory
;
600 /* Insert this heap to the head of the chain. */
601 heap
->next
= Heap
->heap
;
602 heap
->prev
= gcvNULL
;
603 heap
->size
= Heap
->allocationSize
- gcmSIZEOF(gcskHEAP
);
605 if (heap
->next
!= gcvNULL
)
607 heap
->next
->prev
= heap
;
611 /* Mark the end of the heap. */
612 node
= (gcskNODE_PTR
) ( (gctUINT8_PTR
) heap
613 + Heap
->allocationSize
614 - gcmSIZEOF(gcskNODE
)
617 node
->next
= gcvNULL
;
619 /* Create a free list. */
620 node
= (gcskNODE_PTR
) (heap
+ 1);
621 heap
->freeList
= node
;
623 /* Initialize the free list. */
624 node
->bytes
= heap
->size
- gcmSIZEOF(gcskNODE
);
625 node
->next
= gcvNULL
;
627 /* No previous free. */
630 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
631 /* Update profiling. */
632 Heap
->heapCount
+= 1;
633 Heap
->heapMemory
+= Heap
->allocationSize
;
635 if (Heap
->heapCount
> Heap
->heapCountMax
)
637 Heap
->heapCountMax
= Heap
->heapCount
;
639 if (Heap
->heapMemory
> Heap
->heapMemoryMax
)
641 Heap
->heapMemoryMax
= Heap
->heapMemory
;
646 /* Verify some stuff. */
647 gcmkASSERT(heap
!= gcvNULL
);
648 gcmkASSERT(node
!= gcvNULL
);
649 gcmkASSERT(node
->bytes
>= bytes
);
651 if (heap
->prev
!= gcvNULL
)
653 /* Unlink the heap from the linked list. */
654 heap
->prev
->next
= heap
->next
;
655 if (heap
->next
!= gcvNULL
)
657 heap
->next
->prev
= heap
->prev
;
660 /* Move the heap to the front of the list. */
661 heap
->next
= Heap
->heap
;
662 heap
->prev
= gcvNULL
;
664 heap
->next
->prev
= heap
;
667 /* Check if there is enough free space left after usage for another free
669 if (node
->bytes
- bytes
>= gcmSIZEOF(gcskNODE
))
671 /* Allocated used space from the back of the free list. */
672 used
= (gcskNODE_PTR
) ((gctUINT8_PTR
) node
+ node
->bytes
- bytes
);
674 /* Adjust the number of free bytes. */
675 node
->bytes
-= bytes
;
676 gcmkASSERT(node
->bytes
>= gcmSIZEOF(gcskNODE
));
680 /* Remove this free list from the chain. */
681 if (prevFree
== gcvNULL
)
683 heap
->freeList
= node
->next
;
687 prevFree
->next
= node
->next
;
690 /* Consume the entire free node. */
691 used
= (gcskNODE_PTR
) node
;
695 /* Mark node as used. */
697 used
->next
= gcdIN_USE
;
698 #if gcmIS_DEBUG(gcdDEBUG_CODE)
699 used
->timeStamp
= ++Heap
->timeStamp
;
702 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
703 /* Update profile counters. */
704 Heap
->allocCount
+= 1;
705 Heap
->allocBytes
+= bytes
;
706 Heap
->allocBytesMax
= gcmMAX(Heap
->allocBytes
, Heap
->allocBytesMax
);
707 Heap
->allocBytesTotal
+= bytes
;
710 /* Release the mutex. */
712 gckOS_ReleaseMutex(Heap
->os
, Heap
->mutex
));
714 /* Return pointer to memory. */
718 gcmkFOOTER_ARG("*Memory=0x%x", *Memory
);
724 /* Release the mutex. */
726 gckOS_ReleaseMutex(Heap
->os
, Heap
->mutex
));
729 if (memory
!= gcvNULL
)
731 /* Free the heap memory. */
732 gckOS_FreeMemory(Heap
->os
, memory
);
735 /* Return the status. */
740 /*******************************************************************************
744 ** Free allocated memory from the heap.
749 ** Pointer to a gckHEAP object.
751 ** IN gctPOINTER Memory
752 ** Pointer to memory to free.
767 gcmkHEADER_ARG("Heap=0x%x Memory=0x%x", Heap
, Memory
);
769 /* Verify the arguments. */
770 gcmkVERIFY_OBJECT(Heap
, gcvOBJ_HEAP
);
771 gcmkVERIFY_ARGUMENT(Memory
!= gcvNULL
);
773 /* Acquire the mutex. */
775 gckOS_AcquireMutex(Heap
->os
, Heap
->mutex
, gcvINFINITE
));
777 /* Pointer to structure. */
778 node
= (gcskNODE_PTR
) Memory
- 1;
780 /* Mark the node as freed. */
781 node
->next
= gcvNULL
;
783 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
784 /* Update profile counters. */
785 Heap
->allocBytes
-= node
->bytes
;
788 /* Release the mutex. */
790 gckOS_ReleaseMutex(Heap
->os
, Heap
->mutex
));
797 /* Return the status. */
804 gckHEAP_ProfileStart(
808 gcmkHEADER_ARG("Heap=0x%x", Heap
);
810 /* Verify the arguments. */
811 gcmkVERIFY_OBJECT(Heap
, gcvOBJ_HEAP
);
813 /* Zero the counters. */
814 Heap
->allocCount
= 0;
815 Heap
->allocBytes
= 0;
816 Heap
->allocBytesMax
= 0;
817 Heap
->allocBytesTotal
= 0;
819 Heap
->heapCountMax
= 0;
820 Heap
->heapMemory
= 0;
821 Heap
->heapMemoryMax
= 0;
831 IN gctCONST_STRING Title
834 gcmkHEADER_ARG("Heap=0x%x Title=0x%x", Heap
, Title
);
836 /* Verify the arguments. */
837 gcmkVERIFY_OBJECT(Heap
, gcvOBJ_HEAP
);
838 gcmkVERIFY_ARGUMENT(Title
!= gcvNULL
);
841 gcmkPRINT("=====[ HEAP - %s ]=====", Title
);
842 gcmkPRINT("Number of allocations : %12u", Heap
->allocCount
);
843 gcmkPRINT("Number of bytes allocated : %12llu", Heap
->allocBytes
);
844 gcmkPRINT("Maximum allocation size : %12llu", Heap
->allocBytesMax
);
845 gcmkPRINT("Total number of bytes allocated : %12llu", Heap
->allocBytesTotal
);
846 gcmkPRINT("Number of heaps : %12u", Heap
->heapCount
);
847 gcmkPRINT("Heap memory in bytes : %12llu", Heap
->heapMemory
);
848 gcmkPRINT("Maximum number of heaps : %12u", Heap
->heapCountMax
);
849 gcmkPRINT("Maximum heap memory in bytes : %12llu", Heap
->heapMemoryMax
);
850 gcmkPRINT("==============================================");
856 #endif /* VIVANTE_PROFILER */
858 /*******************************************************************************
859 ***** Test Code ****************************************************************
860 *******************************************************************************/