Skip to content

Commit 8e80e90

Browse files
rcj1max-charlambCopilotjkotas
authored
Adding GetReJITInformation cDAC API (#119111)
* Adding GetRejitInformation cDAC API * Restructuring SHash --------- Co-authored-by: Max Charlamb <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Jan Kotas <[email protected]>
1 parent fc5252e commit 8e80e90

File tree

28 files changed

+617
-12
lines changed

28 files changed

+617
-12
lines changed

docs/design/datacontracts/CodeVersions.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ public virtual TargetCodePointer GetNativeCode(NativeCodeVersionHandle codeVersi
4545

4646
// Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null
4747
public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVersionHandle);
48+
49+
// Gets the IL address given a code version
50+
public virtual TargetPointer GetIL(ILCodeVersionHandle ilCodeVersionHandle);
4851
```
4952
### Extension Methods
5053
```csharp
@@ -74,6 +77,8 @@ Data descriptors used:
7477
| ILCodeVersioningState | ActiveVersionMethodDef | if the active version is synthetic or unknown, the MethodDef token for the method |
7578
| ILCodeVersionNode | VersionId | Version ID of the node |
7679
| ILCodeVersionNode | Next | Pointer to the next `ILCodeVersionNode`|
80+
| ILCodeVersionNode | RejitState | ReJIT state of the node |
81+
| ILCodeVersionNode | ILAddress | Address of IL corresponding to `ILCodeVersionNode`|
7782
| GCCoverageInfo | SavedCode | Pointer to the GCCover saved code copy, if supported |
7883

7984
The flag indicates that the default version of the code for a method desc is active:
@@ -111,6 +116,60 @@ Contracts used:
111116
| Loader |
112117
| RuntimeTypeSystem |
113118

119+
Implementation of CodeVersionHandles
120+
121+
```csharp
122+
private readonly struct ILCodeVersionHandle
123+
{
124+
public readonly TargetPointer Module;
125+
public readonly uint MethodDefinition;
126+
public readonly TargetPointer ILCodeVersionNode;
127+
private ILCodeVersionHandle(TargetPointer module, uint methodDef, TargetPointer ilCodeVersionNodeAddress)
128+
{
129+
Module = module;
130+
MethodDefinition = methodDef;
131+
ILCodeVersionNode = ilCodeVersionNodeAddress;
132+
}
133+
134+
// for more information on Explicit/Synthetic code versions see docs/design/features/code-versioning.md
135+
public static ILCodeVersionHandle CreateExplicit(TargetPointer ilCodeVersionNodeAddress) =>
136+
// create handle from node address
137+
public static ILCodeVersionHandle CreateSynthetic(TargetPointer module, uint methodDef) =>
138+
// create handle from module and methodDef
139+
140+
public static ILCodeVersionHandle Invalid { get; } = // everything is null
141+
142+
public bool IsValid => // either module or node addr is non nulls
143+
144+
public bool IsExplicit => ILCodeVersionNode != TargetPointer.Null;
145+
}
146+
```
147+
148+
```csharp
149+
private readonly struct NativeCodeVersionHandle
150+
{
151+
public readonly TargetPointer MethodDescAddress;
152+
public readonly TargetPointer CodeVersionNodeAddress;
153+
private NativeCodeVersionHandle(TargetPointer methodDescAddress, TargetPointer codeVersionNodeAddress)
154+
{
155+
MethodDescAddress = methodDescAddress;
156+
CodeVersionNodeAddress = codeVersionNodeAddress;
157+
}
158+
159+
// for more information on Explicit/Synthetic code versions see docs/design/features/code-versioning.md
160+
public static NativeCodeVersionHandle CreateExplicit(TargetPointer codeVersionNodeAddress) =>
161+
// create handle from node address
162+
public static NativeCodeVersionHandle CreateSynthetic(TargetPointer methodDescAddress) =>
163+
// create handle from method desc
164+
165+
public static NativeCodeVersionHandle Invalid { get; } = // all is null
166+
167+
public bool Valid => // either method desc or node address is non null
168+
169+
public bool IsExplicit => CodeVersionNodeAddress != TargetPointer.Null;
170+
}
171+
```
172+
114173
### Finding active ILCodeVersion for a method
115174
```csharp
116175
public virtual ILCodeVersionHandle GetActiveILCodeVersion(TargetPointer methodDesc);
@@ -295,3 +354,29 @@ public virtual TargetPointer GetGCStressCodeCopy(NativeCodeVersionHandle codeVer
295354

296355
1. If `codeVersionHandle` is synthetic, use the `IRuntimeTypeSystem` to find the GCStressCodeCopy.
297356
2. If `codeVersionHandle` is explicit, read the `NativeCodeVersionNode` for the `GCCoverageInfo` pointer. This value only exists in some builds. If the value doesn't exist or is a nullptr, return `TargetPointer.Null`. Otherwise return the `SavedCode` pointer from the `GCCoverageInfo` struct.
357+
358+
### Finding IL address for method
359+
```csharp
360+
TargetPointer ICodeVersions.GetIL(ILCodeVersionHandle ilCodeVersionHandle, TargetPointer methodDescPtr)
361+
{
362+
TargetPointer ilAddress = default;
363+
if (ilCodeVersionHandle.IsExplicit)
364+
{
365+
ilAddress = target.ReadPointer(ilCodeVersionHandle.ILCodeVersionNode + /* ILCodeVersionNode::ILAddress offset */)
366+
}
367+
368+
// For the default code version we always fetch the globally stored default IL for a method
369+
// See src/coreclr/vm/codeversion.cpp for more detailed implementation comments.
370+
371+
if (ilAddress == TargetPointer.Null)
372+
{
373+
// Synthetic ILCodeVersion, get the IL from the module and method def
374+
375+
ILoader loader = _target.Contracts.Loader;
376+
ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(ilCodeVersionHandle.Module);
377+
ilAddress = loader.GetILHeader(moduleHandle, ilCodeVersionHandle.MethodDefinition);
378+
}
379+
380+
return ilAddress;
381+
}
382+
```

docs/design/datacontracts/Loader.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer);
106106
| `Module` | `MethodDefToDescMap` | Mapping table |
107107
| `Module` | `TypeDefToMethodTableMap` | Mapping table |
108108
| `Module` | `TypeRefToMethodTableMap` | Mapping table |
109+
| `Module` | `DynamicILBlobTable` | pointer to the table of dynamic IL |
109110
| `ModuleLookupMap` | `TableData` | Start of the mapping table's data |
110111
| `ModuleLookupMap` | `SupportedFlagsMask` | Mask for flag bits on lookup map entries |
111112
| `ModuleLookupMap` | `Count` | Number of TargetPointer sized entries in this section of the map |
@@ -149,6 +150,12 @@ TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer);
149150
| `InstMethodHashTable` | `Count` | Count of elements in the hash table |
150151
| `InstMethodHashTable` | `VolatileEntryValue` | The data stored in the hash table entry |
151152
| `InstMethodHashTable` | `VolatileEntryNextEntry` | Next pointer in the hash table entry |
153+
| `DynamicILBlobTable` | `Table` | Pointer to IL blob table |
154+
| `DynamicILBlobTable` | `TableSize` | Number of entries in table |
155+
| `DynamicILBlobTable` | `EntrySize` | Size of each table entry |
156+
| `DynamicILBlobTable` | `EntryMethodToken` | Offset of each entry method token from entry address |
157+
| `DynamicILBlobTable` | `EntryIL` | Offset of each entry IL from entry address |
158+
152159

153160

154161
### Global variables used:
@@ -164,6 +171,12 @@ TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer);
164171
| `ASSEMBLY_LEVEL_LOADED` | uint | The value of Assembly Level required for an Assembly to be considered loaded. In the runtime, this is `FILE_LOAD_DELIVER_EVENTS` | `0x4` |
165172
| `ASSEMBLY_NOTIFYFLAGS_PROFILER_NOTIFIED` | uint | Flag in Assembly NotifyFlags indicating the Assembly will notify profilers. | `0x1` |
166173

174+
Contracts used:
175+
| Contract Name |
176+
| --- |
177+
| EcmaMetadata |
178+
| SHash |
179+
167180
### Data Structures
168181
```csharp
169182
// The runtime representation of Module's flag field.
@@ -625,6 +638,42 @@ TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer)
625638
return target.ReadPointer(loaderAllocatorPointer + /* LoaderAllocator::ObjectHandle offset */);
626639
}
627640

