Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cdac] Handle hot/cold map lookup in ExecutionManager.ReadyToRunJitManager.GetMethodInfo #110087

Merged
merged 13 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,8 @@ CDAC_TYPE_INDETERMINATE(ReadyToRunInfo)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, CompositeInfo, cdac_data<ReadyToRunInfo>::CompositeInfo)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumRuntimeFunctions, cdac_data<ReadyToRunInfo>::NumRuntimeFunctions)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, RuntimeFunctions, cdac_data<ReadyToRunInfo>::RuntimeFunctions)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*uint32*/, NumHotColdMap, cdac_data<ReadyToRunInfo>::NumHotColdMap)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, HotColdMap, cdac_data<ReadyToRunInfo>::HotColdMap)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*pointer*/, DelayLoadMethodCallThunks, cdac_data<ReadyToRunInfo>::DelayLoadMethodCallThunks)
CDAC_TYPE_FIELD(ReadyToRunInfo, /*HashMap*/, EntryPointToMethodDescMap, cdac_data<ReadyToRunInfo>::EntryPointToMethodDescMap)
CDAC_TYPE_END(ReadyToRunInfo)
Expand All @@ -431,8 +433,19 @@ CDAC_TYPE_END(ImageDataDirectory)
CDAC_TYPE_BEGIN(RuntimeFunction)
CDAC_TYPE_SIZE(sizeof(RUNTIME_FUNCTION))
CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, BeginAddress, offsetof(RUNTIME_FUNCTION, BeginAddress))
#ifdef TARGET_AMD64
CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, EndAddress, offsetof(RUNTIME_FUNCTION, EndAddress))
#endif
CDAC_TYPE_FIELD(RuntimeFunction, /*uint32*/, UnwindData, offsetof(RUNTIME_FUNCTION, UnwindData))
CDAC_TYPE_END(RuntimeFunction)

CDAC_TYPE_BEGIN(UnwindInfo)
CDAC_TYPE_INDETERMINATE(UnwindInfo)
#ifdef TARGET_X86
CDAC_TYPE_FIELD(UnwindInfo, /*uint32*/, FunctionLength, offsetof(UNWIND_INFO, FunctionLength))
#endif
CDAC_TYPE_END(UnwindInfo)

CDAC_TYPE_BEGIN(HashMap)
CDAC_TYPE_INDETERMINATE(HashMap)
CDAC_TYPE_FIELD(HashMap, /*pointer*/, Buckets, cdac_data<HashMap>::Buckets)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/readytoruninfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ struct cdac_data<ReadyToRunInfo>
static constexpr size_t CompositeInfo = offsetof(ReadyToRunInfo, m_pCompositeInfo);
static constexpr size_t NumRuntimeFunctions = offsetof(ReadyToRunInfo, m_nRuntimeFunctions);
static constexpr size_t RuntimeFunctions = offsetof(ReadyToRunInfo, m_pRuntimeFunctions);
static constexpr size_t NumHotColdMap = offsetof(ReadyToRunInfo, m_nHotColdMap);
static constexpr size_t HotColdMap = offsetof(ReadyToRunInfo, m_pHotColdMap);
static constexpr size_t DelayLoadMethodCallThunks = offsetof(ReadyToRunInfo, m_pSectionDelayLoadMethodCallThunks);
static constexpr size_t EntryPointToMethodDescMap = offsetof(ReadyToRunInfo, m_entryPointToMethodDescMap);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ internal interface IExecutionManager : IContract
CodeBlockHandle? GetCodeBlockHandle(TargetCodePointer ip) => throw new NotImplementedException();
TargetPointer GetMethodDesc(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();
TargetCodePointer GetStartAddress(CodeBlockHandle codeInfoHandle) => throw new NotImplementedException();

}

