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] start of RuntimeTypeSystem contract; implement GetMethodTableData SOS method #103444

Merged
merged 72 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
b98afa8
Implement GetThreadStoreData in cDAC
elinor-fung May 16, 2024
890f9c6
Add placeholder for getting thread data
elinor-fung May 17, 2024
41ba95c
Apply suggestions from code review
elinor-fung May 23, 2024
29214f0
WIP: Metadata contract
lambdageek May 14, 2024
8beced0
Merge remote-tracking branch 'elinor-fung/cdac-threadstore' into cdac…
lambdageek May 29, 2024
5213286
fix build
lambdageek May 29, 2024
622e01a
WIP: ValidateMethodTable
lambdageek May 29, 2024
e112416
DataCache.GetOrAdd
elinor-fung May 29, 2024
ae1eac9
wip
lambdageek May 30, 2024
79ea0d4
Merge remote-tracking branch 'elinor-fung/cdac-threadstore' into cdac…
lambdageek May 30, 2024
5c696da
checkpoint: ValidateWithPossibleAV
lambdageek May 30, 2024
6eaf80f
Merge remote-tracking branch 'origin/main' into cdac-wip
lambdageek May 30, 2024
3a7808d
checkpoint EEClass from MethodTable
lambdageek May 31, 2024
66e5476
checkpoint: ValidateMethodTablePointer
lambdageek May 31, 2024
95914b8
cp: delegate from legacy dac
lambdageek May 31, 2024
2b8fda3
add Metadata to runtime contract descriptor
lambdageek Jun 11, 2024
5c7d2ac
checkpoint: more MethodTable fields
lambdageek Jun 12, 2024
7be5c60
checkpoint GetMethodTableData implemented
lambdageek Jun 13, 2024
30b7b26
checkpoint: same answer as legacy dac
lambdageek Jun 13, 2024
187bcbe
Merge remote-tracking branch 'origin/main' into cdac-wip
lambdageek Jun 14, 2024
f0d1fcb
new flags for statics
lambdageek Jun 14, 2024
c53db36
fix GCC build
lambdageek Jun 14, 2024
b70bb1d
WIP: opaque MethodTableHandle
lambdageek Jun 17, 2024
a65fd50
Add contract accessors for MethodTableHandle
lambdageek Jun 18, 2024
6f844bf
fixup
lambdageek Jun 18, 2024
76e0384
simplify FreeObjectMethodTable handling
lambdageek Jun 18, 2024
cdb7543
cleanup
lambdageek Jun 18, 2024
78830ea
fixup
lambdageek Jun 18, 2024
fbbd45b
Merge remote-tracking branch 'origin/main' into cdac-wip
lambdageek Jun 18, 2024
32665b2
[dac] Return canonical MethodTable instead of EEClass
lambdageek Jun 18, 2024
acf8436
Delete unreferenced MethodTable flags
lambdageek Jun 18, 2024
f246c86
add Metadata contract doc; fixups
lambdageek Jun 19, 2024
8409f3e
Merge remote-tracking branch 'origin/main' into cdac-wip
lambdageek Jun 20, 2024
08d069a
rename DacpMethodTableData:klass field
lambdageek Jun 20, 2024
674655f
document GetMethodTableData string baseSize adjustment
lambdageek Jun 20, 2024
2ae4625
Apply suggestions from code review
lambdageek Jun 20, 2024
e18a2be
fix typo
lambdageek Jun 21, 2024
846e779
rename flag to ContainsGCPointers
lambdageek Jun 21, 2024
383af83
[vm] rename ContainsPointers flag to ContainsGCPointers
lambdageek Jun 21, 2024
0f8c7f1
code style suggestions from code review
lambdageek Jun 21, 2024
7a337c1
BUGFIX: read DwFlags2 from the correct offset
lambdageek Jun 21, 2024
a7c8158
hide utility methods
lambdageek Jun 21, 2024
f4a3493
remove EEClass_1 struct
lambdageek Jun 21, 2024
10624c7
Merge remote-tracking branch 'origin/main' into cdac-wip
lambdageek Jun 21, 2024
65cc531
rename data descriptor members to remove prefixes
lambdageek Jun 21, 2024
d526087
cleanup the contract docs
lambdageek Jun 21, 2024
0a4112e
remove hungariant notation prefixes from data descriptors
lambdageek Jun 24, 2024
1071ca4
DAC: always set wNumVirtuals and wNumVtableSlots to 0
lambdageek Jun 25, 2024
6c5235c
Remove NumVirtuals and NumVtableSlots from Metadata.md
lambdageek Jun 27, 2024
95b728a
Merge remote-tracking branch 'origin/main' into cdac-wip
lambdageek Jun 28, 2024
6573e14
"untrusted" -> "non-validated"
lambdageek Jun 28, 2024
8596892
merge fixup
lambdageek Jun 28, 2024
6eabf42
remove #if 0
lambdageek Jun 28, 2024
4d3200d
cleanup
lambdageek Jun 28, 2024
2e66740
pull test target helpers out
lambdageek Jun 28, 2024
8533148
Add one FreeObjectMethodTable unit test
lambdageek Jul 1, 2024
77cf405
clean up the test helpers a bit
lambdageek Jul 2, 2024
f9bce4c
validate that a mock system object is a valid method table
lambdageek Jul 2, 2024
3721992
code review feedback and more tests:
lambdageek Jul 2, 2024
1af7c80
Update src/coreclr/gc/env/gcenv.object.h
lambdageek Jul 2, 2024
993ae1d
Update src/native/managed/cdacreader/src/Contracts/Metadata_1.MethodT…
lambdageek Jul 2, 2024
f04d880
Address code review feedback
lambdageek Jul 2, 2024
76859d1
move non-validated MethodTable handling to a separate class
lambdageek Jul 2, 2024
a12a407
clear up ComponentSize contract spec and impl
lambdageek Jul 2, 2024
9cf4c5a
rename Metadata -> RuntimeTypeSystem
lambdageek Jul 3, 2024
1814848
add validation failure test; change validation to throw InvalidOperat…
lambdageek Jul 3, 2024
89f98a3
Merge remote-tracking branch 'origin/main' into cdac-wip
lambdageek Jul 3, 2024
a0989fa
Update src/native/managed/cdacreader/src/Contracts/RuntimeTypeSystem_…
lambdageek Jul 3, 2024
617bf62
spellcheck
lambdageek Jul 3, 2024
815ff0d
Merge branch 'cdac-wip' of github.com:lambdageek/runtime into cdac-wip
lambdageek Jul 3, 2024
1ab4f08
Add a generic instance test
lambdageek Jul 3, 2024
ee3a362
add array instance test
lambdageek Jul 3, 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
287 changes: 287 additions & 0 deletions docs/design/datacontracts/Metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
# Contract Metadata

