Skip to content

Implementation of SOSDacApi GetMethodDescName for cDAC #106169

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

Merged
merged 43 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
255da99
Progress towards GetMethodTableName
davidwrighton Jul 9, 2024
0671cd4
Move metadata reader into helpers
davidwrighton Jul 11, 2024
636bb2b
Simplify storage of saved metadata copy for reflection emit scenarios
davidwrighton Jul 11, 2024
d359ed9
Fix bugs found when trying to actually use this logic
davidwrighton Jul 11, 2024
fc24cd4
Update contract documentation
davidwrighton Jul 11, 2024
d93e98b
Merge branch 'main' into cdac-methodtable-name
davidwrighton Jul 11, 2024
fead409
Feedback
davidwrighton Jul 12, 2024
d2b36fc
Add support for the EEName DacStream
davidwrighton Jul 15, 2024
8fdcc24
- Refactor to expose TypeHandle exclusively from the contracts
davidwrighton Jul 15, 2024
30da32c
Address concern around data structure for DynamicMetadata
davidwrighton Jul 15, 2024
87f7003
Merge branch 'cdac-methodtable-name' of github.com:davidwrighton/runt…
davidwrighton Jul 15, 2024
143cb86
Fix build break
davidwrighton Jul 15, 2024
7284f83
Fix musl build issue
davidwrighton Jul 16, 2024
2d5ec4f
Cherrypick RuntimeTypeSystem changes from #104759
lambdageek Jul 16, 2024
8e2dbca
update tests
lambdageek Jul 16, 2024
e3964f9
start GetMethodDescDataImpl
lambdageek Jul 8, 2024
38d0a51
WIP: managed GetMethodDescData skeleton
lambdageek Jul 8, 2024
9d88f19
wip: MethodDesc
lambdageek Jul 9, 2024
4a4c3a4
add MethodDescChunk
lambdageek Jul 10, 2024
510a10f
WIP: validating a MethodDesc
lambdageek Jul 11, 2024
419cb11
checkpoint: MethodDesc validation
lambdageek Jul 12, 2024
1321aa9
update contract
lambdageek Jul 12, 2024
dcc4540
fix RuntimeTypeSystem unit tests
lambdageek Jul 12, 2024
c31bdcf
update contract
lambdageek Jul 15, 2024
7517e32
fix GetMethodDescChunkPointerMayThrow
lambdageek Jul 15, 2024
54ab23e
update tests
lambdageek Jul 16, 2024
27fcddc
fixup rebase
lambdageek Jul 16, 2024
2a41206
Remove unnecessary usings
davidwrighton Jul 16, 2024
dbe3cf4
- Transform magic numbers from DacStreams implementation into constants
davidwrighton Jul 16, 2024
3a35f41
Feedback
davidwrighton Jul 16, 2024
1eb3b36
Merge branch 'cdac-methodtable-name' into cdac-methoddescname
davidwrighton Jul 17, 2024
785ced4
Add non-contract portion of GetMethodDescName
davidwrighton Jul 19, 2024
01d3d24
Remove default parameter from AppendMethodInternal
davidwrighton Aug 7, 2024
1b6b6fd
Merge branch 'main' of github.com:dotnet/runtime into cdac-methoddesc…
davidwrighton Aug 7, 2024
0c7a254
First stab at the actual contract implementations for the MethodDesc …
davidwrighton Aug 8, 2024
ec9eb54
Commit current state
davidwrighton Aug 8, 2024
54ded9a
It Works!
davidwrighton Aug 8, 2024
3ffb03b
Document the new contracts needed for generating MethodDesc names
davidwrighton Aug 8, 2024
e72cea6
Handle Path fallback scenario. Leave actual implementation/documentat…
davidwrighton Aug 9, 2024
c250bb8
Remove extra lines found in Loader_1.cs
davidwrighton Aug 9, 2024
c3ebfdb
Address code review comments
davidwrighton Aug 9, 2024
bcf9fba
Adjust cdac naming to new model
davidwrighton Aug 12, 2024
de846a5
Merge branch 'main' into cdac-methoddescname
davidwrighton Aug 12, 2024
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
255 changes: 247 additions & 8 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,51 @@ struct MethodDescHandle
}
```

```csharp
public enum ArrayFunctionType
{
Get = 0,
Set = 1,
Address = 2,
Constructor = 3
}
```

```csharp
partial interface IRuntimeTypeSystem : IContract
{
public virtual MethodDescHandle GetMethodDescHandle(TargetPointer methodDescPointer);

public virtual TargetPointer GetMethodTable(MethodDescHandle methodDesc);

// Return true for an uninstantiated generic method
public virtual bool IsGenericMethodDefinition(MethodDescHandle methodDesc);

public virtual ReadOnlySpan<TypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDesc);

