Skip to content

Commit 1fc8222

Browse files
authored
[cDAC] Implement ReJIT portion of SOSDacImpl::GetMethodDescData (#109936)
* Modifies CodeVersions contract * Adds to ReJIT contract * Adds contract extension methods as helpers for when functionality can be implemented in terms of versioned APIs.
1 parent 72a1c2b commit 1fc8222

File tree

18 files changed

+1057
-229
lines changed

18 files changed

+1057
-229
lines changed

docs/design/datacontracts/CodeVersions.md

Lines changed: 81 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,48 @@ This contract encapsulates support for [code versioning](../features/code-versio
44

55
## APIs of contract
66

7+
```csharp
8+
internal readonly struct ILCodeVersionHandle
9+
{
10+
public static ILCodeVersionHandle Invalid;
11+
12+
public bool IsValid;
13+
}
14+
```
15+
716
```csharp
817
internal struct NativeCodeVersionHandle
918
{
10-
// no public constructors
11-
internal readonly TargetPointer MethodDescAddress;
12-
internal readonly TargetPointer CodeVersionNodeAddress;
13-
internal NativeCodeVersionHandle(TargetPointer methodDescAddress, TargetPointer codeVersionNodeAddress)
14-
{
15-
if (methodDescAddress != TargetPointer.Null && codeVersionNodeAddress != TargetPointer.Null)
16-
{
17-
throw new ArgumentException("Only one of methodDescAddress and codeVersionNodeAddress can be non-null");
18-
}
19-
MethodDescAddress = methodDescAddress;
20-
CodeVersionNodeAddress = codeVersionNodeAddress;
21-
}
19+
internal static NativeCodeVersionHandle Invalid;
2220

23-
internal static NativeCodeVersionHandle Invalid => new(TargetPointer.Null, TargetPointer.Null);
24-
public bool Valid => MethodDescAddress != TargetPointer.Null || CodeVersionNodeAddress != TargetPointer.Null;
21+
public bool Valid;
2522
}
2623
```
2724

2825
```csharp
26+
// Return a handle to the active version of the IL code for a given method descriptor
27+
public virtual ILCodeVersionHandle GetActiveILCodeVersion(TargetPointer methodDesc);
28+
// Return a handle to the IL code version representing the given native code version
29+
public virtual ILCodeVersionHandle GetILCodeVersion(NativeCodeVersionHandle codeVersionHandle);
30+
// Return all of the IL code versions for a given method descriptor
31+
public virtual IEnumerable<ILCodeVersionHandle> GetILCodeVersions(TargetPointer methodDesc);
32+
2933
// Return a handle to the version of the native code that includes the given instruction pointer
3034
public virtual NativeCodeVersionHandle GetNativeCodeVersionForIP(TargetCodePointer ip);
31-
// Return a handle to the active version of the native code for a given method descriptor
32-
public virtual NativeCodeVersionHandle GetActiveNativeCodeVersion(TargetPointer methodDesc);
35+
// Return a handle to the active version of the native code for a given method descriptor and IL code version. The IL code version and method descriptor must represent the same method
36+
public virtual NativeCodeVersionHandle GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle);
3337

3438
// returns true if the given method descriptor supports multiple code versions
3539
public virtual bool CodeVersionManagerSupportsMethod(TargetPointer methodDesc);
3640

3741
// Return the instruction pointer corresponding to the start of the given native code version
3842
public virtual TargetCodePointer GetNativeCode(NativeCodeVersionHandle codeVersionHandle);
3943
```
44+
### Extension Methods
45+
```csharp
46+
// Return a handle to the active version of the native code for a given method descriptor
47+
public static NativeCodeVersionHandle GetActiveNativeCodeVersion(this ICodeVersions, TargetPointer methodDesc);
48+
```
4049

4150
## Version 1
4251

@@ -52,11 +61,13 @@ Data descriptors used:
5261
| NativeCodeVersionNode | NativeCode | indicates an explicit native code version node |
5362
| NativeCodeVersionNode | Flags | `NativeCodeVersionNodeFlags` flags, see below |
5463
| NativeCodeVersionNode | VersionId | Version ID corresponding to the parent IL code version |
64+
| ILCodeVersioningState | FirstVersionNode | pointer to the first `ILCodeVersionNode` |
5565
| ILCodeVersioningState | ActiveVersionKind | an `ILCodeVersionKind` value indicating which fields of the active version are value |
5666
| ILCodeVersioningState | ActiveVersionNode | if the active version is explicit, the NativeCodeVersionNode for the active version |
5767
| ILCodeVersioningState | ActiveVersionModule | if the active version is synthetic or unknown, the pointer to the Module that defines the method |
5868
| ILCodeVersioningState | ActiveVersionMethodDef | if the active version is synthetic or unknown, the MethodDef token for the method |
5969
| ILCodeVersionNode | VersionId | Version ID of the node |
70+
| ILCodeVersionNode | Next | Pointer to the next `ILCodeVersionNode`|
6071

6172
The flag indicates that the default version of the code for a method desc is active:
6273
```csharp
@@ -93,6 +104,51 @@ Contracts used:
93104
| Loader |
94105
| RuntimeTypeSystem |
95106

107+
### Finding active ILCodeVersion for a method
108+
```csharp
109+
public virtual ILCodeVersionHandle GetActiveILCodeVersion(TargetPointer methodDesc);
110+
```
111+
1. Check if the method has an `ILCodeVersioningState`.
112+
2. If the method does not have an `ILCodeVersioningState`, the synthetic ILCodeVersion must be active. Return the synthetic ILCodeVersion for the method.
113+
3. Otherwise, read the active ILCodeVersion off of the `ILCodeVersioningState`.
114+
115+
### Finding ILCodeVersion from a NativeCodeVersion
116+
```csharp
117+
public virtual ILCodeVersionHandle GetILCodeVersion(NativeCodeVersionHandle nativeCodeVersionHandle);
118+
```
119+
1. If `nativeCodeVersionHandle` is invalid, return an invalid `ILCodeVersionHandle`.
120+
2. If `nativeCodeVersionHandle` is synthetic, the corresponding ILCodeVersion must also be synthetic; return the synthetic ILCodeVersion for the method.
121+
3. Search the linked list of ILCodeVersions for one with the matching ILVersionId. Return the ILCodeVersion if found. Otherwise return invalid.
122+
123+
### Finding all of the ILCodeVersions for a method
124+
```csharp
125+
IEnumerable<ILCodeVersionHandle> ICodeVersions.GetILCodeVersions(TargetPointer methodDesc)
126+
{
127+
// CodeVersionManager::GetILCodeVersions
128+
GetModuleAndMethodDesc(methodDesc, out TargetPointer module, out uint methodDefToken);
129+
130+
ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module);
131+
TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState;
132+
TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _);
133+
134+
// always add the synthetic version
135+
yield return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null);
136+
137+
// if explicit versions exist, iterate linked list and return them
138+
if (ilVersionStateAddress != TargetPointer.Null)
139+
{
140+
Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd<Data.ILCodeVersioningState>(ilVersionStateAddress);
141+
TargetPointer nodePointer = ilState.FirstVersionNode;
142+
while (nodePointer != TargetPointer.Null)
143+
{
144+
Data.ILCodeVersionNode current = _target.ProcessedData.GetOrAdd<Data.ILCodeVersionNode>(nodePointer);
145+
yield return new ILCodeVersionHandle(TargetPointer.Null, 0, nodePointer);
146+
nodePointer = current.Next;
147+
}
148+
}
149+
}
150+
```
151+
96152
### Finding the start of a specific native code version
97153

98154
```csharp
@@ -113,7 +169,7 @@ NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointe
113169
MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress);
114170
if (!rts.IsVersionable(md))
115171
{
116-
return new NativeCodeVersionHandle(methodDescAddress, codeVersionNodeAddress: TargetPointer.Null);
172+
return NativeCodeVersion.OfSynthetic(methodDescAddress);
117173
}
118174
else
119175
{
@@ -128,7 +184,7 @@ NativeCodeVersionHandle GetSpecificNativeCodeVersion(MethodDescHandle md, Target
128184
TargetCodePointer firstNativeCode = rts.GetNativeCode(md);
129185
if (firstNativeCode == startAddress)
130186
{
131-
NativeCodeVersionHandle first = new NativeCodeVersionHandle(md.Address, TargetPointer.Null);
187+
NativeCodeVersionHandle first = NativeCodeVersionHandle.OfSynthetic(md.Address);
132188
return first;
133189
}
134190

@@ -154,114 +210,23 @@ NativeCodeVersionHandle FindFirstCodeVersion(IRuntimeTypeSystem rts, MethodDescH
154210
Data.NativeCodeVersionNode current = _target.ProcessedData.GetOrAdd<Data.NativeCodeVersionNode>(currentAddress);
155211
if (predicate(current))
156212
{
157-
return new NativeCodeVersionHandle(methodDescAddress: TargetPointer.Null, currentAddress);
213+
return NativeCodeVersionHandle.OfExplicit(currentAddress);
158214
}
159215
currentAddress = current.Next;
160216
}
161217
return NativeCodeVersionHandle.Invalid;
162218
}
163219
```
164220

165-
### Finding the active native code version of a method descriptor
166-
221+
### Finding the active native code version of an ILCodeVersion for a method descriptor
167222
```csharp
168-
NativeCodeVersionHandle ICodeVersions.GetActiveNativeCodeVersion(TargetPointer methodDesc)
169-
{
170-
IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
171-
MethodDescHandle md = rts.GetMethodDescHandle(methodDesc);
172-
TargetPointer mtAddr = rts.GetMethodTable(md);
173-
TypeHandle typeHandle = rts.GetTypeHandle(mtAddr);
174-
TargetPointer module = rts.GetModule(typeHandle);
175-
uint methodDefToken = rts.GetMethodToken(md);
176-
ILCodeVersionHandle methodDefActiveVersion = FindActiveILCodeVersion(module, methodDefToken);
177-
if (!methodDefActiveVersion.IsValid)
178-
{
179-
return NativeCodeVersionHandle.Invalid;
180-
}
181-
return FindActiveNativeCodeVersion(methodDefActiveVersion, methodDesc);
182-
}
183-
184-
ILCodeVersionHandle ILCodeVersionHandleFromState(Data.ILCodeVersioningState ilState)
185-
{
186-
switch ((ILCodeVersionKind)ilState.ActiveVersionKind)
187-
{
188-
case ILCodeVersionKind.Explicit:
189-
return new ILCodeVersionHandle(module: TargetPointer.Null, methodDef: 0, ilState.ActiveVersionNode);
190-
case ILCodeVersionKind.Synthetic:
191-
case ILCodeVersionKind.Unknown:
192-
return new ILCodeVersionHandle(ilState.ActiveVersionModule, ilState.ActiveVersionMethodDef, TargetPointer.Null);
193-
default:
194-
throw new InvalidOperationException($"Unknown ILCodeVersionKind {ilState.ActiveVersionKind}");
195-
}
196-
}
197-
198-
ILCodeVersionHandle FindActiveILCodeVersion(TargetPointer module, uint methodDefinition)
199-
{
200-
ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandle(module);
201-
TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState;
202-
TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefinition, out var _);
203-
if (ilVersionStateAddress == TargetPointer.Null)
204-
{
205-
return new ILCodeVersionHandle(module, methodDefinition, TargetPointer.Null);
206-
}
207-
Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd<Data.ILCodeVersioningState>(ilVersionStateAddress);
208-
return ILCodeVersionHandleFromState(ilState);
209-
}
210-
211-
bool IsActiveNativeCodeVersion(NativeCodeVersionHandle nativeCodeVersion)
212-
{
213-
if (nativeCodeVersion.MethodDescAddress != TargetPointer.Null)
214-
{
215-
MethodDescHandle md = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(nativeCodeVersion.MethodDescAddress);
216-
TargetPointer versioningStateAddress = _target.Contracts.RuntimeTypeSystem.GetMethodDescVersioningState(md);
217-
if (versioningStateAddress == TargetPointer.Null)
218-
{
219-
return true;
220-
}
221-
Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd<Data.MethodDescVersioningState>(versioningStateAddress);
222-
MethodDescVersioningStateFlags flags = (MethodDescVersioningStateFlags)versioningState.Flags;
223-
return flags.HasFlag(MethodDescVersioningStateFlags.IsDefaultVersionActiveChildFlag);
224-
}
225-
else if (nativeCodeVersion.CodeVersionNodeAddress != TargetPointer.Null)
226-
{
227-
uint flags = _target.Read<uint>(nativeCodeVersion.CodeVersionNodeAddress + /* NativeCodVersionNode::Flags offset*/)
228-
return ((NativeCodeVersionNodeFlags)flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
229-
}
230-
else
231-
{
232-
throw new ArgumentException("Invalid NativeCodeVersionHandle");
233-
}
234-
}
235-
236-
NativeCodeVersionHandle FindActiveNativeCodeVersion(ILCodeVersionHandle methodDefActiveVersion, TargetPointer methodDescAddress)
237-
{
238-
TargetNUInt? ilVersionId = default;
239-
if (methodDefActiveVersion.Module != TargetPointer.Null)
240-
{
241-
NativeCodeVersionHandle provisionalHandle = new NativeCodeVersionHandle(methodDescAddress: methodDescAddress, codeVersionNodeAddress: TargetPointer.Null);
242-
if (IsActiveNativeCodeVersion(provisionalHandle))
243-
{
244-
return provisionalHandle;
245-
}
246-
}
247-
else
248-
{
249-
// Get the explicit IL code version
250-
Debug.Assert(methodDefActiveVersion.ILCodeVersionNode != TargetPointer.Null);
251-
ilVersionId = _target.ReadNUint(methodDefActiveVersion.ILCodeVersionNode + /* ILCodeVersionNode::VersionId offset */);
252-
}
253-
254-
// Iterate through versioning state nodes and return the active one, matching any IL code version
255-
Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem;
256-
MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress);
257-
return FindFirstCodeVersion(rts, md, (codeVersion) =>
258-
{
259-
return (!ilVersionId.HasValue || ilVersionId.Value.Value == codeVersion.ILVersionId.Value)
260-
&& ((NativeCodeVersionNodeFlags)codeVersion.Flags).HasFlag(NativeCodeVersionNodeFlags.IsActiveChild);
261-
});
262-
}
223+
public virtual NativeCodeVersionHandle GetActiveNativeCodeVersionForILCodeVersion(TargetPointer methodDesc, ILCodeVersionHandle ilCodeVersionHandle);
263224
```
264225

226+
1. If `ilCodeVersionHandle` is invalid, return invalid.
227+
2. If `ilCodeVersionHandle` is synthetic, the active native code version could be synthetic. Check if the method's synthetic NativeCodeVersion is active. If it is, return that NativeCodeVersion.
228+
3. Search the linked list of NativeCodeVersions for one with the active flag and the relevent ILVersionId. If found return that node. Otherwise return invalid.
229+
265230
### Determining whether a method descriptor supports code versioning
266231

267232
```csharp