This contract is for exploring the properties of the metadata of values on the heap or on the stack in a .NET process.

## APIs of contract

A `MethodTable` is the runtime representation of the type information about a value. Given a `TargetPointer` address, the `Metadata` contract provides a `MethodTableHandle` for querying the `MethodTable`.

``` csharp
struct MethodTableHandle
{
// no public properties or constructors

internal TargetPointer Address { get; }
}
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
```

``` csharp
#region MethodTable inspection APIs
public virtual MethodTableHandle GetMethodTableHandle(TargetPointer targetPointer) => throw new NotImplementedException();

public virtual TargetPointer GetModule(MethodTableHandle methodTable) => throw new NotImplementedException();
// A canonical method table is either the MethodTable itself, or in the case of a generic instantiation, it is the
// MethodTable of the prototypical instance.
public virtual TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException();
public virtual TargetPointer GetParentMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException();

public virtual uint GetBaseSize(MethodTableHandle methodTable) => throw new NotImplementedException();
// The component size is only available for strings and arrays. It is the size of the element type of the array, or the size of an ECMA 335 character (2 bytes)
public virtual uint GetComponentSize(MethodTableHandle methodTable) => throw new NotImplementedException();

// True if the MethodTable is the sentinel value associated with unallocated space in the managed heap
public virtual bool IsFreeObjectMethodTable(MethodTableHandle methodTable) => throw new NotImplementedException();
public virtual bool IsString(MethodTableHandle methodTable) => throw new NotImplementedException();
// True if the MethodTable represents a type that contains managed references
public virtual bool ContainsPointers(MethodTableHandle methodTable) => throw new NotImplementedException();
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
public virtual bool IsDynamicStatics(MethodTableHandle methodTable) => throw new NotImplementedException();
public virtual ushort GetNumMethods(MethodTableHandle methodTable) => throw new NotImplementedException();
public virtual ushort GetNumInterfaces(MethodTableHandle methodTable) => throw new NotImplementedException();
public virtual ushort GetNumVirtuals(MethodTableHandle methodTable) => throw new NotImplementedException();
public virtual ushort GetNumVtableSlots(MethodTableHandle methodTable) => throw new NotImplementedException();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would having a singe API for getting slot counts (or maybe also method/field counts) be reasonable, or do you think keeping everything separate makes more sense? I'm wondering if there are any logical groupings that work for all the Get / Is APIs on here.

Copy link
Member

@jkotas jkotas Jun 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would question the diagnostic tools need for these numbers. These numbers go into the MethodTable data layout details. Are the number of slots actually needed by anything else except some SOS command printing them?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this a little bit in our standup. The explanation was that some of these fields, while they're only used for SOS command output are important for a number of reasons:

  1. Some 3rd party tooling scrapes SOS output. So eliminating the slot counts from the output entirely or changing the ouptut format too drastically may end up breaking those uses
  2. The actual values are useful for engineers working on bringing up the runtime on new platforms

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry I was not able to make it to the standup today.

Some 3rd party tooling scrapes SOS output.

I do not see a problem with that. The screen scrapping done by 3rd party tools should not prevent us from making the SOS better.

The actual values are useful for engineers working on bringing up the runtime on new platforms

The main value of SOS dump commands - for both platform bring up and general debugging - is in displaying information that is hard (labor intensive) to get manually via regular windbg/lldb commands. Type name is an example of such information.

NumVtableSlots is easy to get using regular windbg/lldb commands, only needed to interpret data layout of the methodtable, and rarely used for low-level debugging. It does not need to be part of Dump... output. I would even say that Dump... would be better without it.. We should be free to delete it to make SOS better. dotnet/coreclr#16560 or dotnet/diagnostics#4751 are examples of changes like that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see a problem with that. The screen scrapping done by 3rd party tools should not prevent us from making the SOS better.

I agree with this in the long term and how we should frame the ultimate outcome of the cDAC workstream. For these initial phases though I tend to consider them more proving this can be done in a seamless and transparent manner. This implies ensuring we maintain the illusion of the static nature should be a priority until we can announce changes in this space and start to innovate. Basically, for now I think we should match precise output unless there is a compelling or hard-blocking reason to deviate from that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying that we should revert 32665b2 to match existing output and that we should avoid dotnet/diagnostics#4751 (comment)? Both of these changes can break screen scraping tools.

As an initial goal, I would've said "yes, we should stick to avoiding breaking changes in this space". However, it seems like that ship has already sailed. I want to innovate in this space a great deal so as an end goal I agree. My concern was and is the burden this can place on automation that relies on scapping. We end up encouraging odd solutions as people try to anticipate or adapt to our "infight work". Since it seems that isn't a current goal, I agree then that we should remove as much cruft as possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps there should be an SOS query that can help scrapping tooling know what they can expect? Is a new SOS command along the lines of !sos.version appropriate in this case? I truly don't know how onerous this is so I could be concerned about something that has limited impact. Perhaps @leculver can shed some light on a version command option, if one doesn't already exist.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the current PR to always set DacpMethodTableData::wNumVirtuals and DacpMethodTableData::wNumVtableSlots to 0 in both the cDAC and the Legacy DAC. Updated the Metadata contract to exclude getters for this info.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related change in dotnet/diagnostics to not print the counts if they're 0 dotnet/diagnostics#4760

I checked and https://github.com/Microsoft/clrmd doesn't use these fields. They only look at wNumMethods

Copy link
Contributor

@leculver leculver Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps there should be an SOS query that can help scrapping tooling know what they can expect? Is a new SOS command along the lines of !sos.version appropriate in this case? I truly don't know how onerous this is so I could be concerned about something that has limited impact. Perhaps @leculver can shed some light on a version command option, if one doesn't already exist.

Sorry I missed this ping.

My guiding principle is: When possible, and when it doesn't cause too much work, don't regress or take back features that affect debugging. That includes .Net Core and Desktop Framework (since we can debug 4.5+ desktop framework with current SOS).

That's my only real request.

Perhaps there should be an SOS query that can help scrapping tooling know what they can expect? Is a new SOS command along the lines of !sos.version appropriate in this case?

I don't think we need that. The output of SOS does not constitute an interface. I've been saying this for 15 years and will die on this hill. :)

We shouldn't bend over backwards to accommodate screen scraping. Literally the original reason ClrMD was built was to replace screen-scraping SOS. We don't have all functionality built into ClrMD, but the SOS source is open-source and folks can reimplement functionality from there if they need to.

I haven't fully dug into everything here, but I'm fine with these changes. I'd honestly be fine if folks wanted to randomize the output of SOS to intentionally break screen scraping. (Hyperbole. Obviously, we would never actually do that.) I owned SOS 15 years ago and built a lot of its functionality. Screen scraping was the bane of my existence because we couldn't do anything to SOS in the presence of screen scraping. Just use ClrMD if you need that programmatic access! No screen scraping!


// Returns an ECMA-335 TypeDef table token for this type, or for its generic type definition if it is a generic instantiation
public virtual uint GetTypeDefToken(MethodTableHandle methodTable) => throw new NotImplementedException();
// Returns the ECMA 335 TypeDef table Flags value (a bitmask of TypeAttributes) for this type,
// or for its generic type definition if it is a generic instantiation
public virtual uint GetTypeDefTypeAttributes(MethodTableHandle methodTable) => throw new NotImplementedException();
#endregion MethodTable inspection APIs
```