641+
private sealed class DynamicILBlobTraits : ITraits<uint, DynamicILBlobEntry>
642+
{
643+
public uint GetKey(DynamicILBlobEntry entry) => entry.EntryMethodToken;
644+
public bool Equals(uint left, uint right) => left == right;
645+
public uint Hash(uint key) => key;
646+
public bool IsNull(DynamicILBlobEntry entry) => entry.EntryMethodToken == 0;
647+
public DynamicILBlobEntry Null() => new DynamicILBlobEntry(0, TargetPointer.Null);
648+
public bool IsDeleted(DynamicILBlobEntry entry) => false;
649+
}
650+
651+
TargetPointer GetILHeader(ModuleHandle handle, uint token)
652+
{
653+
// we need module
654+
ILoader loader = this;
655+
TargetPointer peAssembly = loader.GetPEAssembly(handle);
656+
TargetPointer headerPtr = GetDynamicIL(handle, token);
657+
TargetPointer dynamicBlobTablePtr = target.ReadPointer(handle.Address + /* Module::DynamicILBlobTable offset */);
658+
Contracts.IThread shashContract = target.Contracts.SHash;
659+
DynamicILBlobTraits traits = new();
660+
/* To construct an SHash we must pass a DataType enum.
661+
We must be able to look up this enum in a dictionary of known types and retrieve a Target.TypeInfo struct.
662+
This struct contains a dictionary of fields with keys corresponding to the names of offsets
663+
and values corresponding to the offset values. Optionally, it contains a Size field.
664+
*/
665+
SHash<uint, Data.DynamicILBlobEntry> shash = shashContract.CreateSHash<uint, Data.DynamicILBlobEntry>(target, dynamicBlobTablePtr, DataType.DynamicILBlobTable, traits)
666+
Data.DynamicILBlobEntry blobEntry = shashContract.LookupSHash(shash, token);
667+
if (blobEntry.EntryIL == TargetPointer.Null)
668+
{
669+
IEcmaMetadata ecmaMetadataContract = _target.Contracts.EcmaMetadata;
670+
MetadataReader mdReader = ecmaMetadataContract.GetMetadata(handle)!;
671+
MethodDefinition methodDef = mdReader.GetMethodDefinition(MetadataTokens.MethodDefinitionHandle(token));
672+
int rva = methodDef.RelativeVirtualAddress;
673+
headerPtr = loader.GetILAddr(peAssembly, rva);
674+
}
675+
return headerPtr;
676+
}
628677
```
629678

630679
### DacEnumerableHash (EETypeHashTable and InstMethodHashTable)

docs/design/datacontracts/RuntimeTypeSystem.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ partial interface IRuntimeTypeSystem : IContract
182182

183183
// Gets the GCStressCodeCopy pointer if available, otherwise returns TargetPointer.Null
184184
public virtual TargetPointer GetGCStressCodeCopy(MethodDescHandle methodDesc);
185+
185186
}
186187
```
187188