// Return mdTokenNil (0x06000000) if the method doesn't have a token, otherwise return the token of the method
public virtual uint GetMethodToken(MethodDescHandle methodDesc);

// Return true if a MethodDesc represents an array method
// An array method is also a StoredSigMethodDesc
public virtual bool IsArrayMethod(MethodDescHandle methodDesc, out ArrayFunctionType functionType);

// Return true if a MethodDesc represents a method without metadata method, either an IL Stub dynamically
// generated by the runtime, or a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
// Or something else similar.
// A no metadata method is also a StoredSigMethodDesc
public virtual bool IsNoMetadataMethod(MethodDescHandle methodDesc, out ReadOnlySpan<byte> methodName);

// A StoredSigMethodDesc is a MethodDesc for which the signature isn't found in metadata.
public virtual bool IsStoredSigMethodDesc(MethodDescHandle methodDesc, out ReadOnlySpan<byte> signature);

// Return true for a MethodDesc that describes a method represented by the System.Reflection.Emit.DynamicMethod class
// A DynamicMethod is also a StoredSigMethodDesc, and a NoMetadataMethod
public virtual bool IsDynamicMethod(MethodDescHandle methodDesc);

// Return true if a MethodDesc represents an IL Stub dynamically generated by the runtime
// A IL Stub method is also a StoredSigMethodDesc, and a NoMetadataMethod
public virtual bool IsILStub(MethodDescHandle methodDesc);
}
```

Expand Down Expand Up @@ -563,7 +602,8 @@ The version 1 `MethodDesc` APIs depend on the `MethodDescAlignment` global and t

| Global name | Meaning |
| --- | --- |
| `MethodDescAlignment` | `MethodDescChunk` trailing data is allocated in multiples of this constant. The size (in bytes) of each `MethodDesc` (or subclass) instance is a multiple of this constant.
| `MethodDescAlignment` | `MethodDescChunk` trailing data is allocated in multiples of this constant. The size (in bytes) of each `MethodDesc` (or subclass) instance is a multiple of this constant. |
| `MethodDescTokenRemainderBitCount` | Number of bits in the token remainder in `MethodDesc` |


In the runtime a `MethodDesc` implicitly belongs to a single `MethodDescChunk` and some common data is shared between method descriptors that belong to the same chunk. A single method table
Expand All @@ -572,12 +612,211 @@ will typically have multiple chunks. There are subkinds of MethodDescs at runti
We depend on the following data descriptors:
| Data Descriptor Name | Field | Meaning |
| --- | --- | --- |
| `MethodDesc` | `ChunkIndex` | Offset of this `MethodDesc` relative to the end of its containing `MethodDescChunk` - in multiples of `MethodDescAlignment`
| `MethodDesc` | `Slot` | The method's slot
| `MethodDesc` | `Flags` | The method's flags
| `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to
| `MethodDescChunk` | `Next` | The next chunk of methods
| `MethodDescChunk` | `Size` | The size of this `MethodDescChunk` following this `MethodDescChunk` header, minus 1. In multiples of `MethodDescAlignment`
| `MethodDescChunk` | `Count` | The number of `MethodDesc` entries in this chunk, minus 1.
| `MethodDesc` | `ChunkIndex` | Offset of this `MethodDesc` relative to the end of its containing `MethodDescChunk` - in multiples of `MethodDescAlignment` |
| `MethodDesc` | `Slot` | The method's slot |
| `MethodDesc` | `Flags` | The method's flags |
| `MethodDesc` | `Flags3AndTokenRemainder` | More flags for the method, and the low bits of the method's token's RID |
| `MethodDescChunk` | `MethodTable` | The method table set of methods belongs to |
| `MethodDescChunk` | `Next` | The next chunk of methods |
| `MethodDescChunk` | `Size` | The size of this `MethodDescChunk` following this `MethodDescChunk` header, minus 1. In multiples of `MethodDescAlignment` |
| `MethodDescChunk` | `Count` | The number of `MethodDesc` entries in this chunk, minus 1. |
| `MethodDescChunk` | `FlagsAndTokenRange` | `MethodDescChunk` flags, and the upper bits of the method token's RID |
| `InstantiatedMethodDesc` | `PerInstInfo` | The pointer to the method's type arguments |
| `InstantiatedMethodDesc` | `Flags2` | Flags for the `InstantiatedMethodDesc` |
| `InstantiatedMethodDesc` | `NumGenericArgs` | How many generic args the method has |
| `StoredSigMethodDesc` | `Sig` | Pointer to a metadata signature |
| `StoredSigMethodDesc` | `cSig` | Count of bytes in the metadata signature |
| `StoredSigMethodDesc` | `ExtendedFlags` | Flags field for the `StoredSigMethodDesc` |
| `DynamicMethodDesc` | `MethodName` | Pointer to Null-terminated UTF8 string describing the Method desc |