docs/design/datacontracts/ReJIT.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,22 @@ This contract encapsulates support for [ReJIT](../features/code-versioning.md) i
44

55
## APIs of contract
66

7+
```csharp
8+
public enum RejitState
9+
{
10+
Requested,
11+
Active
12+
}
13+
```
14+
715
```csharp
816
bool IsEnabled();
17+
18+
RejitState GetRejitState(ILCodeVersionHandle codeVersionHandle);
19+
20+
TargetNUInt GetRejitId(ILCodeVersionHandle codeVersionHandle);
21+
22+
IEnumerable<TargetNUInt> GetRejitIds(TargetPointer methodDesc)
923
```
1024

1125
## Version 1
@@ -14,6 +28,8 @@ Data descriptors used:
1428
| Data Descriptor Name | Field | Meaning |
1529
| --- | --- | --- |
1630
| ProfControlBlock | GlobalEventMask | an `ICorProfiler` `COR_PRF_MONITOR` value |
31+
| ILCodeVersionNode | VersionId | `ILCodeVersion` ReJIT ID
32+
| ILCodeVersionNode | RejitState | a `RejitFlags` value |
1733

1834
Global variables used:
1935
| Global Name | Type | Purpose |
@@ -23,6 +39,7 @@ Global variables used:
2339
Contracts used:
2440
| Contract Name |
2541
| --- |
42+
| CodeVersions |
2643