## Version 1

The `MethodTable` inspection APIs are implemented in terms of the following flags on the runtime `MethodTable` structure:

``` csharp
internal partial struct Metadata_1
{
[Flags]
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
internal enum WFLAGS_LOW : uint
{
GenericsMask = 0x00000030,
GenericsMask_NonGeneric = 0x00000000, // no instantiation

StringArrayValues =
GenericsMask_NonGeneric |
0,
}

[Flags]
internal enum WFLAGS_HIGH : uint
{
Category_Mask = 0x000F0000,
Category_Array = 0x00080000,
Category_Array_Mask = 0x000C0000,
Category_Interface = 0x000C0000,
ContainsPointers = 0x01000000, // Contains object references
HasComponentSize = 0x80000000, // This is set if component size is used for flags.
}

[Flags]
internal enum WFLAGS2_ENUM : uint
{
DynamicStatics = 0x0002,
}

internal struct MethodTableFlags
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
{
public uint DwFlags { get; init; }
public uint DwFlags2 { get; init; }
public uint BaseSize { get; init; }

private WFLAGS_HIGH FlagsHigh => (WFLAGS_HIGH)DwFlags;
private WFLAGS_LOW FlagsLow => (WFLAGS_LOW)DwFlags;
public int GetTypeDefRid() => (int)(DwFlags2 >> Constants.MethodTableDwFlags2TypeDefRidShift);

public WFLAGS_LOW GetFlag(WFLAGS_LOW mask) => throw new NotImplementedException("TODO");
public WFLAGS_HIGH GetFlag(WFLAGS_HIGH mask) => FlagsHigh & mask;

public WFLAGS2_ENUM GetFlag(WFLAGS2_ENUM mask) => (WFLAGS2_ENUM)DwFlags2 & mask;
public bool IsInterface => GetFlag(WFLAGS_HIGH.Category_Mask) == WFLAGS_HIGH.Category_Interface;
public bool IsString => HasComponentSize && !IsArray && RawGetComponentSize() == 2;

public bool HasComponentSize => GetFlag(WFLAGS_HIGH.HasComponentSize) != 0;

public bool IsArray => GetFlag(WFLAGS_HIGH.Category_Array_Mask) == WFLAGS_HIGH.Category_Array;

public bool IsStringOrArray => HasComponentSize;
public ushort RawGetComponentSize() => (ushort)(DwFlags >> 16);

public bool TestFlagWithMask(WFLAGS_LOW mask, WFLAGS_LOW flag)
{
if (IsStringOrArray)
{
return (WFLAGS_LOW.StringArrayValues & mask) == flag;
}
else
{
return (FlagsLow & mask) == flag;
}
}

public bool TestFlagWithMask(WFLAGS2_ENUM mask, WFLAGS2_ENUM flag)
{
return ((WFLAGS2_ENUM)DwFlags2 & mask) == flag;
}

public bool HasInstantiation => !TestFlagWithMask(WFLAGS_LOW.GenericsMask, WFLAGS_LOW.GenericsMask_NonGeneric);

public bool ContainsPointers => GetFlag(WFLAGS_HIGH.ContainsPointers) != 0;
}

internal static class Constants
{
internal const int MethodTableDwFlags2TypeDefRidShift = 8;
}

[Flags]
internal enum EEClassOrCanonMTBits
{
EEClass = 0,
CanonMT = 1,
Mask = 1,
}
```