internal readonly struct ExecutionManager : IExecutionManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@ public enum DataType
RuntimeFunction,
HashMap,
Bucket,
UnwindInfo,
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ internal partial class ExecutionManagerBase<T> : IExecutionManager
private class ReadyToRunJitManager : JitManager
{
private readonly uint _runtimeFunctionSize;
private readonly PtrHashMapLookup _lookup;
private readonly PtrHashMapLookup _hashMap;
private readonly HotColdLookup _hotCold;
private readonly RuntimeFunctionLookup _runtimeFunctions;

public ReadyToRunJitManager(Target target) : base(target)
{
_runtimeFunctionSize = Target.GetTypeInfo(DataType.RuntimeFunction).Size!.Value;
_lookup = PtrHashMapLookup.Create(target);
_hashMap = PtrHashMapLookup.Create(target);
_hotCold = HotColdLookup.Create(target);
_runtimeFunctions = RuntimeFunctionLookup.Create(target);
}

public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer jittedCodeAddress, [NotNullWhen(true)] out CodeBlock? info)
Expand All @@ -43,35 +47,44 @@ public override bool GetMethodInfo(RangeSection rangeSection, TargetCodePointer
TargetPointer imageBase = rangeSection.Data.RangeBegin;
TargetPointer relativeAddr = addr - imageBase;

int index = GetRuntimeFunctionIndexForAddress(r2rInfo, relativeAddr);
if (index < 0)
uint index;
if (!_runtimeFunctions.TryGetRuntimeFunctionIndexForAddress(r2rInfo.RuntimeFunctions, r2rInfo.NumRuntimeFunctions, relativeAddr, out index))
return false;

bool featureEHFunclets = Target.ReadGlobal<byte>(Constants.Globals.FeatureEHFunclets) != 0;
if (featureEHFunclets)
{
// TODO: [cdac] Look up in hot/cold mapping lookup table and if the method is in the cold block,
// get the index of the associated hot block.
// HotColdMappingLookupTable::LookupMappingForMethod
//
// while GetMethodDescForEntryPoint for the begin address of function at index is null
// index--
// Look up index in hot/cold map - if the function is in the cold part, get the index of the hot part.
index = _hotCold.GetHotFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index);
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
}

TargetPointer functionEntry = r2rInfo.RuntimeFunctions + (ulong)(index * _runtimeFunctionSize);
Data.RuntimeFunction function = Target.ProcessedData.GetOrAdd<Data.RuntimeFunction>(functionEntry);

// ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage
TargetCodePointer startAddress = imageBase + function.BeginAddress;
TargetPointer entryPoint = CodePointerUtils.AddressFromCodePointer(startAddress, Target);
TargetPointer methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index);
while (featureEHFunclets && methodDesc == TargetPointer.Null)
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
{
index--;
methodDesc = GetMethodDescForRuntimeFunction(r2rInfo, imageBase, index);
}

TargetPointer methodDesc = _lookup.GetValue(r2rInfo.EntryPointToMethodDescMap, entryPoint);
Debug.Assert(methodDesc != TargetPointer.Null);
Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, index);

// TODO: [cdac] Handle method with cold code when computing relative offset
// ReadyToRunJitManager::JitTokenToMethodRegionInfo
TargetCodePointer startAddress = imageBase + function.BeginAddress;
TargetNUInt relativeOffset = new TargetNUInt(addr - startAddress);

// Take hot/cold splitting into account for the relative offset
if (_hotCold.TryGetColdFunctionIndex(r2rInfo.NumHotColdMap, r2rInfo.HotColdMap, index, out uint coldFunctionIndex))
{
Data.RuntimeFunction coldFunction = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, coldFunctionIndex);
TargetPointer coldStart = imageBase + coldFunction.BeginAddress;
if (addr >= coldStart)
{
// If the address is in the cold part, the relative offset is the size of the
// hot part plus the offset from the address to the start of the cold part
uint hotSize = _runtimeFunctions.GetFunctionLength(function);
relativeOffset = new TargetNUInt(hotSize + addr - coldStart);
}
}

