public override SegmentCacheEntry CreateEntryForSegment()

in src/Microsoft.Diagnostics.Runtime/src/Windows/AWEBasedCacheEntryFactory.cs [40:118]


        public override SegmentCacheEntry CreateEntryForSegment(MinidumpSegment segmentData, Action<uint> updateOwningCacheForSizeChangeCallback)
        {
            bool setFPRes = CacheNativeMethods.File.SetFilePointerEx(_dumpFileHandle, (long)segmentData.FileOffset, SeekOrigin.Begin);
            if (!setFPRes)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            uint numberOfPages = (uint)(segmentData.Size / (ulong)Environment.SystemPageSize);
            if ((segmentData.Size % (ulong)Environment.SystemPageSize) != 0)
                numberOfPages++;

            uint bytesNeededForPageArray = numberOfPages * (uint)IntPtr.Size;
            UIntPtr pageFrameArray = CacheNativeMethods.Memory.HeapAlloc(bytesNeededForPageArray);
            uint numberOfPagesAllocated = numberOfPages;
            bool handedAllocationsToCacheEntry = false;

            // Allocate the physical memory for our pages and store the mapping data into our page frame array
            try
            {
                // Allocate the physical memory, this claims this much physical memory but it does not yet count against our process usage limits
                bool physicalAllocRes = CacheNativeMethods.AWE.AllocateUserPhysicalPages(ref numberOfPagesAllocated, pageFrameArray);
                if (!physicalAllocRes)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if (numberOfPagesAllocated != numberOfPages)
                    throw new OutOfMemoryException("Failed to allocate the required number of pages for segment in AWE based cache.");

                UIntPtr reservedMemory = UIntPtr.Zero;
                try
                {
                    // Now reserve a chunk of VM equivalent in size to the physical memory, this will now count against our process usage limits, but only temporarily
                    reservedMemory = _sharedSegment != UIntPtr.Zero ? _sharedSegment : CacheNativeMethods.Memory.VirtualAlloc((uint)segmentData.Size,
                                                                                                                                      CacheNativeMethods.Memory.VirtualAllocType.Reserve | CacheNativeMethods.Memory.VirtualAllocType.Physical,
                                                                                                                                      CacheNativeMethods.Memory.MemoryProtection.ReadWrite);

                    // Now assign our previously reserved physical memory to the VM range we just reserved
                    bool mapPhysicalPagesResult = CacheNativeMethods.AWE.MapUserPhysicalPages(reservedMemory, numberOfPages, pageFrameArray);
                    if (!mapPhysicalPagesResult)
                        throw new Win32Exception(Marshal.GetLastWin32Error());

                    // Now that the physical memory is mapped into our VM space, fill it with data from the heap segment from the dump
                    bool readFileRes = CacheNativeMethods.File.ReadFile(_dumpFileHandle, reservedMemory, (uint)segmentData.Size, out uint bytesRead);
                    if (!readFileRes)
                        throw new Win32Exception(Marshal.GetLastWin32Error());

                    // Now for the magic, this line unmaps the physical memory from our process, it still counts against our process limits (until the VirtualFree below). Once we
                    // VirtualFree it below the memory 'cost' still is assigned to our process BUT it doesn't count against our resource limits until/unless we map it back into the VM space.
                    // BUT, most importantly, the physical memory remains and contains the data we read from the file.
                    bool unmapPhysicalPagesResult = CacheNativeMethods.AWE.MapUserPhysicalPages(reservedMemory, numberOfPages, UIntPtr.Zero);
                    if (!unmapPhysicalPagesResult)
                        throw new Win32Exception(Marshal.GetLastWin32Error());

                    if (_sharedSegment != reservedMemory)
                    {
                        // Free the virtual memory we were using to map the physical memory. NOTE: sizeToFree must be 0 when we are calling with VirtualFreeType.Release
                        bool virtualFreeRes = CacheNativeMethods.Memory.VirtualFree(reservedMemory, sizeToFree: UIntPtr.Zero, CacheNativeMethods.Memory.VirtualFreeType.Release);
                        if (!virtualFreeRes)
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                    }

                    reservedMemory = UIntPtr.Zero;

                    // Now give our page frame table to the AWE cache node so it can map the memory back into our VM as needed
                    handedAllocationsToCacheEntry = true;
                    return new AWEBasedCacheEntry(segmentData, updateOwningCacheForSizeChangeCallback, pageFrameArray, (int)numberOfPages);
                }
                finally
                {
                    // Something failed, clean up if we allocated memory
                    if (!handedAllocationsToCacheEntry && (reservedMemory != UIntPtr.Zero) && (_sharedSegment != reservedMemory))
                        CacheNativeMethods.Memory.VirtualFree(reservedMemory, UIntPtr.Zero, CacheNativeMethods.Memory.VirtualFreeType.Release);
                }
            }
            finally
            {
                // Something failed, clean up
                if (!handedAllocationsToCacheEntry)
                    CacheNativeMethods.AWE.FreeUserPhysicalPages(ref numberOfPagesAllocated, pageFrameArray);
            }
        }