Internally the contract has structs `MethodTable_1` and `EEClass_1`

```csharp
internal struct MethodTable_1
{
internal Metadata_1.MethodTableFlags Flags { get; }
internal ushort NumInterfaces { get; }
internal ushort NumVirtuals { get; }
internal TargetPointer ParentMethodTable { get; }
internal TargetPointer Module { get; }
internal TargetPointer EEClassOrCanonMT { get; }
internal MethodTable_1(Data.MethodTable data)
{
Flags = new Metadata_1.MethodTableFlags
{
DwFlags = data.DwFlags,
DwFlags2 = data.DwFlags2,
BaseSize = data.BaseSize,
};
NumInterfaces = data.NumInterfaces;
NumVirtuals = data.NumVirtuals;
EEClassOrCanonMT = data.EEClassOrCanonMT;
Module = data.Module;
ParentMethodTable = data.ParentMethodTable;
}
}

internal struct EEClass_1
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
{
internal TargetPointer MethodTable { get; }
internal ushort NumMethods { get; }
internal ushort NumNonVirtualSlots { get; }
internal uint TypeDefTypeAttributes { get; }
internal EEClass_1(Data.EEClass eeClassData)
{
MethodTable = eeClassData.MethodTable;
NumMethods = eeClassData.NumMethods;
NumNonVirtualSlots = eeClassData.NumNonVirtualSlots;
TypeDefTypeAttributes = eeClassData.DwAttrClass;
}
}
```