And the following enumeration definitions

```csharp
internal enum MethodDescClassification
{
IL = 0, // IL
FCall = 1, // FCall (also includes tlbimped ctor, Delegate ctor)
PInvoke = 2, // PInvoke method
EEImpl = 3, // special method; implementation provided by EE (like Delegate Invoke)
Array = 4, // Array ECall
Instantiated = 5, // Instantiated generic methods, including descriptors
// for both shared and unshared code (see InstantiatedMethodDesc)
ComInterop = 6,
Dynamic = 7, // for method desc with no metadata behind
}

[Flags]
internal enum MethodDescFlags : ushort
{
ClassificationMask = 0x7,
HasNonVtableSlot = 0x0008,
}

internal enum InstantiatedMethodDescFlags2 : ushort
{
KindMask = 0x07,
GenericMethodDefinition = 0x01,
UnsharedMethodInstantiation = 0x02,
SharedMethodInstantiation = 0x03,
WrapperStubWithInstantiations = 0x04,
}

[Flags]
internal enum DynamicMethodDescExtendedFlags : uint
{
IsLCGMethod = 0x00004000,
IsILStub = 0x00008000,
}
```


And the various apis are implemented with the following algorithms

```csharp
public bool IsGenericMethodDefinition(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

if (methodDesc.Classification != MethodDescClassification.Instantiated)
return false;

ushort Flags2 = // Read Flags2 field from InstantiatedMethodDesc contract using address methodDescHandle.Address

return ((int)Flags2 & (int)InstantiatedMethodDescFlags2.KindMask) == (int)InstantiatedMethodDescFlags2.GenericMethodDefinition;
}

public ReadOnlySpan<TypeHandle> GetGenericMethodInstantiation(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

if (methodDesc.Classification != MethodDescClassification.Instantiated)
return default;

TargetPointer dictionaryPointer = // Read PerInstInfo field from InstantiatedMethodDesc contract using address methodDescHandle.Address
if (dictionaryPointer == 0)
return default;

int NumTypeArgs = // Read NumGenericArgs from methodDescHandle.Address using InstantiatedMethodDesc contract
TypeHandle[] instantiation = new TypeHandle[NumTypeArgs];
for (int i = 0; i < NumTypeArgs; i++)
instantiation[i] = GetTypeHandle(_target.ReadPointer(dictionaryPointer + _target.PointerSize * i));

return instantiation;
}

public uint GetMethodToken(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

TargetPointer methodDescChunk = // Using ChunkIndex from methodDesc, compute the wrapping MethodDescChunk

ushort Flags3AndTokenRemainder = // Read Flags3AndTokenRemainder field from MethodDesc contract using address methodDescHandle.Address

ushort FlagsAndTokenRange = // Read FlagsAndTokenRange field from MethodDescChunk contract using address methodDescChunk

int tokenRemainderBitCount = _target.ReadGlobal<byte>(Constants.Globals.MethodDescTokenRemainderBitCount);
int tokenRangeBitCount = 24 - tokenRemainderBitCount;
uint allRidBitsSet = 0xFFFFFF;
uint tokenRemainderMask = allRidBitsSet >> tokenRangeBitCount;
uint tokenRangeMask = allRidBitsSet >> tokenRemainderBitCount;

uint tokenRemainder = (uint)(_desc.Flags3AndTokenRemainder & tokenRemainderMask);
uint tokenRange = ((uint)(_chunk.FlagsAndTokenRange & tokenRangeMask)) << tokenRemainderBitCount;

return 0x06000000 | tokenRange | tokenRemainder;
}

public bool IsArrayMethod(MethodDescHandle methodDescHandle, out ArrayFunctionType functionType)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

if (methodDesc.Classification != MethodDescClassification.Array)
{
functionType = default;
return false;
}

int arrayMethodIndex = methodDesc.Slot - GetNumVtableSlots(GetTypeHandle(methodDesc.MethodTable));

functionType = arrayMethodIndex switch
{
0 => ArrayFunctionType.Get,
1 => ArrayFunctionType.Set,
2 => ArrayFunctionType.Address,
> 3 => ArrayFunctionType.Constructor,
_ => throw new InvalidOperationException()
};

return true;
}

public bool IsNoMetadataMethod(MethodDescHandle methodDescHandle, out ReadOnlySpan<byte> methodName)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

if (methodDesc.Classification != MethodDescClassification.Dynamic)
{
methodName = default;
return false;
}

TargetPointer methodNamePointer = // Read MethodName field from DynamicMethodDesc contract using address methodDescHandle.Address

methodName = // ReadBuffer from target of a utf8 null terminated string, starting at address methodNamePointer
return true;
}

public bool IsStoredSigMethodDesc(MethodDescHandle methodDescHandle, out ReadOnlySpan<byte> signature)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

switch (methodDesc.Classification)
{
case MethodDescClassification.Dynamic:
case MethodDescClassification.EEImpl:
case MethodDescClassification.Array:
break; // These have stored sigs

default:
signature = default;
return false;
}

TargetPointer Sig = // Read Sig field from StoredSigMethodDesc contract using address methodDescHandle.Address
uint cSig = // Read cSig field from StoredSigMethodDesc contract using address methodDescHandle.Address

TargetPointer methodNamePointer = // Read S field from DynamicMethodDesc contract using address methodDescHandle.Address
signature = // Read buffer from target memory starting at address Sig, with cSig bytes in it.
return true;
}

public bool IsDynamicMethod(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

if (methodDesc.Classification != MethodDescClassification.Dynamic)
{
return false;
}

uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address

return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsLCGMethod);
}

public bool IsILStub(MethodDescHandle methodDescHandle)
{
MethodDesc methodDesc = _methodDescs[methodDescHandle.Address];

if (methodDesc.Classification != MethodDescClassification.Dynamic)
{
return false;
}

uint ExtendedFlags = // Read ExtendedFlags field from StoredSigMethodDesc contract using address methodDescHandle.Address

return ((DynamicMethodDescExtendedFlags)ExtendedFlags).HasFlag(DynamicMethodDescExtendedFlags.IsILStub);
}
```
**TODO(cdac)**
13 changes: 13 additions & 0 deletions src/coreclr/debug/daccess/dacfn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,15 @@ DacAllocHostOnlyInstance(ULONG32 size, bool throwEx)
return inst + 1;
}

