| File: Contracts\StackWalk\GC\GCInteriorPointerResolver.cs | Web Access |
| Project: Microsoft.Diagnostics.DataContractReader.Contracts.csproj (Microsoft.Diagnostics.DataContractReader.Contracts) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Diagnostics.DataContractReader.Contracts.Extensions; namespace Microsoft.Diagnostics.DataContractReader.Contracts.StackWalkHelpers; // Resolves an address that may point into the interior of a managed object to the address of the // containing object by linearly walking the GC heap segment objects. This is used by GcScanContext // to resolve interior stack roots and by RefWalk to resolve external memory handle roots. public sealed class GCInteriorPointerResolver { private readonly IGC _gc; private readonly IRuntimeTypeSystem _rts; private readonly LinearReadCache _cache; private readonly uint _numComponentsOffsetArray; private readonly uint _numComponentsOffsetString; private readonly ulong _methodTableOffset; private readonly byte _objectToMethodTableUnmask; public GCInteriorPointerResolver(Target target) { _gc = target.Contracts.GC; _rts = target.Contracts.RuntimeTypeSystem; _cache = new LinearReadCache(target); _numComponentsOffsetArray = (uint)Data.Array.GetNumComponentsOffset(target); _numComponentsOffsetString = (uint)Data.String.GetStringLengthOffset(target); _methodTableOffset = (ulong)Data.Object.GetMethodTableOffset(target); _objectToMethodTableUnmask = target.ReadGlobal<byte>(Constants.Globals.ObjectToMethodTableUnmask); } // Resolves obj (an address that may point anywhere within a managed object) to the address of // the object that contains it, or TargetPointer.Null if obj does not fall within a live object // on any GC heap segment (including if obj is itself null/invalid, or the heap data is corrupt). public TargetPointer Resolve(TargetPointer obj) { TargetPointer outerObj = TargetPointer.Null; foreach ((GCHeapSegmentInfo seg, GCHeapData _) in _gc.EnumerateAllSegments()) { if (obj.Value < seg.Start.Value || obj.Value >= seg.End.Value) continue; TargetPointer currentObj = _gc.GetPotentialNextObjectAddress(seg.Start, 0, seg); ulong size = 0; while (currentObj.Value <= obj.Value) { // Replicate IObject.GetMethodTableAddress in fast path with linear read cache if (!_cache.TryReadPointer(currentObj.Value + _methodTableOffset, out TargetPointer mt)) { return TargetPointer.Null; } mt = mt.Value & (ulong)~_objectToMethodTableUnmask; // Replicate IObject.GetSize in fast path with linear read cache if (!TryGetObjectSize(currentObj, mt, out size) || size == 0) { return TargetPointer.Null; } size = _gc.AlignObjectSize(size, seg.Generation); if (currentObj.Value + size > seg.End.Value || size == 0) { return TargetPointer.Null; } outerObj = currentObj; currentObj = _gc.GetPotentialNextObjectAddress(currentObj, size, seg); } return outerObj + size > obj ? outerObj : TargetPointer.Null; } return outerObj; } private bool TryGetObjectSize(TargetPointer objAddr, TargetPointer mt, out ulong size) { size = 0; try { ITypeHandle handle = _rts.GetTypeHandle(mt); ulong baseSize = _rts.GetBaseSize(handle); uint componentSize = _rts.GetComponentSize(handle); uint numComponentsOffset = 0; if (componentSize != 0) { if (_rts.IsArray(handle, out _) || _rts.IsFreeObjectMethodTable(handle)) numComponentsOffset = _numComponentsOffsetArray; else if (_rts.IsString(handle)) numComponentsOffset = _numComponentsOffsetString; else return false; // unrecognized component type if (!_cache.TryReadUInt32(objAddr.Value + numComponentsOffset, out uint numComponents)) return false; baseSize += (ulong)componentSize * numComponents; } size = baseSize; return true; } catch { // The MT may be corrupt — surface as a read failure. return false; } } }