```csharp
private readonly Dictionary<TargetPointer, MethodTable_1> _methodTables;
private readonly TargetPointer _freeObjectMethodTablePointer;

internal TargetPointer FreeObjectMethodTablePointer => _freeObjectMethodTablePointer;

public MethodTableHandle GetMethodTableHandle(TargetPointer methodTablePointer)
{
...
}

internal static EEClassOrCanonMTBits GetEEClassOrCanonMTBits(TargetPointer eeClassOrCanonMTPtr)
{
return (EEClassOrCanonMTBits)(eeClassOrCanonMTPtr & (ulong)EEClassOrCanonMTBits.Mask);
}

public uint GetBaseSize(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.BaseSize;

public uint GetComponentSize(MethodTableHandle methodTableHandle) => GetComponentSize(_methodTables[methodTableHandle.Address]);

private EEClass_1 GetClassData(MethodTableHandle methodTableHandle)
{
TargetPointer clsPtr = GetClass(methodTableHandle);
// Check if we cached it already
if (_target.ProcessedData.TryGet(clsPtr, out Data.EEClass? eeClassData))
{
return new EEClass_1(eeClassData);
}
eeClassData = _target.ProcessedData.GetOrAdd<Data.EEClass>(clsPtr);
return new EEClass_1(eeClassData);
}

private TargetPointer GetClass(MethodTableHandle methodTableHandle)
{
MethodTable_1 methodTable = _methodTables[methodTableHandle.Address];
switch (GetEEClassOrCanonMTBits(methodTable.EEClassOrCanonMT))
{
case EEClassOrCanonMTBits.EEClass:
return methodTable.EEClassOrCanonMT;
case EEClassOrCanonMTBits.CanonMT:
TargetPointer canonMTPtr = new TargetPointer((ulong)methodTable.EEClassOrCanonMT & ~(ulong)Metadata_1.EEClassOrCanonMTBits.Mask);
MethodTableHandle canonMTHandle = GetMethodTableHandle(canonMTPtr);
MethodTable_1 canonMT = _methodTables[canonMTHandle.Address];
return canonMT.EEClassOrCanonMT; // canonical method table EEClassOrCanonMT is always EEClass
default:
throw new InvalidOperationException();
}
}
public TargetPointer GetCanonicalMethodTable(MethodTableHandle methodTableHandle) => GetClassData(methodTableHandle).MethodTable;

public TargetPointer GetModule(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Module;
public TargetPointer GetParentMethodTable(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].ParentMethodTable;

public bool IsFreeObjectMethodTable(MethodTableHandle methodTableHandle) => FreeObjectMethodTablePointer == methodTableHandle.Address;

public bool IsString(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.IsString;
public bool ContainsPointers(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.ContainsPointers;

public uint GetTypeDefToken(MethodTableHandle methodTableHandle)
{
MethodTable_1 methodTable = _methodTables[methodTableHandle.Address];
return (uint)(methodTable.Flags.GetTypeDefRid() | ((int)TableIndex.TypeDef << 24));
}

public ushort GetNumMethods(MethodTableHandle methodTableHandle)
{
EEClass_1 cls = GetClassData(methodTableHandle);
return cls.NumMethods;
}

public ushort GetNumInterfaces(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].NumInterfaces;

public ushort GetNumVirtuals(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].NumVirtuals;
private ushort GetNumNonVirtualSlots(MethodTableHandle methodTableHandle)
{
MethodTable_1 methodTable = _methodTables[methodTableHandle.Address];
TargetPointer eeClassOrCanonMT = methodTable.EEClassOrCanonMT;
if (GetEEClassOrCanonMTBits(eeClassOrCanonMT) == EEClassOrCanonMTBits.EEClass)
{
return GetClassData(methodTableHandle).NumNonVirtualSlots;
}
else
{
return 0;
}
}

public ushort GetNumVtableSlots(MethodTableHandle methodTableHandle)
{
return checked((ushort)(GetNumVirtuals(methodTableHandle) + GetNumNonVirtualSlots(methodTableHandle)));
}

public uint GetTypeDefTypeAttributes(MethodTableHandle methodTableHandle)
{
return GetClassData(methodTableHandle).TypeDefTypeAttributes;
}

public bool IsDynamicStatics(MethodTableHandle methodTableHandle) => _methodTables[methodTableHandle.Address].Flags.GetFlag(WFLAGS2_ENUM.DynamicStatics) != 0;
```
2 changes: 2 additions & 0 deletions src/coreclr/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,8 @@ class ClrDataAccess

HRESULT GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData);
HRESULT GetThreadStoreDataImpl(struct DacpThreadStoreData *data);
HRESULT GetMethodTableDataImpl(CLRDATA_ADDRESS mt, struct DacpMethodTableData *data);
HRESULT GetMethodTableForEEClassImpl (CLRDATA_ADDRESS eeClassReallyMT, CLRDATA_ADDRESS *value);

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