2744
```csharp
2845
// see src/coreclr/inc/corprof.idl
@@ -32,6 +49,21 @@ private enum COR_PRF_MONITOR
3249
COR_PRF_ENABLE_REJIT = 0x00040000,
3350
}
3451

52+
// see src/coreclr/vm/codeversion.h
53+
[Flags]
54+
public enum RejitFlags : uint
55+
{
56+
kStateRequested = 0x00000000,
57+
58+
kStateGettingReJITParameters = 0x00000001,
59+
60+
kStateActive = 0x00000002,
61+
62+
kStateMask = 0x0000000F,
63+
64+
kSuppressParams = 0x80000000
65+
}
66+
3567
bool IsEnabled()
3668
{
3769
TargetPointer address = target.ReadGlobalPointer("ProfilerControlBlock");
@@ -40,4 +72,56 @@ bool IsEnabled()
4072
bool clrConfigEnabledReJit = /* host process does not have environment variable DOTNET_ProfAPI_ReJitOnAttach set to 0 */;
4173
return profEnabledReJIT || clrConfigEnabledReJIT;
4274
}
75+
76+
RejitState GetRejitState(ILCodeVersionHandle codeVersion)
77+
{
78+
// ILCodeVersion::GetRejitState
79+
if (codeVersion is not explicit)
80+
{
81+
// for non explicit ILCodeVersions, ReJITState is always kStateActive
82+
return RejitState.Active;
83+
}
84+
else
85+
{
86+
// ILCodeVersionNode::GetRejitState
87+
ILCodeVersionNode codeVersionNode = AsNode(codeVersion);
88+
return ((RejitFlags)ilCodeVersionNode.RejitState & RejitFlags.kStateMask) switch
89+
{
90+
RejitFlags.kStateRequested => RejitState.Requested,
91+
RejitFlags.kStateActive => RejitState.Active,
92+
_ => throw new NotImplementedException($"Unknown ReJIT state: {ilCodeVersionNode.RejitState}"),
93+
};
94+
}
95+
}
96+
97+
TargetNUInt GetRejitId(ILCodeVersionHandle codeVersion)
98+
{
99+
// ILCodeVersion::GetVersionId
100+
if (codeVersion is not explicit)
101+
{
102+
// for non explicit ILCodeVersions, ReJITId is always 0
103+
return new TargetNUInt(0);
104+
}
105+
else
106+
{
107+
// ILCodeVersionNode::GetVersionId
108+
ILCodeVersionNode codeVersionNode = AsNode(codeVersion);
109+
return codeVersionNode.VersionId;
110+
}
111+
}
112+
113+
IEnumerable<TargetNUInt> GetRejitIds(TargetPointer methodDesc)
114+
{
115+
// ReJitManager::GetReJITIDs
116+
ICodeVersions cv = _target.Contracts.CodeVersions;
117+
IEnumerable<ILCodeVersionHandle> ilCodeVersions = cv.GetILCodeVersions(methodDesc);
118+
119+
foreach (ILCodeVersionHandle ilCodeVersionHandle in ilCodeVersions)
120+
{
121+
if (GetRejitState(ilCodeVersionHandle) == RejitState.Active)
122+
{
123+
yield return GetRejitId(ilCodeVersionHandle);
124+
}
125+
}
126+
}
43127
```

0 commit comments

Comments
 (0)