docs/design/datacontracts/SHash.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Contract SHash
2+
3+
This contract is for creating and using the SHash data structure.
4+
5+
## APIs of contract
6+
7+
```csharp
8+
public interface ITraits<TKey, TEntry>
9+
{
10+
TKey GetKey(TEntry entry);
11+
bool Equals(TKey left, TKey right);
12+
uint Hash(TKey key);
13+
bool IsNull(TEntry entry);
14+
TEntry Null();
15+
bool IsDeleted(TEntry entry);
16+
}
17+
18+
public interface ISHash<TKey, TEntry> where TEntry : IData<TEntry>
19+
{
20+
21+
}
22+
```
23+
24+
``` csharp
25+
TEntry LookupSHash<TKey, TEntry>(ISHash<TKey, TEntry> hashTable, TKey key) where TEntry : IData<TEntry>;
26+
SHash<TKey, TEntry> CreateSHash<TKey, TEntry>(Target target, TargetPointer address, Target.TypeInfo type, ITraits<TKey, TEntry> traits) where TEntry : IData<TEntry>;
27+
```
28+
29+
## Version 1
30+
31+
In order to properly populate an SHash, we need to know the size of each element, which varies from instantiation to instantiation of SHash. Therefore, we pass as an argument the DataType ```type``` which contains the particular offsets.
32+
33+
Data descriptors used:
34+
| Data Descriptor Name | Field | Meaning |
35+
| --- | --- | --- |
36+
| `type` | `Table` | Address of the SHash table |
37+
| `type` | `TableSize` | Number of entries in the table |
38+
| `type` | `EntrySize` | Size in bytes of each table entry |
39+
40+
``` csharp
41+
42+
private class SHash<TKey, TEntry> : ISHash<TKey, TEntry> where TEntry : IData<TEntry>
43+
{
44+
public TargetPointer Table { get; set; }
45+
public uint TableSize { get; set; }
46+
public uint EntrySize { get; set; }
47+
public List<TEntry>? Entries { get; set; }
48+
public ITraits<TKey, TEntry>? Traits { get; set; }
49+
}
50+
51+
ISHash<TKey, TEntry> ISHash.CreateSHash<TKey, TEntry>(Target target, TargetPointer address, Target.TypeInfo type, ITraits<TKey, TEntry> traits)
52+
{
53+
TargetPointer table = target.ReadPointer(address + /* type::Table offset */);
54+
uint tableSize = target.Read<uint>(address + /* type::TableSize offset */);
55+
uint entrySize = target.Read<uint>(address + /* type::EntrySize offset */);
56+
List<TEntry> entries = [];
57+
for (int i = 0; i < tableSize; i++)
58+
{
59+
TargetPointer entryAddress = table + (ulong)(i * entrySize);
60+
TEntry entry = new TEntry(entryAddress);
61+
entries.Add(entry);
62+
}
63+
return new SHash<TKey, TEntry>
64+
{
65+
Table = table,
66+
TableSize = tableSize,
67+
EntrySize = entrySize,
68+
Traits = traits,
69+
Entries = entries
70+
};
71+
}
72+
TEntry ISHash.LookupSHash<TKey, TEntry>(ISHash<TKey, TEntry> hashTable, TKey key)
73+
{
74+
SHash<TKey, TEntry> shashTable = (SHash<TKey, TEntry>)hashTable;
75+
if (shashTable.TableSize == 0)
76+
return shashTable.Traits!.Null();
77+
78+
uint hash = shashTable.Traits!.Hash(key);
79+
uint index = hash % shashTable.TableSize;
80+
uint increment = 0;
81+
while (true)
82+
{
83+
TEntry current = shashTable.Entries![(int)index];
84+
if (shashTable.Traits.IsNull(current))
85+
return shashTable.Traits.Null();
86+
// we don't support the removal of entries
87+
if (!shashTable.Traits.IsDeleted(current) && shashTable.Traits.Equals(key, shashTable.Traits.GetKey(current)))
88+
return current;
89+
90+
if (increment == 0)
91+
increment = (hash % (shashTable.TableSize - 1)) + 1;
92+
93+
index += increment;
94+
if (index >= shashTable.TableSize)
95+
index -= shashTable.TableSize;
96+
}
97+
}
98+
```