info = new CodeBlock(startAddress.Value, methodDesc, relativeOffset, rangeSection.Data!.JitManager);
return true;
}
Expand All @@ -87,51 +100,19 @@ private bool IsStubCodeBlockThunk(Data.RangeSection rangeSection, Data.ReadyToRu
return thunksData.VirtualAddress <= rva && rva < thunksData.VirtualAddress + thunksData.Size;
}

private int GetRuntimeFunctionIndexForAddress(Data.ReadyToRunInfo r2rInfo, TargetPointer relativeAddress)
private TargetPointer GetMethodDescForRuntimeFunction(Data.ReadyToRunInfo r2rInfo, TargetPointer imageBase, uint runtimeFunctionIndex)
{
// NativeUnwindInfoLookupTable::LookupUnwindInfoForMethod
uint start = 0;
uint end = r2rInfo.NumRuntimeFunctions - 1;
relativeAddress = CodePointerUtils.CodePointerFromAddress(relativeAddress, Target).AsTargetPointer;

// Entries are sorted. Binary search until we get to 10 or fewer items.
while (end - start > 10)
{
uint middle = start + (end - start) / 2;
Data.RuntimeFunction func = GetRuntimeFunction(r2rInfo, middle);
if (relativeAddress < func.BeginAddress)
{
end = middle - 1;
}
else
{
start = middle;
}
}
Data.RuntimeFunction function = _runtimeFunctions.GetRuntimeFunction(r2rInfo.RuntimeFunctions, runtimeFunctionIndex);

// Find the runtime function that contains the address of interest
for (uint i = start; i <= end; ++i)
{
// Entries are terminated by a sentinel value of -1, so we can index one past the end safely.
// Read as a runtime function, its begin address is 0xffffffff (always > relative address).
// See RuntimeFunctionsTableNode.GetData in RuntimeFunctionsTableNode.cs
Data.RuntimeFunction nextFunc = GetRuntimeFunction(r2rInfo, i + 1);
if (relativeAddress >= nextFunc.BeginAddress)
continue;

Data.RuntimeFunction func = GetRuntimeFunction(r2rInfo, i);
if (relativeAddress >= func.BeginAddress)
return (int)i;
}
// ReadyToRunInfo::GetMethodDescForEntryPointInNativeImage
TargetCodePointer startAddress = imageBase + function.BeginAddress;
TargetPointer entryPoint = CodePointerUtils.AddressFromCodePointer(startAddress, Target);

return -1;
}
TargetPointer methodDesc = _hashMap.GetValue(r2rInfo.EntryPointToMethodDescMap, entryPoint);
if (methodDesc == (ulong)HashMapLookup.SpecialKeys.InvalidEntry)
return TargetPointer.Null;