thread_local bool t_DacAssertsUnconditionally = false;

bool DacSetEnableDacAssertsUnconditionally(bool enable)
{
bool oldValue = t_DacAssertsUnconditionally;
t_DacAssertsUnconditionally = enable;
return oldValue;
}

//
// Queries whether ASSERTs should be raised when inconsistencies in the target are detected
//
Expand All @@ -1404,6 +1413,10 @@ bool DacTargetConsistencyAssertsEnabled()
return true;
}

// If asserts are unconditionally enabled via the thread local, simply return true.
if (t_DacAssertsUnconditionally)
return true;

return g_dacImpl->TargetConsistencyAssertsEnabled();
}

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,7 @@ class ClrDataAccess
HRESULT GetObjectStringDataImpl(CLRDATA_ADDRESS obj, unsigned int count, _Inout_updates_z_(count) WCHAR *stringData, unsigned int *pNeeded);
HRESULT GetUsefulGlobalsImpl(struct DacpUsefulGlobalsData *globalsData);
HRESULT GetMethodDescDataImpl(CLRDATA_ADDRESS methodDesc, CLRDATA_ADDRESS ip, struct DacpMethodDescData *data, ULONG cRevertedRejitVersions, DacpReJitData * rgRevertedRejitData, ULONG * pcNeededRevertedRejitData);
HRESULT GetMethodDescNameImpl(CLRDATA_ADDRESS methodDesc, unsigned int count, _Inout_updates_z_(count) WCHAR *name, unsigned int *pNeeded);

BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
#ifndef TARGET_UNIX
Expand Down
Loading
Loading