src/coreclr/inc/shash.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "clrtypes.h"
1010
#include "check.h"
1111
#include "iterator.h"
12+
#include "../vm/cdacdata.h"
1213

1314
// SHash is a templated closed chaining hash table of pointers. It provides
1415
// for multiple entries under the same key, and also for deleting elements.
@@ -593,6 +594,7 @@ class EMPTY_BASES_DECL SHash : public TRAITS
593594
count_t m_tableCount; // number of elements in table
594595
count_t m_tableOccupied; // number, includes deleted slots
595596
count_t m_tableMax; // maximum occupied count before reallocating
597+
friend struct ::cdac_data<SHash<TRAITS>>;
596598
}; // class SHash
597599

598600
// disables support for DAC marshaling. Useful for defining right-side only SHashes

src/coreclr/vm/ceeload.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,16 @@ class DynamicILBlobTraits : public NoRemoveSHashTraits<DefaultSHashTraits<Dynami
424424
typedef SHash<DynamicILBlobTraits> DynamicILBlobTable;
425425
typedef DPTR(DynamicILBlobTable) PTR_DynamicILBlobTable;
426426

427+
template<>
428+
struct cdac_data<DynamicILBlobTable>
429+
{
430+
static constexpr size_t Table = offsetof(DynamicILBlobTable, m_table);
431+
static constexpr size_t TableSize = offsetof(DynamicILBlobTable, m_tableSize);
432+
static constexpr size_t EntrySize = sizeof(DynamicILBlobEntry);
433+
static constexpr size_t EntryMethodToken = offsetof(DynamicILBlobEntry, m_methodToken);
434+
static constexpr size_t EntryIL = offsetof(DynamicILBlobEntry, m_il);
435+
};
436+
427437
#ifdef FEATURE_READYTORUN
428438
typedef DPTR(class ReadyToRunInfo) PTR_ReadyToRunInfo;
429439
#endif
@@ -1705,6 +1715,7 @@ struct cdac_data<Module>
17051715
static constexpr size_t TypeDefToMethodTableMap = offsetof(Module, m_TypeDefToMethodTableMap);
17061716
static constexpr size_t TypeRefToMethodTableMap = offsetof(Module, m_TypeRefToMethodTableMap);
17071717
static constexpr size_t MethodDefToILCodeVersioningStateMap = offsetof(Module, m_ILCodeVersioningStateMap);
1718+
static constexpr size_t DynamicILBlobTable = offsetof(Module, m_debuggerSpecificData.m_pDynamicILBlobTable);
17081719
};
17091720