private Data.RuntimeFunction GetRuntimeFunction(Data.ReadyToRunInfo r2rInfo, uint index)
{
TargetPointer first = r2rInfo.RuntimeFunctions;
TargetPointer addr = first + (ulong)(index * _runtimeFunctionSize);
return Target.ProcessedData.GetOrAdd<Data.RuntimeFunction>(addr);
return methodDesc;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace Microsoft.Diagnostics.DataContractReader.ExecutionManagerHelpers;

internal sealed class HotColdLookup
{
public static HotColdLookup Create(Target target)
=> new HotColdLookup(target);

private readonly Target _target;

private HotColdLookup(Target target)
{
_target = target;
}

public uint GetHotFunctionIndex(uint numHotColdMap, TargetPointer hotColdMap, uint runtimeFunctionIndex)
{
if (!IsColdCode(numHotColdMap, hotColdMap, runtimeFunctionIndex))
return runtimeFunctionIndex;

uint hotIndex;
if (!TryLookupHotColdMappingForMethod(numHotColdMap, hotColdMap, runtimeFunctionIndex, out hotIndex, out _))
return runtimeFunctionIndex;

// If runtime function is in the cold part, get the associated hot part
Debug.Assert((hotIndex & 1) == 1);
return _target.Read<uint>(hotColdMap + (ulong)hotIndex * sizeof(uint));
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
}

public bool TryGetColdFunctionIndex(uint numHotColdMap, TargetPointer hotColdMap, uint runtimeFunctionIndex, out uint functionIndex)
{
functionIndex = ~0u;

uint coldIndex;
if (!TryLookupHotColdMappingForMethod(numHotColdMap, hotColdMap, runtimeFunctionIndex, out uint _, out coldIndex))
return false;

functionIndex = _target.Read<uint>(hotColdMap + (ulong)coldIndex * sizeof(uint));
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved
return true;
}

private bool IsColdCode(uint numHotColdMap, TargetPointer hotColdMap, uint runtimeFunctionIndex)
{
if (numHotColdMap == 0)
return false;

// Determine if the method index represents a hot or cold part by comparing against the first
// cold part index (hot < cold).
uint firstColdRuntimeFunctionIndex = _target.Read<uint>(hotColdMap);
return runtimeFunctionIndex >= firstColdRuntimeFunctionIndex;
}

// Look up a runtime function index in the hot/cold map. If the function is in the
// hot/cold map, return whether the function corresponds to cold code and the hot
// and cold lookup indexes for the function.
private bool TryLookupHotColdMappingForMethod(
uint numHotColdMap,
TargetPointer hotColdMap,
uint runtimeFunctionIndex,
out uint hotIndex,
out uint coldIndex)
{
hotIndex = ~0u;
coldIndex = ~0u;

// HotColdMappingLookupTable::LookupMappingForMethod
if (numHotColdMap == 0)
return false;

// Each method is represented by a pair of unsigned 32-bit integers. First is the runtime
// function index of the cold part, second is the runtime function index of the hot part.
// HotColdMap is these pairs as an array, so the logical size is half the array size.
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
uint start = 0;
uint end = (numHotColdMap - 1) / 2;

bool isColdCode = IsColdCode(numHotColdMap, hotColdMap, runtimeFunctionIndex);
int indexCorrection = isColdCode ? 0 : 1;

// Entries are sorted by the hot part runtime function indices. This also means they are sorted
// by the cold part indices, as the cold part is emitted in the same order as hot parts.
// Binary search until we get to 10 or fewer items.
while (end - start > 10)
{
uint middle = start + (end - start) / 2;
long index = middle * 2 + indexCorrection;

if (runtimeFunctionIndex < _target.Read<uint>(hotColdMap + (ulong)(index * sizeof(uint))))
{
end = middle - 1;
}
else
{
start = middle;
}
}
elinor-fung marked this conversation as resolved.
Show resolved Hide resolved

// Find the hot/cold map index corresponding to the cold/hot runtime function index
for (uint i = start; i <= end; ++i)
{
uint index = i * 2;

uint value = _target.Read<uint>(hotColdMap + (ulong)(index + indexCorrection) * sizeof(uint));
if (value == runtimeFunctionIndex)
{
hotIndex = index + 1;
coldIndex = index;
return true;
}

// If function index is a cold funclet from a cold block, the above check for equality will fail.
// To get its corresponding hot block, find the cold block containing the funclet,
// then use the lookup table.
// The cold funclet's function index will be greater than its cold block's function index,
// but less than the next cold block's function index in the lookup table.
if (isColdCode && runtimeFunctionIndex > _target.Read<uint>(hotColdMap + (ulong)index * sizeof(uint)))
{
bool isFuncletIndex = index + 2 == numHotColdMap
|| runtimeFunctionIndex < _target.Read<uint>(hotColdMap + (ulong)(index + 2) * sizeof(uint));
if (isFuncletIndex)
{
hotIndex = index + 1;
coldIndex = index;
return true;
}
}
}

return false;
}
}
Loading
Loading