public override ulong GetPreviousObjectAddress(ulong addr) { if (!ObjectRange.Contains(addr)) { throw new InvalidOperationException($"Segment [{FirstObjectAddress:x},{CommittedMemory:x}] does not contain address {addr:x}"); } if (addr == FirstObjectAddress) { return(0); } // Default to the start of the segment ulong prevAddr = FirstObjectAddress; // Look for markers that are closer to the address. We keep the size of _markers small so a linear walk // should be roughly as fast as a binary search. foreach (ulong marker in _markers) { // Markers can be 0 even when _markers was fully initialized by a full heap walk. This is because parts of // the ephemeral GC heap may be not in use (allocation contexts) or when objects on the large object heap // are so big that there's simply not a valid object starting point in that range. if (marker != 0) { if (marker >= addr) { break; } prevAddr = marker; } } // Linear walk from the last known good previous address to the one we are looking for. // This could take a while if we don't know a close enough address. ulong curr = prevAddr; while (curr != 0 && curr <= addr) { ulong next = GetNextObjectAddress(curr); if (next >= addr) { return(curr); } curr = next; } return(0); }
public override IEnumerable <ClrObject> EnumerateObjects() { bool large = IsLargeObjectSegment; uint minObjSize = (uint)IntPtr.Size * 3; ulong obj = FirstObjectAddress; IDataReader dataReader = _helpers.DataReader; // C# isn't smart enough to understand that !large means memoryReader is non-null. We will just be // careful here. using MemoryReader memoryReader = (!large ? new MemoryReader(dataReader, 0x10000) : null) !; byte[] buffer = ArrayPool <byte> .Shared.Rent(IntPtr.Size * 2 + sizeof(uint)); // The large object heap if (!large) { memoryReader.EnsureRangeInCache(obj); } while (ObjectRange.Contains(obj)) { ulong mt; if (large) { if (dataReader.Read(obj, buffer) != buffer.Length) { break; } mt = Unsafe.As <byte, nuint>(ref buffer[0]); } else { if (!memoryReader.ReadPtr(obj, out mt)) { break; } } ClrType?type = _helpers.Factory.GetOrCreateType(_clrmdHeap, mt, obj); if (type is null) { break; } int marker = GetMarkerIndex(obj); if (marker != -1 && _markers[marker] == 0) { _markers[marker] = obj; } ClrObject result = new ClrObject(obj, type); yield return(result); ulong size; if (type.ComponentSize == 0) { size = (uint)type.StaticSize; } else { uint count; if (large) { count = Unsafe.As <byte, uint>(ref buffer[IntPtr.Size]); } else { memoryReader.ReadDword(obj + (uint)IntPtr.Size, out count); } // Strings in v4+ contain a trailing null terminator not accounted for. if (_clrmdHeap.StringType == type) { count++; } size = count * (ulong)type.ComponentSize + (ulong)type.StaticSize; } size = ClrmdHeap.Align(size, large); if (size < minObjSize) { size = minObjSize; } obj += size; obj = _clrmdHeap.SkipAllocationContext(this, obj); } ArrayPool <byte> .Shared.Return(buffer); }
public override ulong GetNextObjectAddress(ulong addr) { if (addr == 0) { return(0); } if (!ObjectRange.Contains(addr)) { throw new InvalidOperationException($"Segment [{FirstObjectAddress:x},{CommittedMemory:x}] does not contain object {addr:x}"); } bool large = IsLargeObjectSegment; uint minObjSize = (uint)IntPtr.Size * 3; IMemoryReader memoryReader = _helpers.DataReader; ulong mt = memoryReader.ReadPointer(addr); ClrType?type = _helpers.Factory.GetOrCreateType(Heap, mt, addr); if (type is null) { return(0); } ulong size; if (type.ComponentSize == 0) { size = (uint)type.StaticSize; } else { uint count = memoryReader.Read <uint>(addr + (uint)IntPtr.Size); // Strings in v4+ contain a trailing null terminator not accounted for. if (Heap.StringType == type) { count++; } size = count * (ulong)type.ComponentSize + (ulong)type.StaticSize; } size = ClrmdHeap.Align(size, large); if (size < minObjSize) { size = minObjSize; } ulong obj = addr + size; if (!large) { obj = _clrmdHeap.SkipAllocationContext(this, obj); // ignore mt here because it won't be used } if (obj >= End) { return(0); } int marker = GetMarkerIndex(obj); if (marker != -1 && _markers[marker] == 0) { _markers[marker] = obj; } return(obj); }