internal CacheEntryBase(MinidumpSegment segmentData, int derivedMinSize, Action <uint> updateOwningCacheForAddedChunk) { _segmentData = segmentData; int pageCount = (int)((_segmentData.End - _segmentData.VirtualAddress) / EntryPageSize); if (((int)(_segmentData.End - _segmentData.VirtualAddress) % EntryPageSize) != 0) { pageCount++; } _pages = new CachePage <T> [pageCount]; _pageLocks = new ReaderWriterLockSlim[pageCount]; for (int i = 0; i < _pageLocks.Length; i++) { _pageLocks[i] = new ReaderWriterLockSlim(); } _minSize = 4 * UIntPtr.Size + /*our four fields that are refrence type fields (pages, pageLocks, segmentData, and updateOwningCacheForAddedChunk)*/ 2 * (_pages.Length * UIntPtr.Size) + /*The array of cache pages and matching size array of locks */ 2 * sizeof(uint) + /*entrySize and minSize fields*/ sizeof(long) /*lastAccessTickCount field*/ + derivedMinSize /*size added from our derived classes bookkeeping overhead*/; _entrySize = _minSize; _updateOwningCacheForAddedChunk = updateOwningCacheForAddedChunk; UpdateLastAccessTickCount(); }
internal ArrayPoolBasedCacheEntry(MemoryMappedFile mappedFile, MinidumpSegment segmentData, Action <ulong, uint> updateOwningCacheForAddedChunk) { _mappedFile = mappedFile; _segmentData = segmentData; int pageCount = (int)((segmentData.End - segmentData.VirtualAddress) / PageSize); if (((int)(segmentData.End - segmentData.VirtualAddress) % PageSize) != 0) { pageCount++; } _dataChunks = new CachePage[pageCount]; _dataChunkLocks = new ReaderWriterLockSlim[pageCount]; for (int i = 0; i < _dataChunkLocks.Length; i++) { _dataChunkLocks[i] = new ReaderWriterLockSlim(); } MinSize = (6 * UIntPtr.Size) + /*our six fields that are reference type fields (updateOwningCacheForAddedChunk, disposeQueue, mappedFile, segmentData, dataChunkLocks, dataChunks)*/ (_dataChunks.Length * UIntPtr.Size) + /*The array of data chunks (each element being a pointer)*/ (_dataChunkLocks.Length * UIntPtr.Size) + /*The array of locks for our data chunks*/ sizeof(int) + /*accessCount field*/ sizeof(uint) + /*entrySize field*/ sizeof(long) /*lastAccessTickCount field*/; _entrySize = MinSize; _updateOwningCacheForAddedChunk = updateOwningCacheForAddedChunk; IncrementAccessCount(); UpdateLastAccessTickCount(); }
private int GetSegmentContaining(ulong address) { int result = -1; int lower = 0; int upper = _segments.Length - 1; while (lower <= upper) { int mid = (lower + upper) >> 1; MinidumpSegment seg = _segments[mid]; if (seg.Contains(address)) { result = mid; break; } if (address < seg.VirtualAddress) { upper = mid - 1; } else { lower = mid + 1; } } return(result); }
internal AWEBasedCacheEntry(MinidumpSegment segmentData, Action <ulong, uint> updateOwningCacheForSizeChangeCallback, UIntPtr pageFrameArray, int pageFrameArrayItemCount) { int pagesSize = (int)(segmentData.Size / VirtualAllocPageSize); if ((segmentData.Size % VirtualAllocPageSize) != 0) { pagesSize++; } _pages = new CachePage[pagesSize]; _pageLocks = new ReaderWriterLockSlim[pagesSize]; for (int i = 0; i < _pageLocks.Length; i++) { _pageLocks[i] = new ReaderWriterLockSlim(); } MinSize = (_pages.Length * UIntPtr.Size) + /*size of pages array*/ (_pageLocks.Length * UIntPtr.Size) + /*size of pageLocks array*/ (_pageFrameArrayItemCount * UIntPtr.Size) + /*size of pageFrameArray*/ (5 * IntPtr.Size) + /*size of reference type fields (updateOwningCacheForSizeChangeCallback, segmentData, pageFrameArray, pageLocks, pages)*/ (2 * sizeof(int)) + /*size of int fields (pageFrameArrayItemCount, accessSize)*/ sizeof(uint) + /*size of uint field (accessCount)*/ sizeof(long); /*size of long field (lasAccessTickCount)*/ _segmentData = segmentData; _updateOwningCacheForSizeChangeCallback = updateOwningCacheForSizeChangeCallback; _pageFrameArray = pageFrameArray; _pageFrameArrayItemCount = pageFrameArrayItemCount; _entrySize = MinSize; IncrementAccessCount(); UpdateLastAccessTickCount(); }
public SegmentCacheEntry CreateAndAddEntry(MinidumpSegment segment) { SegmentCacheEntry entry = _entryFactory.CreateEntryForSegment(segment, UpdateOverallCacheSizeForAddedChunk); _cacheLock.EnterWriteLock(); try { // Check the cache again now that we have acquired the write lock if (_cache.TryGetValue(segment.VirtualAddress, out SegmentCacheEntry? existingEntry)) { // Someone else beat us to adding this entry, clean up the entry we created and return the existing one using (entry as IDisposable) return(existingEntry); } _cache.Add(segment.VirtualAddress, entry); } finally { _cacheLock.ExitWriteLock(); } Interlocked.Add(ref _cacheSize, entry.CurrentSize); TrimCacheIfOverLimit(segment.VirtualAddress); return(entry); }
public override int Read(ulong address, Span <byte> buffer) { if (address == 0) { return(0); } lock (_sync) { try { int bytesRead = 0; while (bytesRead < buffer.Length) { ulong currAddress = address + (uint)bytesRead; int curr = GetSegmentContaining(currAddress); if (curr == -1) { break; } MinidumpSegment seg = _segments[curr]; ulong offset = currAddress - seg.VirtualAddress; Span <byte> slice = buffer.Slice(bytesRead, Math.Min(buffer.Length - bytesRead, (int)(seg.Size - offset))); _stream.Position = (long)(seg.FileOffset + offset); int read = _stream.Read(slice); if (read == 0) { break; } bytesRead += read; } return(bytesRead); } catch (IOException) { return(0); } } }
public SegmentCacheEntry CreateAndAddEntry(MinidumpSegment segment) { ThrowIfDisposed(); if (_cacheIsComplete) { throw new InvalidOperationException($"You cannot call {nameof(CreateAndAddEntry)} after having called {nameof(CreateAndAddEntry)} enough times to cause the entry count to rise to {_entryCountWhenFull}, which was given to the ctor as the largest possible size"); } SegmentCacheEntry entry = _entryFactory.CreateEntryForSegment(segment, UpdateOverallCacheSizeForAddedChunk); if (!_cacheIsFullyPopulatedBeforeUse) { _cacheLock.EnterWriteLock(); } try { // Check the cache again now that we have acquired the write lock if (_cache.TryGetValue(segment.VirtualAddress, out SegmentCacheEntry existingEntry)) { // Someone else beat us to adding this entry, clean up the entry we created and return the existing one using (entry as IDisposable) return(existingEntry); } _cache.Add(segment.VirtualAddress, entry); _cacheIsComplete = (_cache.Count == _entryCountWhenFull); } finally { if (!_cacheIsFullyPopulatedBeforeUse) { _cacheLock.ExitWriteLock(); } } Interlocked.Add(ref _cacheSize, entry.CurrentSize); TrimCacheIfOverLimit(); return(entry); }
public bool Read(ulong address, Span <byte> buffer, out int bytesRead) { lock (_sync) { if (_disposed) { throw new ObjectDisposedException(nameof(MinidumpMemoryReader)); } try { bytesRead = 0; while (bytesRead < buffer.Length) { ulong currAddress = address + (uint)bytesRead; int curr = GetSegmentContaining(currAddress); if (curr == -1) { break; } ref MinidumpSegment seg = ref _segments[curr]; ulong offset = currAddress - seg.VirtualAddress; Span <byte> slice = buffer.Slice(bytesRead, Math.Min(buffer.Length - bytesRead, (int)(seg.Size - offset))); _stream.Position = (long)(seg.FileOffset + offset); int read = _stream.Read(slice); if (read == 0) { break; } bytesRead += read; } return(bytesRead > 0); }
internal AWEBasedCacheEntry(MinidumpSegment segmentData, Action <uint> updateOwningCacheForSizeChangeCallback, UIntPtr pageFrameArray, int pageFrameArrayItemCount) : base(segmentData, derivedMinSize: UIntPtr.Size + (pageFrameArrayItemCount * UIntPtr.Size) + sizeof(int), updateOwningCacheForSizeChangeCallback) { _pageFrameArray = pageFrameArray; _pageFrameArrayItemCount = pageFrameArrayItemCount; }
public abstract SegmentCacheEntry CreateEntryForSegment(MinidumpSegment segmentData, Action <uint> updateOwningCacheForSizeChangeCallback);
public override SegmentCacheEntry CreateEntryForSegment(MinidumpSegment segmentData, Action <uint> updateOwningCacheForSizeChangeCallback) { return(new ArrayPoolBasedCacheEntry(_mappedFile, segmentData, updateOwningCacheForSizeChangeCallback)); }
public override SegmentCacheEntry CreateEntryForSegment(MinidumpSegment segmentData, Action <ulong, 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 ony 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 importatnly, 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); } } }
internal ArrayPoolBasedCacheEntry(MemoryMappedFile mappedFile, MinidumpSegment segmentData, Action <uint> updateOwningCacheForAddedChunk) : base(segmentData, derivedMinSize: 2 * IntPtr.Size, updateOwningCacheForAddedChunk) { _mappedFile = mappedFile; }