1 /****************************************************************************
3 * Copyright (C) 2005 - 2013 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 *****************************************************************************/
24 ** gckHEAP object for kernel HAL layer. The heap implemented here is an arena-
25 ** based memory allocation. An arena-based memory heap allocates data quickly
26 ** from specified arenas and reduces memory fragmentation.
29 #include "gc_hal_kernel_precomp.h"
31 #define _GC_OBJ_ZONE gcvZONE_HEAP
33 /*******************************************************************************
34 ***** Structures ***************************************************************
35 *******************************************************************************/
37 #define gcdIN_USE ((gcskNODE_PTR) ~0)
39 typedef struct _gcskNODE * gcskNODE_PTR;
40 typedef struct _gcskNODE
42 /* Number of byets in node. */
45 /* Pointer to next free node, or gcvNULL to mark the node as freed, or
46 ** gcdIN_USE to mark the node as used. */
49 #if gcmIS_DEBUG(gcdDEBUG_CODE)
50 /* Time stamp of allocation. */
56 typedef struct _gcskHEAP * gcskHEAP_PTR;
57 typedef struct _gcskHEAP
67 gcskNODE_PTR freeList;
76 /* Pointer to a gckOS object. */
82 /* Allocation parameters. */
83 gctSIZE_T allocationSize;
87 #if gcmIS_DEBUG(gcdDEBUG_CODE)
91 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
92 /* Profile information. */
95 gctUINT64 allocBytesMax;
96 gctUINT64 allocBytesTotal;
98 gctUINT32 heapCountMax;
100 gctUINT64 heapMemoryMax;
104 /*******************************************************************************
105 ***** Static Support Functions *************************************************
106 *******************************************************************************/
108 #if gcmIS_DEBUG(gcdDEBUG_CODE)
115 gctSIZE_T leaked = 0;
117 /* Start at first node. */
120 /* Convert the pointer. */
121 gcskNODE_PTR node = (gcskNODE_PTR) p;
123 /* Check if this is a used node. */
124 if (node->next == gcdIN_USE)
126 /* Print the leaking node. */
127 gcmkTRACE_ZONE(gcvLEVEL_WARNING, gcvZONE_HEAP,
128 "Detected leaking: node=0x%x bytes=%lu timeStamp=%llu "
130 node, node->bytes, node->timeStamp,
131 ((gctUINT32_PTR) (node + 1))[0],
132 gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[0]),
133 gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[1]),
134 gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[2]),
135 gcmPRINTABLE(((gctUINT8_PTR) (node + 1))[3]));
137 /* Add leaking byte count. */
138 leaked += node->bytes;
141 /* Test for end of heap. */
142 if (node->bytes == 0)
149 /* Move to next node. */
150 p = (gctUINT8_PTR) node + node->bytes;
154 /* Return the number of leaked bytes. */
164 gcskHEAP_PTR heap, next;
166 gcskHEAP_PTR freeList = gcvNULL;
168 gcmkHEADER_ARG("Heap=0x%x", Heap);
170 /* Walk all the heaps. */
171 for (heap = Heap->heap; heap != gcvNULL; heap = next)
173 gcskNODE_PTR lastFree = gcvNULL;
175 /* Zero out the free list. */
176 heap->freeList = gcvNULL;
178 /* Start at the first node. */
179 for (p = (gctUINT8_PTR) (heap + 1);;)
181 /* Convert the pointer. */
182 gcskNODE_PTR node = (gcskNODE_PTR) p;
184 gcmkASSERT(p <= (gctPOINTER) ((gctUINT8_PTR) (heap + 1) + heap->size));
186 /* Test if this node not used. */
187 if (node->next != gcdIN_USE)
189 /* Test if this is the end of the heap. */
190 if (node->bytes == 0)
195 /* Test of this is the first free node. */
196 else if (lastFree == gcvNULL)
198 /* Initialzie the free list. */
199 heap->freeList = node;
205 /* Test if this free node is contiguous with the previous
207 if ((gctUINT8_PTR) lastFree + lastFree->bytes == p)
209 /* Just increase the size of the previous free node. */
210 lastFree->bytes += node->bytes;
214 /* Add to linked list. */
215 lastFree->next = node;
221 /* Move to next node. */
222 p = (gctUINT8_PTR) node + node->bytes;
225 /* Mark the end of the chain. */
226 if (lastFree != gcvNULL)
228 lastFree->next = gcvNULL;
234 /* Check if the entire heap is free. */
235 if ((heap->freeList != gcvNULL)
236 && (heap->freeList->bytes == heap->size - gcmSIZEOF(gcskNODE))
239 /* Remove the heap from the linked list. */
240 if (heap->prev == gcvNULL)
246 heap->prev->next = next;
249 if (heap->next != gcvNULL)
251 heap->next->prev = heap->prev;
254 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
255 /* Update profiling. */
256 Heap->heapCount -= 1;
257 Heap->heapMemory -= heap->size + gcmSIZEOF(gcskHEAP);
260 /* Add this heap to the list of heaps that need to be freed. */
261 heap->next = freeList;
266 if (freeList != gcvNULL)
268 /* Release the mutex, remove any chance for a dead lock. */
270 gckOS_ReleaseMutex(Heap->os, Heap->mutex));
272 /* Free all heaps in the free list. */
273 for (heap = freeList; heap != gcvNULL; heap = next)
275 /* Get pointer to the next heap. */
279 gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP,
280 "Freeing heap 0x%x (%lu bytes)",
281 heap, heap->size + gcmSIZEOF(gcskHEAP));
282 gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap));
285 /* Acquire the mutex again. */
287 gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
295 /*******************************************************************************
296 ***** gckHEAP API Code *********************************************************
297 *******************************************************************************/
299 /*******************************************************************************
303 ** Construct a new gckHEAP object.
308 ** Pointer to a gckOS object.
310 ** gctSIZE_T AllocationSize
311 ** Minimum size per arena.
316 ** Pointer to a variable that will hold the pointer to the gckHEAP
322 IN gctSIZE_T AllocationSize,
327 gckHEAP heap = gcvNULL;
328 gctPOINTER pointer = gcvNULL;
330 gcmkHEADER_ARG("Os=0x%x AllocationSize=%lu", Os, AllocationSize);
332 /* Verify the arguments. */
333 gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
334 gcmkVERIFY_ARGUMENT(Heap != gcvNULL);
336 /* Allocate the gckHEAP object. */
337 gcmkONERROR(gckOS_AllocateMemory(Os,
338 gcmSIZEOF(struct _gckHEAP),
343 /* Initialize the gckHEAP object. */
344 heap->object.type = gcvOBJ_HEAP;
346 heap->allocationSize = AllocationSize;
347 heap->heap = gcvNULL;
348 #if gcmIS_DEBUG(gcdDEBUG_CODE)
352 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
353 /* Zero the counters. */
354 heap->allocCount = 0;
355 heap->allocBytes = 0;
356 heap->allocBytesMax = 0;
357 heap->allocBytesTotal = 0;
359 heap->heapCountMax = 0;
360 heap->heapMemory = 0;
361 heap->heapMemoryMax = 0;
364 /* Create the mutex. */
365 gcmkONERROR(gckOS_CreateMutex(Os, &heap->mutex));
367 /* Return the pointer to the gckHEAP object. */
371 gcmkFOOTER_ARG("*Heap=0x%x", *Heap);
378 /* Free the heap structure. */
379 gcmkVERIFY_OK(gckOS_FreeMemory(Os, heap));
382 /* Return the status. */
387 /*******************************************************************************
391 ** Destroy a gckHEAP object.
396 ** Pointer to a gckHEAP object to destroy.
408 #if gcmIS_DEBUG(gcdDEBUG_CODE)
409 gctSIZE_T leaked = 0;
412 gcmkHEADER_ARG("Heap=0x%x", Heap);
414 for (heap = Heap->heap; heap != gcvNULL; heap = Heap->heap)
416 /* Unlink heap from linked list. */
417 Heap->heap = heap->next;
419 #if gcmIS_DEBUG(gcdDEBUG_CODE)
420 /* Check for leaked memory. */
421 leaked += _DumpHeap(heap);
425 gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, heap));
428 /* Free the mutex. */
429 gcmkVERIFY_OK(gckOS_DeleteMutex(Heap->os, Heap->mutex));
431 /* Free the heap structure. */
432 gcmkVERIFY_OK(gckOS_FreeMemory(Heap->os, Heap));
435 #if gcmIS_DEBUG(gcdDEBUG_CODE)
436 gcmkFOOTER_ARG("leaked=%lu", leaked);
443 /*******************************************************************************
447 ** Allocate data from the heap.
452 ** Pointer to a gckHEAP object.
454 ** IN gctSIZE_T Bytes
455 ** Number of byte to allocate.
459 ** gctPOINTER * Memory
460 ** Pointer to a variable that will hold the address of the allocated
467 OUT gctPOINTER * Memory
470 gctBOOL acquired = gcvFALSE;
474 gcskNODE_PTR node, used, prevFree = gcvNULL;
475 gctPOINTER memory = gcvNULL;
477 gcmkHEADER_ARG("Heap=0x%x Bytes=%lu", Heap, Bytes);
479 /* Verify the arguments. */
480 gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
481 gcmkVERIFY_ARGUMENT(Bytes > 0);
482 gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
484 /* Determine number of bytes required for a node. */
485 bytes = gcmALIGN(Bytes + gcmSIZEOF(gcskNODE), 8);
487 /* Acquire the mutex. */
489 gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
493 /* Check if this allocation is bigger than the default allocation size. */
494 if (bytes > Heap->allocationSize - gcmSIZEOF(gcskHEAP) - gcmSIZEOF(gcskNODE))
496 /* Adjust allocation size. */
497 Heap->allocationSize = bytes * 2;
500 else if (Heap->heap != gcvNULL)
504 /* 2 retries, since we might need to compact. */
505 for (i = 0; i < 2; ++i)
507 /* Walk all the heaps. */
508 for (heap = Heap->heap; heap != gcvNULL; heap = heap->next)
510 /* Check if this heap has enough bytes to hold the request. */
511 if (bytes <= heap->size - gcmSIZEOF(gcskNODE))
515 /* Walk the chain of free nodes. */
516 for (node = heap->freeList;
521 gcmkASSERT(node->next != gcdIN_USE);
523 /* Check if this free node has enough bytes. */
524 if (node->bytes >= bytes)
530 /* Save current free node for linked list management. */
538 /* Compact the heap. */
539 gcmkVERIFY_OK(_CompactKernelHeap(Heap));
541 #if gcmIS_DEBUG(gcdDEBUG_CODE)
542 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
543 "===== KERNEL HEAP =====");
544 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
545 "Number of allocations : %12u",
547 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
548 "Number of bytes allocated : %12llu",
550 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
551 "Maximum allocation size : %12llu",
552 Heap->allocBytesMax);
553 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
554 "Total number of bytes allocated : %12llu",
555 Heap->allocBytesTotal);
556 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
557 "Number of heaps : %12u",
559 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
560 "Heap memory in bytes : %12llu",
562 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
563 "Maximum number of heaps : %12u",
565 gcmkTRACE_ZONE(gcvLEVEL_VERBOSE, gcvZONE_HEAP,
566 "Maximum heap memory in bytes : %12llu",
567 Heap->heapMemoryMax);
573 /* Release the mutex. */
575 gckOS_ReleaseMutex(Heap->os, Heap->mutex));
579 /* Allocate a new heap. */
581 gckOS_AllocateMemory(Heap->os,
582 Heap->allocationSize,
585 gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_HEAP,
586 "Allocated heap 0x%x (%lu bytes)",
587 memory, Heap->allocationSize);
589 /* Acquire the mutex. */
591 gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
595 /* Use the allocated memory as the heap. */
596 heap = (gcskHEAP_PTR) memory;
598 /* Insert this heap to the head of the chain. */
599 heap->next = Heap->heap;
600 heap->prev = gcvNULL;
601 heap->size = Heap->allocationSize - gcmSIZEOF(gcskHEAP);
603 if (heap->next != gcvNULL)
605 heap->next->prev = heap;
609 /* Mark the end of the heap. */
610 node = (gcskNODE_PTR) ( (gctUINT8_PTR) heap
611 + Heap->allocationSize
612 - gcmSIZEOF(gcskNODE)
615 node->next = gcvNULL;
617 /* Create a free list. */
618 node = (gcskNODE_PTR) (heap + 1);
619 heap->freeList = node;
621 /* Initialize the free list. */
622 node->bytes = heap->size - gcmSIZEOF(gcskNODE);
623 node->next = gcvNULL;
625 /* No previous free. */
628 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
629 /* Update profiling. */
630 Heap->heapCount += 1;
631 Heap->heapMemory += Heap->allocationSize;
633 if (Heap->heapCount > Heap->heapCountMax)
635 Heap->heapCountMax = Heap->heapCount;
637 if (Heap->heapMemory > Heap->heapMemoryMax)
639 Heap->heapMemoryMax = Heap->heapMemory;
644 /* Verify some stuff. */
645 gcmkASSERT(heap != gcvNULL);
646 gcmkASSERT(node != gcvNULL);
647 gcmkASSERT(node->bytes >= bytes);
649 if (heap->prev != gcvNULL)
651 /* Unlink the heap from the linked list. */
652 heap->prev->next = heap->next;
653 if (heap->next != gcvNULL)
655 heap->next->prev = heap->prev;
658 /* Move the heap to the front of the list. */
659 heap->next = Heap->heap;
660 heap->prev = gcvNULL;
662 heap->next->prev = heap;
665 /* Check if there is enough free space left after usage for another free
667 if (node->bytes - bytes >= gcmSIZEOF(gcskNODE))
669 /* Allocated used space from the back of the free list. */
670 used = (gcskNODE_PTR) ((gctUINT8_PTR) node + node->bytes - bytes);
672 /* Adjust the number of free bytes. */
673 node->bytes -= bytes;
674 gcmkASSERT(node->bytes >= gcmSIZEOF(gcskNODE));
678 /* Remove this free list from the chain. */
679 if (prevFree == gcvNULL)
681 heap->freeList = node->next;
685 prevFree->next = node->next;
688 /* Consume the entire free node. */
689 used = (gcskNODE_PTR) node;
693 /* Mark node as used. */
695 used->next = gcdIN_USE;
696 #if gcmIS_DEBUG(gcdDEBUG_CODE)
697 used->timeStamp = ++Heap->timeStamp;
700 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
701 /* Update profile counters. */
702 Heap->allocCount += 1;
703 Heap->allocBytes += bytes;
704 Heap->allocBytesMax = gcmMAX(Heap->allocBytes, Heap->allocBytesMax);
705 Heap->allocBytesTotal += bytes;
708 /* Release the mutex. */
710 gckOS_ReleaseMutex(Heap->os, Heap->mutex));
712 /* Return pointer to memory. */
716 gcmkFOOTER_ARG("*Memory=0x%x", *Memory);
722 /* Release the mutex. */
724 gckOS_ReleaseMutex(Heap->os, Heap->mutex));
727 if (memory != gcvNULL)
729 /* Free the heap memory. */
730 gckOS_FreeMemory(Heap->os, memory);
733 /* Return the status. */
738 /*******************************************************************************
742 ** Free allocated memory from the heap.
747 ** Pointer to a gckHEAP object.
749 ** IN gctPOINTER Memory
750 ** Pointer to memory to free.
765 gcmkHEADER_ARG("Heap=0x%x Memory=0x%x", Heap, Memory);
767 /* Verify the arguments. */
768 gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
769 gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
771 /* Acquire the mutex. */
773 gckOS_AcquireMutex(Heap->os, Heap->mutex, gcvINFINITE));
775 /* Pointer to structure. */
776 node = (gcskNODE_PTR) Memory - 1;
778 /* Mark the node as freed. */
779 node->next = gcvNULL;
781 #if VIVANTE_PROFILER || gcmIS_DEBUG(gcdDEBUG_CODE)
782 /* Update profile counters. */
783 Heap->allocBytes -= node->bytes;
786 /* Release the mutex. */
788 gckOS_ReleaseMutex(Heap->os, Heap->mutex));
795 /* Return the status. */
802 gckHEAP_ProfileStart(
806 gcmkHEADER_ARG("Heap=0x%x", Heap);
808 /* Verify the arguments. */
809 gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
811 /* Zero the counters. */
812 Heap->allocCount = 0;
813 Heap->allocBytes = 0;
814 Heap->allocBytesMax = 0;
815 Heap->allocBytesTotal = 0;
817 Heap->heapCountMax = 0;
818 Heap->heapMemory = 0;
819 Heap->heapMemoryMax = 0;
829 IN gctCONST_STRING Title
832 gcmkHEADER_ARG("Heap=0x%x Title=0x%x", Heap, Title);
834 /* Verify the arguments. */
835 gcmkVERIFY_OBJECT(Heap, gcvOBJ_HEAP);
836 gcmkVERIFY_ARGUMENT(Title != gcvNULL);
839 gcmkPRINT("=====[ HEAP - %s ]=====", Title);
840 gcmkPRINT("Number of allocations : %12u", Heap->allocCount);
841 gcmkPRINT("Number of bytes allocated : %12llu", Heap->allocBytes);
842 gcmkPRINT("Maximum allocation size : %12llu", Heap->allocBytesMax);
843 gcmkPRINT("Total number of bytes allocated : %12llu", Heap->allocBytesTotal);
844 gcmkPRINT("Number of heaps : %12u", Heap->heapCount);
845 gcmkPRINT("Heap memory in bytes : %12llu", Heap->heapMemory);
846 gcmkPRINT("Maximum number of heaps : %12u", Heap->heapCountMax);
847 gcmkPRINT("Maximum heap memory in bytes : %12llu", Heap->heapMemoryMax);
848 gcmkPRINT("==============================================");
854 #endif /* VIVANTE_PROFILER */
856 /*******************************************************************************
857 ***** Test Code ****************************************************************
858 *******************************************************************************/