17101721
//

src/coreclr/vm/codeversion.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -917,11 +917,41 @@ PTR_COR_ILMETHOD ILCodeVersion::GetIL() const
917917
if(pIL == NULL)
918918
{
919919
PTR_Module pModule = GetModule();
920-
PTR_MethodDesc pMethodDesc = dac_cast<PTR_MethodDesc>(pModule->LookupMethodDef(GetMethodDef()));
921-
if (pMethodDesc != NULL && pMethodDesc->MayHaveILHeader())
920+
// Always pickup overrides like reflection emit, EnC, etc. irrespective of RVA.
921+
// Profilers can attach dynamic IL to methods with zero RVA.
922+
mdMethodDef methodDef = GetMethodDef();
923+
TADDR pIL = pModule->GetDynamicIL(methodDef);
924+
if (pIL == (TADDR)NULL)
922925
{
923-
pIL = dac_cast<PTR_COR_ILMETHOD>(pMethodDesc->GetILHeader());
926+
DWORD rva;
927+
DWORD dwImplFlags;
928+
if (methodDef & 0x00FFFFFF)
929+
{
930+
if (FAILED(pModule->GetMDImport()->GetMethodImplProps(methodDef, &rva, &dwImplFlags)))
931+
{ // Class loader already asked for MethodImpls, so this should always succeed (unless there's a
932+
// bug or a new code path)
933+
_ASSERTE(!"If this ever fires, then this method should return HRESULT");
934+
rva = 0;
935+
}
936+
937+
// RVA points to IL header only when the code type is IL
938+
if (!IsMiIL(dwImplFlags))
939+
{
940+
rva = 0;
941+
}
942+
}
943+
else
944+
{
945+
rva = 0;
946+
}
947+
pIL = pModule->GetIL(rva);
924948
}
949+
950+
#ifdef DACCESS_COMPILE
951+
return (pIL != (TADDR)NULL) ? dac_cast<PTR_COR_ILMETHOD>(DacGetIlMethod(pIL)) : NULL;
952+
#else // !DACCESS_COMPILE
953+
return PTR_COR_ILMETHOD(pIL);
954+
#endif // !DACCESS_COMPILE
925955
}
926956

927957
return pIL;

src/coreclr/vm/codeversion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ struct cdac_data<ILCodeVersionNode>
426426
static constexpr size_t VersionId = offsetof(ILCodeVersionNode, m_rejitId);
427427
static constexpr size_t Next = offsetof(ILCodeVersionNode, m_pNextILVersionNode);
428428
static constexpr size_t RejitState = offsetof(ILCodeVersionNode, m_rejitState);
429+
static constexpr size_t ILAddress = offsetof(ILCodeVersionNode, m_pIL);
429430
};
430431

431432
class ILCodeVersionCollection

src/coreclr/vm/datadescriptor/contracts.jsonc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"ReJIT": 1,
2323
"RuntimeInfo": 1,
2424
"RuntimeTypeSystem": 1,
25+
"SHash": 1,
2526
"StackWalk": 1,
2627
"StressLog": 2,
2728
"Thread": 1
28-
}
29+
}

0 commit comments

Comments
 (0)