| File: Dbi\Helpers\RefWalk.cs | Web Access |
| Project: src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Legacy\Microsoft.Diagnostics.DataContractReader.Legacy.csproj (Microsoft.Diagnostics.DataContractReader.Legacy) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using Microsoft.Diagnostics.DataContractReader.Contracts; namespace Microsoft.Diagnostics.DataContractReader.Legacy; /// <summary> /// cDAC port of the native DacRefWalker. /// </summary> internal sealed class RefWalk : IEnum<DacGcReference> { private const uint CDAC_DEFERRED_FRAME = 0x40000000; private readonly Target _target; private readonly IGC _gc; private readonly bool _walkStacks; private readonly CorGCReferenceType _handleWalkMask; private readonly TargetPointer _appDomain; public IEnumerator<DacGcReference> Enumerator { get; } public nuint LegacyHandle { get; set; } = 0; public RefWalk(Target target, bool walkStacks, CorGCReferenceType handleWalkMask) { _target = target; _gc = target.Contracts.GC; _walkStacks = walkStacks; _handleWalkMask = handleWalkMask; _appDomain = target.Contracts.Loader.GetAppDomain(); Enumerator = Walk().GetEnumerator(); } private IEnumerable<DacGcReference> Walk() { if (_handleWalkMask != 0) { foreach (DacGcReference reference in WalkHandles()) yield return reference; } if (_walkStacks) { foreach (DacGcReference reference in WalkStacks()) yield return reference; } } private IEnumerable<DacGcReference> WalkHandles() { HandleType[] requestedTypes = GetRequestedHandleTypes(); if (requestedTypes.Length == 0) yield break; foreach (HandleData handle in _gc.GetHandles(requestedTypes)) { if (!TryMapHandle(handle, out CorGCReferenceType dwType, out ulong extraData)) continue; yield return new DacGcReference { vmDomain = _appDomain.Value, objHnd = handle.Handle.Value, dwType = dwType, i64ExtraData = extraData, }; } } private HandleType[] GetRequestedHandleTypes() { // Mirror native DacRefWalker::GetHandleWalkerMask: translate the CorGCReferenceType bits // in the mask into the handle types consumed by IGC.GetHandles. List<HandleType> types = new(); if (_handleWalkMask.HasFlag(CorGCReferenceType.CorHandleStrong)) types.Add(HandleType.Strong); if (_handleWalkMask.HasFlag(CorGCReferenceType.CorHandleStrongPinning)) types.Add(HandleType.Pinned); if (_handleWalkMask.HasFlag(CorGCReferenceType.CorHandleWeakShort)) types.Add(HandleType.WeakShort); if (_handleWalkMask.HasFlag(CorGCReferenceType.CorHandleWeakLong)) types.Add(HandleType.WeakLong); if (_handleWalkMask.HasFlag(CorGCReferenceType.CorHandleWeakRefCount) || _handleWalkMask.HasFlag(CorGCReferenceType.CorHandleStrongRefCount)) types.Add(HandleType.RefCounted); if (_handleWalkMask.HasFlag(CorGCReferenceType.CorHandleStrongDependent)) types.Add(HandleType.Dependent); if (types.Count == 0) return []; // Only request types the target actually supports HashSet<HandleType> supported = new(_gc.GetSupportedHandleTypes()); types.RemoveAll(t => !supported.Contains(t)); return types.ToArray(); } private bool TryMapHandle(HandleData handle, out CorGCReferenceType dwType, out ulong extraData) { extraData = 0; switch (_gc.GetHandleTypes([handle.Type])[0]) { case HandleType.Strong: dwType = CorGCReferenceType.CorHandleStrong; return true; case HandleType.Pinned: dwType = CorGCReferenceType.CorHandleStrongPinning; return true; case HandleType.WeakShort: dwType = CorGCReferenceType.CorHandleWeakShort; return true; case HandleType.WeakLong: dwType = CorGCReferenceType.CorHandleWeakLong; return true; case HandleType.RefCounted: extraData = handle.RefCount; dwType = handle.RefCount != 0 ? CorGCReferenceType.CorHandleStrongRefCount : CorGCReferenceType.CorHandleWeakRefCount; return true; case HandleType.Dependent: dwType = CorGCReferenceType.CorHandleStrongDependent; extraData = handle.Secondary.Value; return true; default: dwType = 0; return false; } } private IEnumerable<DacGcReference> WalkStacks() { IThread threadContract = _target.Contracts.Thread; IStackWalk stackWalkContract = _target.Contracts.StackWalk; ThreadStoreData threadStore = threadContract.GetThreadStoreData(); TargetPointer threadAddr = threadStore.FirstThread; while (threadAddr != TargetPointer.Null) { ThreadData threadData = threadContract.GetThreadData(threadAddr); foreach (StackReferenceData stackRef in stackWalkContract.WalkStackReferences(threadData, true)) { // Skip cDAC-private deferred-frame markers; they are not real GC references. if ((stackRef.Flags & CDAC_DEFERRED_FRAME) != 0) continue; DacGcReference reference = new() { vmDomain = _appDomain.Value, dwType = CorGCReferenceType.CorReferenceStack, i64ExtraData = 0, }; // Interior pointers, Frame refs, and enregistered vars are reported as a direct object pointer with the low bit set; // everything else is reported by the address of the stack slot holding the object. if (stackRef.IsInteriorPointer || stackRef.Address == TargetPointer.Null) reference.pObject = stackRef.Object.Value | 1; else reference.objHnd = stackRef.Address.Value; yield return reference; } threadAddr = threadData.NextThread; } } }