| File: Contracts\ExecutionManager\Helpers\NibbleMapConstantLookup.cs | Web Access |
| Project: src\runtime\src\native\managed\cdac\Microsoft.Diagnostics.DataContractReader.Contracts\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 System.Numerics; using System.Diagnostics; using System; using static Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers.NibbleMapHelpers; namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers; // CoreCLR nibblemap with O(1) lookup time. // // Implementation very similar to NibbleMapLinearLookup, but with the addition of writing relative pointers // into the nibblemap whenever a code block completely covers a DWORD. This allows for O(1) lookup // with the cost of O(n) write time. // // Pointers are encoded using the top 28 bits of the DWORD normally, the bottom 4 bits of the pointer // are reduced to 2 bits due to 4 byte code offset and encoded in bits 28 .. 31 of the DWORD with values // 9-12. This is used to differentiate nibble values and pointer DWORDs. // // To read the nibblemap, we check if the DWORD is a pointer. If so, then we know the value currentPC is // part of a managed code block beginning at the mapBase + decoded pointer. If the DWORD is empty // (no pointer or previous nibbles), then we only need to read the previous DWORD. If that DWORD is empty, // then we must not be in a managed function. Otherwise the write algorithm would have written a relative // pointer in the DWORD. // // Note, a currentPC pointing to bytes outside a function have undefined lookup behavior. // In this implementation we may "extend" the lookup period of a function several hundred bytes // if there is not another function following it. internal sealed class NibbleMapConstantLookup : INibbleMap { private readonly Target _target; private NibbleMapConstantLookup(Target target) { _target = target; } internal static bool IsPointer(MapUnit mapUnit) { return (mapUnit.Value & MapUnit.NibbleMask) > 8; } internal static TargetPointer DecodePointer(TargetPointer baseAddress, MapUnit mapUnit) { uint nibble = mapUnit.Value & MapUnit.NibbleMask; uint relativePointer = (mapUnit.Value & ~MapUnit.NibbleMask) + ((nibble - 9) << 2); return baseAddress + relativePointer; } internal static uint EncodePointer(uint relativeAddress) { uint nibble = ((relativeAddress & MapUnit.NibbleMask) >>> 2) + 9; return (relativeAddress & ~MapUnit.NibbleMask) + nibble; } internal TargetPointer FindMethodCode(TargetPointer mapBase, TargetPointer mapStart, TargetCodePointer currentPC) { TargetNUInt relativeAddress = new TargetNUInt(currentPC.Value - mapBase.Value); DecomposeAddress(relativeAddress, out MapKey mapIdx, out Nibble bucketByteIndex); MapUnit t = mapIdx.ReadMapUnit(_target, mapStart); // if pointer, return value if (IsPointer(t)) { return DecodePointer(mapBase, t); } // shift the nibble we want to the least significant position t = t.FocusOnIndexedNibble(mapIdx); // if the nibble is non-zero, we have found the start of a method, // but we need to check that the start is before the current address, not after if (!t.Nibble.IsEmpty && t.Nibble.Value <= bucketByteIndex.Value) { return GetAbsoluteAddress(mapBase, mapIdx, t.Nibble); } // search backwards through the current map unit // we processed the lsb nibble, move to the next one t = t.ShiftNextNibble; // if there's any nibble set in the current unit, find it if (!t.IsEmpty) { mapIdx = mapIdx.Prev; while (t.Nibble.IsEmpty) { t = t.ShiftNextNibble; mapIdx = mapIdx.Prev; } return GetAbsoluteAddress(mapBase, mapIdx, t.Nibble); } // We finished the current map unit, we want to move to the previous one. // But if we were in the first map unit, we can stop if (mapIdx.InFirstMapUnit) { return TargetPointer.Null; } // We're now done with the current map unit. // Align the map index to the current map unit, then move back one nibble into the previous map unit mapIdx = mapIdx.AlignDownToMapUnit(); mapIdx = mapIdx.Prev; // read the map unit containing mapIdx and skip over it if it is all zeros t = mapIdx.ReadMapUnit(_target, mapStart); // if t is empty, then currentPC can not be in a function if (t.IsEmpty) { return TargetPointer.Null; } // if t is not empty, it must contain a pointer or a nibble if (IsPointer(t)) { return DecodePointer(mapBase, t); } // move to the correct nibble in the map unit while (!mapIdx.IsZero && t.Nibble.IsEmpty) { t = t.ShiftNextNibble; mapIdx = mapIdx.Prev; } return GetAbsoluteAddress(mapBase, mapIdx, t.Nibble); } public static INibbleMap Create(Target target) { return new NibbleMapConstantLookup(target); } public TargetPointer FindMethodCode(Data.CodeHeapListNode heapListNode, TargetCodePointer jittedCodeAddress) { if (jittedCodeAddress < heapListNode.StartAddress || jittedCodeAddress > heapListNode.EndAddress) { return TargetPointer.Null; } return FindMethodCode(heapListNode.MapBase, heapListNode.HeaderMap, jittedCodeAddress); } }