ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

heap.c
Go to the documentation of this file.
00001 /*
00002  *  FreeLoader
00003  *  Copyright (C) 2011 Timo Kreuzer (timo.kreuzer@reactos.org)
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License along
00016  *  with this program; if not, write to the Free Software Foundation, Inc.,
00017  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00018  */
00019 
00020 #include <freeldr.h>
00021 #include <debug.h>
00022 
00023 #define FREELDR_HEAP_VERIFIER
00024 
00025 DBG_DEFAULT_CHANNEL(HEAP);
00026 
00027 #define DEFAULT_HEAP_SIZE (1024 * 1024)
00028 #define TEMP_HEAP_SIZE (1024 * 1024)
00029 
00030 #define REDZONE_MARK 0xCCCCCCCCCCCCCCCCULL
00031 #define REDZONE_ALLOCATION 24
00032 #define REDZONE_LOW_OFFSET 16
00033 #define REDZONE_SIZE(Block) ((ULONG64*)Block->Data)
00034 #define REDZONE_LOW(Block) ((ULONG64*)Block->Data + 1)
00035 #define REDZONE_HI(Block) ((ULONG64*)((PUCHAR)Block->Data + 16 + *REDZONE_SIZE(Block)))
00036 
00037 PVOID FrLdrDefaultHeap;
00038 PVOID FrLdrTempHeap;
00039 
00040 typedef struct _BLOCK_DATA
00041 {
00042     ULONG_PTR Flink:32;
00043     ULONG_PTR Blink:32;
00044 } BLOCK_DATA, *PBLOCK_DATA;
00045 
00046 typedef struct _HEAP_BLOCK
00047 {
00048     USHORT Size;
00049     USHORT PreviousSize;
00050     ULONG Tag;
00051     BLOCK_DATA Data[];
00052 } HEAP_BLOCK, *PHEAP_BLOCK;
00053 
00054 typedef struct _HEAP
00055 {
00056     SIZE_T MaximumSize;
00057     SIZE_T CurrentAllocBytes;
00058     SIZE_T MaxAllocBytes;
00059     ULONG NumAllocs;
00060     ULONG NumFrees;
00061     SIZE_T LargestAllocation;
00062     ULONGLONG AllocationTime;
00063     ULONGLONG FreeTime;
00064     ULONG_PTR TerminatingBlock;
00065     HEAP_BLOCK Blocks;
00066 } HEAP, *PHEAP;
00067 
00068 PVOID
00069 HeapCreate(
00070     SIZE_T MaximumSize,
00071     TYPE_OF_MEMORY MemoryType)
00072 {
00073     PHEAP Heap;
00074     PHEAP_BLOCK Block;
00075     SIZE_T Remaining;
00076     USHORT PreviousSize;
00077     TRACE("HeapCreate(MemoryType=%ld)\n", MemoryType);
00078 
00079     /* Allocate some memory for the heap */
00080     MaximumSize = ALIGN_UP_BY(MaximumSize, MM_PAGE_SIZE);
00081     Heap = MmAllocateMemoryWithType(MaximumSize, MemoryType);
00082     if (!Heap)
00083     {
00084         ERR("HEAP: Failed to allocate heap of size 0x%lx, Type\n",
00085             MaximumSize, MemoryType);
00086         return NULL;
00087     }
00088 
00089     /* Initialize the heap header */
00090     Heap->MaximumSize = MaximumSize;
00091     Heap->CurrentAllocBytes = 0;
00092     Heap->MaxAllocBytes = 0;
00093     Heap->NumAllocs = 0;
00094     Heap->NumFrees = 0;
00095     Heap->LargestAllocation = 0;
00096 
00097     /* Calculate what's left to process */
00098     Remaining = (MaximumSize - sizeof(HEAP)) / sizeof(HEAP_BLOCK);
00099     TRACE("Remaining = %ld\n", Remaining);
00100 
00101     /* Substract 2 for the terminating entry (header + free entry) */
00102     Remaining -= 2;
00103 
00104     Block = &Heap->Blocks;
00105     PreviousSize = 0;
00106 
00107     /* Create free blocks */
00108     while (Remaining > 1)
00109     {
00110         /* Initialize this free block */
00111         Block->Size = (USHORT)min(MAXUSHORT, Remaining - 1);
00112         Block->PreviousSize = PreviousSize;
00113         Block->Tag = 0;
00114         Block->Data[0].Flink = (Block - &Heap->Blocks) + Block->Size + 1;
00115         Block->Data[0].Blink = (Block - &Heap->Blocks) - 1 - PreviousSize;
00116 
00117         /* Substract current block size from remainder */
00118         Remaining -= (Block->Size + 1);
00119 
00120         /* Go to next block */
00121         PreviousSize = Block->Size;
00122         Block = Block + Block->Size + 1;
00123 
00124         TRACE("Remaining = %ld\n", Remaining);
00125     }
00126 
00127     /* Now finish with a terminating block */
00128     Heap->TerminatingBlock = Block - &Heap->Blocks;
00129     Block->Size = 0;
00130     Block->PreviousSize = PreviousSize;
00131     Block->Tag = 'dnE#';
00132     Block->Data[0].Flink = 0;
00133     Block->Data[0].Blink = (Block - &Heap->Blocks) - 1 - PreviousSize;
00134     Heap->Blocks.Data[0].Blink = Heap->TerminatingBlock;
00135 
00136     return Heap;
00137 }
00138 
00139 VOID
00140 HeapDestroy(
00141     PVOID HeapHandle)
00142 {
00143     PHEAP Heap = HeapHandle;
00144 
00145     /* Mark all pages as firmware temporary, so they are free for the kernel */
00146     MmMarkPagesInLookupTable(PageLookupTableAddress,
00147                              (ULONG_PTR)Heap / MM_PAGE_SIZE,
00148                              (PFN_COUNT)(Heap->MaximumSize / MM_PAGE_SIZE),
00149                              LoaderFirmwareTemporary);
00150 }
00151 
00152 VOID
00153 HeapRelease(
00154     PVOID HeapHandle)
00155 {
00156     PHEAP Heap = HeapHandle;
00157     PHEAP_BLOCK Block;
00158     PUCHAR StartAddress, EndAddress;
00159     PFN_COUNT FreePages, AllFreePages = 0;
00160     TRACE("HeapRelease(%p)\n", HeapHandle);
00161 
00162     /* Loop all heap chunks */
00163     for (Block = &Heap->Blocks;
00164          Block->Size != 0;
00165          Block = Block + 1 + Block->Size)
00166     {
00167         /* Continue, if its not free */
00168         if (Block->Tag != 0)
00169         {
00170 #ifdef FREELDR_HEAP_VERIFIER
00171             /* Verify size and redzones */
00172             ASSERT(*REDZONE_SIZE(Block) <= Block->Size * sizeof(HEAP_BLOCK));
00173             ASSERT(*REDZONE_LOW(Block) == REDZONE_MARK);
00174             ASSERT(*REDZONE_HI(Block) == REDZONE_MARK);
00175 #endif
00176             continue;
00177         }
00178 
00179         /* Calculate page aligned start address of the free region */
00180         StartAddress = ALIGN_UP_POINTER_BY(Block->Data, PAGE_SIZE);
00181 
00182         /* Walk over adjacent free blocks */
00183         while (Block->Tag == 0) Block = Block + Block->Size + 1;
00184 
00185         /* Check if this was the last block */
00186         if (Block->Size == 0)
00187         {
00188             /* Align the end address up to cover the end of the heap */
00189             EndAddress = ALIGN_UP_POINTER_BY(Block->Data, PAGE_SIZE);
00190         }
00191         else
00192         {
00193             /* Align the end address down to not cover any allocations */
00194             EndAddress = ALIGN_DOWN_POINTER_BY(Block->Data, PAGE_SIZE);
00195         }
00196 
00197         /* Check if we have free pages */
00198         if (EndAddress > StartAddress)
00199         {
00200             /* Calculate the size of the free region in pages */
00201             FreePages = (PFN_COUNT)((EndAddress - StartAddress) / MM_PAGE_SIZE);
00202             AllFreePages += FreePages;
00203 
00204             /* Now mark the pages free */
00205             MmMarkPagesInLookupTable(PageLookupTableAddress,
00206                                      (ULONG_PTR)StartAddress / MM_PAGE_SIZE,
00207                                      FreePages,
00208                                      LoaderFree);
00209         }
00210 
00211         /* bail out, if it was the last block */
00212         if (Block->Size == 0) break;
00213     }
00214 
00215     TRACE("HeapRelease() done, freed %ld pages\n", AllFreePages);
00216 }
00217 
00218 VOID
00219 HeapCleanupAll(VOID)
00220 {
00221     PHEAP Heap;
00222 
00223     Heap = FrLdrDefaultHeap;
00224     TRACE("Heap statistics for default heap:\n"
00225           "CurrentAlloc=0x%lx, MaxAlloc=0x%lx, LargestAllocation=0x%lx\n"
00226           "NumAllocs=%ld, NumFrees=%ld\n",
00227           Heap->CurrentAllocBytes, Heap->MaxAllocBytes, Heap->LargestAllocation,
00228           Heap->NumAllocs, Heap->NumFrees);
00229     TRACE("AllocTime = %I64d, FreeTime = %I64d, sum = %I64d\n",
00230         Heap->AllocationTime, Heap->FreeTime, Heap->AllocationTime + Heap->FreeTime);
00231 
00232 
00233     /* Release fre pages */
00234     HeapRelease(FrLdrDefaultHeap);
00235 
00236     Heap = FrLdrTempHeap;
00237     TRACE("Heap statistics for temp heap:\n"
00238           "CurrentAlloc=0x%lx, MaxAlloc=0x%lx, LargestAllocation=0x%lx\n"
00239           "NumAllocs=%ld, NumFrees=%ld\n",
00240           Heap->CurrentAllocBytes, Heap->MaxAllocBytes, Heap->LargestAllocation,
00241           Heap->NumAllocs, Heap->NumFrees);
00242 
00243     /* Destroy the heap */
00244     HeapDestroy(FrLdrTempHeap);
00245 }
00246 
00247 static VOID
00248 HeapRemoveFreeList(
00249     PHEAP Heap,
00250     PHEAP_BLOCK Block)
00251 {
00252     PHEAP_BLOCK Previous, Next;
00253 
00254     Next = &Heap->Blocks + Block->Data[0].Flink;
00255     Previous = &Heap->Blocks + Block->Data[0].Blink;
00256     ASSERT((Next->Tag == 0) || (Next->Tag == 'dnE#'));
00257     ASSERT(Next->Data[0].Blink == Block - &Heap->Blocks);
00258     ASSERT((Previous->Tag == 0) || (Previous->Tag == 'dnE#'));
00259     ASSERT(Previous->Data[0].Flink == Block - &Heap->Blocks);
00260 
00261     Next->Data[0].Blink = Previous - &Heap->Blocks;
00262     Previous->Data[0].Flink = Next - &Heap->Blocks;
00263 }
00264 
00265 static VOID
00266 HeapInsertFreeList(
00267     PHEAP Heap,
00268     PHEAP_BLOCK FreeBlock)
00269 {
00270     PHEAP_BLOCK ListHead, NextBlock;
00271     ASSERT(FreeBlock->Tag == 0);
00272 
00273     /* Terminating block serves as free list head */
00274     ListHead = &Heap->Blocks + Heap->TerminatingBlock;
00275 
00276     for (NextBlock = &Heap->Blocks + ListHead->Data[0].Flink;
00277          NextBlock < FreeBlock;
00278          NextBlock = &Heap->Blocks + NextBlock->Data[0].Flink);
00279 
00280     FreeBlock->Data[0].Flink = NextBlock - &Heap->Blocks;
00281     FreeBlock->Data[0].Blink = NextBlock->Data[0].Blink;
00282     NextBlock->Data[0].Blink = FreeBlock - &Heap->Blocks;
00283     NextBlock = &Heap->Blocks + FreeBlock->Data[0].Blink;
00284     NextBlock->Data[0].Flink = FreeBlock - &Heap->Blocks;
00285 }
00286 
00287 PVOID
00288 HeapAllocate(
00289     PVOID HeapHandle,
00290     SIZE_T ByteSize,
00291     ULONG Tag)
00292 {
00293     PHEAP Heap = HeapHandle;
00294     PHEAP_BLOCK Block, NextBlock;
00295     USHORT BlockSize, Remaining;
00296     ULONGLONG Time = __rdtsc();
00297 
00298 #ifdef FREELDR_HEAP_VERIFIER
00299     /* Add space for a size field and 2 redzones */
00300     ByteSize += REDZONE_ALLOCATION;
00301 #endif
00302 
00303     /* Check if the allocation is too large */
00304     if ((ByteSize +  sizeof(HEAP_BLOCK)) > MAXUSHORT * sizeof(HEAP_BLOCK))
00305     {
00306         ERR("HEAP: Allocation of 0x%lx bytes too large\n", ByteSize);
00307         return NULL;
00308     }
00309 
00310     /* We need a proper tag */
00311     if (Tag == 0) Tag = 'enoN';
00312 
00313     /* Calculate alloc size */
00314     BlockSize = (USHORT)((ByteSize + sizeof(HEAP_BLOCK) - 1) / sizeof(HEAP_BLOCK));
00315 
00316     /* Walk the free block list */
00317     Block = &Heap->Blocks + Heap->TerminatingBlock;
00318     for (Block = &Heap->Blocks + Block->Data[0].Flink;
00319          Block->Size != 0;
00320          Block = &Heap->Blocks + Block->Data[0].Flink)
00321     {
00322         ASSERT(Block->Tag == 0);
00323 
00324         /* Continue, if its too small */
00325         if (Block->Size < BlockSize) continue;
00326 
00327         /* This block is just fine, use it */
00328         Block->Tag = Tag;
00329 
00330         /* Remove this entry from the free list */
00331         HeapRemoveFreeList(Heap, Block);
00332 
00333         /* Calculate the remaining size */
00334         Remaining = Block->Size - BlockSize;
00335 
00336         /* Check if the remaining space is large enough for a new block */
00337         if (Remaining > 1)
00338         {
00339             /* Make the allocated block as large as neccessary */
00340             Block->Size = BlockSize;
00341 
00342             /* Get pointer to the new block */
00343             NextBlock = Block + 1 + BlockSize;
00344 
00345             /* Make it a free block */
00346             NextBlock->Tag = 0;
00347             NextBlock->Size = Remaining - 1;
00348             NextBlock->PreviousSize = BlockSize;
00349             BlockSize = NextBlock->Size;
00350             HeapInsertFreeList(Heap, NextBlock);
00351 
00352             /* Advance to the next block */
00353             NextBlock = NextBlock + 1 + BlockSize;
00354         }
00355         else
00356         {
00357             /* Not enough left, use the full block */
00358             BlockSize = Block->Size;
00359 
00360             /* Get the next block */
00361             NextBlock = Block + 1 + BlockSize;
00362         }
00363 
00364         /* Update the next blocks back link */
00365         NextBlock->PreviousSize = BlockSize;
00366 
00367         /* Update heap usage */
00368         Heap->NumAllocs++;
00369         Heap->CurrentAllocBytes += Block->Size * sizeof(HEAP_BLOCK);
00370         Heap->MaxAllocBytes = max(Heap->MaxAllocBytes, Heap->CurrentAllocBytes);
00371         Heap->LargestAllocation = max(Heap->LargestAllocation,
00372                                       Block->Size * sizeof(HEAP_BLOCK));
00373         Heap->AllocationTime += (__rdtsc() - Time);
00374 
00375         TRACE("HeapAllocate(%p, %ld, %.4s) -> return %p\n",
00376               HeapHandle, ByteSize, &Tag, Block->Data);
00377 
00378         /* HACK: zero out the allocation */
00379         RtlZeroMemory(Block->Data, Block->Size * sizeof(HEAP_BLOCK));
00380 
00381 #ifdef FREELDR_HEAP_VERIFIER
00382         /* Write size and redzones */
00383         *REDZONE_SIZE(Block) = ByteSize - REDZONE_ALLOCATION;
00384         *REDZONE_LOW(Block) = REDZONE_MARK;
00385         *REDZONE_HI(Block) = REDZONE_MARK;
00386 
00387         /* Allcoation starts after size field and redzone */
00388         return (PUCHAR)Block->Data + REDZONE_LOW_OFFSET;
00389 #endif
00390         /* Return pointer to the data */
00391         return Block->Data;
00392     }
00393 
00394     /* We found nothing */
00395     WARN("HEAP: nothing suitable found for 0x%lx bytes\n", ByteSize);
00396     return NULL;
00397 }
00398 
00399 VOID
00400 HeapFree(
00401     PVOID HeapHandle,
00402     PVOID Pointer,
00403     ULONG Tag)
00404 {
00405     PHEAP Heap = HeapHandle;
00406     PHEAP_BLOCK Block, PrevBlock, NextBlock;
00407     ULONGLONG Time = __rdtsc();
00408     TRACE("HeapFree(%p, %p)\n", HeapHandle, Pointer);
00409     ASSERT(Tag != 'dnE#');
00410 
00411     /* Check if the block is really inside this heap */
00412     if ((Pointer < (PVOID)(Heap + 1)) ||
00413         (Pointer > (PVOID)((PUCHAR)Heap + Heap->MaximumSize)))
00414     {
00415         ERR("HEAP: trying to free %p outside of heap %p\n", Pointer, Heap);
00416         ASSERT(FALSE);
00417     }
00418 
00419     Block = ((PHEAP_BLOCK)Pointer) - 1;
00420 #ifdef FREELDR_HEAP_VERIFIER
00421     Block = (PHEAP_BLOCK)((PUCHAR)Block - REDZONE_LOW_OFFSET);
00422 
00423     /* Verify size and redzones */
00424     ASSERT(*REDZONE_SIZE(Block) <= Block->Size * sizeof(HEAP_BLOCK));
00425     ASSERT(*REDZONE_LOW(Block) == REDZONE_MARK);
00426     ASSERT(*REDZONE_HI(Block) == REDZONE_MARK);
00427 #endif
00428 
00429     /* Check if the tag matches */
00430     if ((Tag && (Block->Tag != Tag)) || (Block->Tag == 0))
00431     {
00432         ERR("HEAP: Bad tag! Pointer=%p: block tag '%.4s', requested '%.4s', size=0x%lx\n",
00433             Pointer, &Block->Tag, &Tag, Block->Size);
00434         ASSERT(FALSE);
00435     }
00436 
00437     /* Mark as free */
00438     Block->Tag = 0;
00439 
00440     /* Update heap usage */
00441     Heap->NumFrees++;
00442     Heap->CurrentAllocBytes -= Block->Size * sizeof(HEAP_BLOCK);
00443 
00444     /* Get pointers to the next and previous block */
00445     PrevBlock = Block - Block->PreviousSize - 1;
00446     NextBlock = Block + Block->Size + 1;
00447 
00448     /* Check if next block is free */
00449     if ((NextBlock->Tag == 0) &&
00450         ((Block->Size + NextBlock->Size + 1) <= MAXUSHORT))
00451     {
00452         /* Merge next block into current */
00453         Block->Size += NextBlock->Size + 1;
00454         HeapRemoveFreeList(Heap, NextBlock);
00455 
00456         NextBlock = Block + Block->Size + 1;
00457     }
00458 
00459     /* Check if there is a block before and it's free */
00460     if ((Block->PreviousSize != 0) && (PrevBlock->Tag == 0) &&
00461         ((PrevBlock->Size + Block->Size + 1) <= MAXUSHORT))
00462     {
00463         /* Merge current block into previous */
00464         PrevBlock->Size += Block->Size + 1;
00465         Block = PrevBlock;
00466     }
00467     else
00468     {
00469         /* Insert the entry into the free list */
00470         HeapInsertFreeList(Heap, Block);
00471     }
00472 
00473     /* Update the next block's back link */
00474     NextBlock->PreviousSize = Block->Size;
00475 
00476     Heap->FreeTime += (__rdtsc() - Time);
00477 }
00478 
00479 
00480 /* Wrapper functions *********************************************************/
00481 
00482 VOID
00483 MmInitializeHeap(PVOID PageLookupTable)
00484 {
00485     TRACE("MmInitializeHeap()\n");
00486 
00487     /* Create the default heap */
00488     FrLdrDefaultHeap = HeapCreate(DEFAULT_HEAP_SIZE, LoaderOsloaderHeap);
00489     ASSERT(FrLdrDefaultHeap);
00490 
00491     /* Create a temporary heap */
00492     FrLdrTempHeap = HeapCreate(TEMP_HEAP_SIZE, LoaderFirmwareTemporary);
00493     ASSERT(FrLdrTempHeap);
00494 
00495     TRACE("MmInitializeHeap() done, default heap %p, temp heap %p\n",
00496           FrLdrDefaultHeap, FrLdrTempHeap);
00497 }
00498 
00499 PVOID
00500 MmHeapAlloc(SIZE_T MemorySize)
00501 {
00502     return HeapAllocate(FrLdrDefaultHeap, MemorySize, 'pHmM');
00503 }
00504 
00505 VOID
00506 MmHeapFree(PVOID MemoryPointer)
00507 {
00508     HeapFree(FrLdrDefaultHeap, MemoryPointer, 'pHmM');
00509 }
00510 
00511 PVOID
00512 NTAPI
00513 ExAllocatePoolWithTag(
00514     IN POOL_TYPE PoolType,
00515     IN SIZE_T NumberOfBytes,
00516     IN ULONG Tag)
00517 {
00518     return HeapAllocate(FrLdrDefaultHeap, NumberOfBytes, Tag);
00519 }
00520 
00521 PVOID
00522 NTAPI
00523 ExAllocatePool(
00524     IN POOL_TYPE PoolType,
00525     IN SIZE_T NumberOfBytes)
00526 {
00527     return HeapAllocate(FrLdrDefaultHeap, NumberOfBytes, 0);
00528 }
00529 
00530 VOID
00531 NTAPI
00532 ExFreePool(
00533     IN PVOID P)
00534 {
00535     HeapFree(FrLdrDefaultHeap, P, 0);
00536 }
00537 
00538 VOID
00539 NTAPI
00540 ExFreePoolWithTag(
00541   IN PVOID P,
00542   IN ULONG Tag)
00543 {
00544     HeapFree(FrLdrDefaultHeap, P, Tag);
00545 }
00546 
00547 PVOID
00548 NTAPI
00549 RtlAllocateHeap(
00550     IN PVOID HeapHandle,
00551     IN ULONG Flags,
00552     IN SIZE_T Size)
00553 {
00554     PVOID ptr;
00555 
00556     ptr = HeapAllocate(FrLdrDefaultHeap, Size, ' ltR');
00557     if (ptr && (Flags & HEAP_ZERO_MEMORY))
00558     {
00559         RtlZeroMemory(ptr, Size);
00560     }
00561 
00562     return ptr;
00563 }
00564 
00565 BOOLEAN
00566 NTAPI
00567 RtlFreeHeap(
00568     IN PVOID HeapHandle,
00569     IN ULONG Flags,
00570     IN PVOID HeapBase)
00571 {
00572     HeapFree(FrLdrDefaultHeap, HeapBase, ' ltR');
00573     return TRUE;
00574 }
00575 

Generated on Sun May 27 2012 04:19:16 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.