diff --git a/docs/design/datacontracts/DacStreams.md b/docs/design/datacontracts/DacStreams.md index 4ddc1eb84ad3ab..a2da0c4ab1eaf3 100644 --- a/docs/design/datacontracts/DacStreams.md +++ b/docs/design/datacontracts/DacStreams.md @@ -54,8 +54,8 @@ Following the EENameStream header, there are CountOfNames entries. Each entry be ``` csharp string StringFromEEAddress(TargetPointer address) { - TargetPointer miniMetaDataBuffAddress = _target.Read(_target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffAddress)); - uint miniMetaDataBuffMaxSize = _target.Read(_target.ReadGlobalPointer(Constants.Globals.MiniMetaDataBuffMaxSize)); + TargetPointer miniMetaDataBuffAddress = _target.Read(_target.ReadGlobalPointer("MiniMetaDataBuffAddress")); + uint miniMetaDataBuffMaxSize = _target.Read(_target.ReadGlobalPointer("MiniMetaDataBuffMaxSize")); // Parse MiniMetadataStream according the the format described above to produce a dictionary from pointer to string from the EENameStream. // Then lookup in the dictionary, to produce a result if it was present in the table. diff --git a/docs/design/datacontracts/Loader.md b/docs/design/datacontracts/Loader.md index be25e4cba9787d..46fa6b716fb651 100644 --- a/docs/design/datacontracts/Loader.md +++ b/docs/design/datacontracts/Loader.md @@ -284,14 +284,14 @@ IEnumerable GetModuleHandles(TargetPointer appDomain, AssemblyIter TargetPointer GetRootAssembly() { - TargetPointer appDomainPointer = target.ReadGlobalPointer(Constants.Globals.AppDomain); + TargetPointer appDomainPointer = target.ReadGlobalPointer("AppDomain"); AppDomain appDomain = // read AppDomain object starting at appDomainPointer return appDomain.RootAssembly; } string ILoader.GetAppDomainFriendlyName() { - TargetPointer appDomainPointer = target.ReadGlobalPointer(Constants.Globals.AppDomain); + TargetPointer appDomainPointer = target.ReadGlobalPointer("AppDomain"); TargetPointer appDomain = target.ReadPointer(appDomainPointer) TargetPointer pathStart = appDomain + /* AppDomain::FriendlyName offset */; char[] name = // Read from target starting at pathStart until null terminator @@ -490,7 +490,7 @@ bool IsAssemblyLoaded(ModuleHandle handle) TargetPointer GetGlobalLoaderAllocator() { - TargetPointer systemDomainPointer = target.ReadGlobalPointer(Constants.Globals.SystemDomain); + TargetPointer systemDomainPointer = target.ReadGlobalPointer("SystemDomain"); TargetPointer systemDomain = target.ReadPointer(systemDomainPointer); return target.ReadPointer(systemDomain + /* SystemDomain::GlobalLoaderAllocator offset */); } diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index 6ce096ea37914a..6a3b9544bbdc50 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -67,6 +67,8 @@ partial interface IRuntimeTypeSystem : IContract public TargetPointer GetGCThreadStaticsBasePointer(TypeHandle typeHandle, TargetPointer threadPtr); public TargetPointer GetNonGCThreadStaticsBasePointer(TypeHandle typeHandle, TargetPointer threadPtr); public TargetPointer GetFieldDescList(TypeHandle typeHandle); + public TargetPointer GetGCStaticsBasePointer(TypeHandle typeHandle); + public TargetPointer GetNonGCStaticsBasePointer(TypeHandle typeHandle); public virtual ReadOnlySpan GetInstantiation(TypeHandle typeHandle); public virtual bool IsGenericTypeDefinition(TypeHandle typeHandle); @@ -337,6 +339,7 @@ The contract depends on the following globals | Global name | Meaning | | --- | --- | | `FreeObjectMethodTablePointer` | A pointer to the address of a `MethodTable` used by the GC to indicate reclaimed memory +| `StaticsPointerMask` | For masking out a bit of DynamicStaticsInfo pointer fields The contract additionally depends on these data descriptors @@ -351,6 +354,10 @@ The contract additionally depends on these data descriptors | `MethodTable` | `NumInterfaces` | Number of interfaces of `MethodTable` | | `MethodTable` | `NumVirtuals` | Number of virtual methods in `MethodTable` | | `MethodTable` | `PerInstInfo` | Either the array element type, or pointer to generic information for `MethodTable` | +| `MethodTable` | `AuxiliaryData` | Pointer to the AuxiliaryData of a method table | +| `DynamicStaticsInfo` | `NonGCStatics` | Pointer to non-GC statics | +| `DynamicStaticsInfo` | `GCStatics` | Pointer to the GC statics | +| `DynamicStaticsInfo` | `Size` | Size of the data | | `ThreadStaticsInfo` | `GCTlsIndex` | Pointer to GC thread local storage index | | `ThreadStaticsInfo` | `NonGCTlsIndex` | Pointer to non-GC thread local storage index | | `EEClass` | `InternalCorElementType` | An InternalCorElementType uses the enum values of a CorElementType to indicate some of the information about the type of the type which uses the EEClass In particular, all reference types are CorElementType.Class, Enums are the element type of their underlying type and ValueTypes which can exactly be represented as an element type are represented as such, all other values types are represented as CorElementType.ValueType. | @@ -472,6 +479,36 @@ Contracts used: public TargetPointer GetFieldDescList(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(typeHandle).FieldDescList; + public TargetPointer GetGCStaticsBasePointer(TypeHandle typeHandle) + { + if (!typeHandle.IsMethodTable()) + return TargetPointer.Null; + + MethodTable methodTable = _methodTables[typeHandle.Address]; + if (!methodTable.Flags.IsDynamicStatics) + return TargetPointer.Null; + TargetPointer dynamicStaticsInfoSize = target.GetTypeInfo(DataType.DynamicStaticsInfo).Size!.Value; + TargetPointer mask = target.ReadGlobalPointer("StaticsPointerMask"); + + TargetPointer dynamicStaticsInfo = methodTable.AuxiliaryData - dynamicStaticsInfoSize; + return (target.ReadPointer(dynamicStaticsInfo + /* DynamicStaticsInfo::GCStatics offset */) & (ulong)mask); + } + + public TargetPointer GetNonGCStaticsBasePointer(TypeHandle typeHandle) + { + if (!typeHandle.IsMethodTable()) + return TargetPointer.Null; + + MethodTable methodTable = _methodTables[typeHandle.Address]; + if (!methodTable.Flags.IsDynamicStatics) + return TargetPointer.Null; + TargetPointer dynamicStaticsInfoSize = target.GetTypeInfo(DataType.DynamicStaticsInfo).Size!.Value; + TargetPointer mask = target.ReadGlobalPointer("StaticsPointerMask"); + + TargetPointer dynamicStaticsInfo = methodTable.AuxiliaryData - dynamicStaticsInfoSize; + return (target.ReadPointer(dynamicStaticsInfo + /* DynamicStaticsInfo::NonGCStatics offset */) & (ulong)mask); + } + public TargetPointer GetGCThreadStaticsBasePointer(TypeHandle typeHandle, TargetPointer threadPtr) { if (!typeHandle.IsMethodTable()) @@ -918,7 +955,7 @@ And the various apis are implemented with the following algorithms ushort FlagsAndTokenRange = // Read FlagsAndTokenRange field from MethodDescChunk contract using address methodDescChunk - int tokenRemainderBitCount = _target.ReadGlobal(Constants.Globals.MethodDescTokenRemainderBitCount); + int tokenRemainderBitCount = _target.ReadGlobal("MethodDescTokenRemainderBitCount"); int tokenRangeBitCount = 24 - tokenRemainderBitCount; uint allRidBitsSet = 0xFFFFFF; uint tokenRemainderMask = allRidBitsSet >> tokenRangeBitCount; diff --git a/docs/design/datacontracts/StressLog.md b/docs/design/datacontracts/StressLog.md index f4630b391a2d21..3b90a216cfdf50 100644 --- a/docs/design/datacontracts/StressLog.md +++ b/docs/design/datacontracts/StressLog.md @@ -95,7 +95,7 @@ StressLogData GetStressLogData() return default; } - StressLog stressLog = new StressLog(Target, Target.ReadGlobalPointer(Constants.Globals.StressLog)); + StressLog stressLog = new StressLog(Target, Target.ReadGlobalPointer("StressLog")); return new StressLogData( stressLog.LoggedFacilities, stressLog.Level, @@ -210,9 +210,9 @@ bool IsPointerInStressLog(StressLogData stressLog, TargetPointer pointer) // This method is a helper for the various specific versions. protected TargetPointer GetFormatPointer(ulong formatOffset) { - if (Target.ReadGlobal(Constants.Globals.StressLogHasModuleTable) == 0) + if (Target.ReadGlobal("StressLogHasModuleTable") == 0) { - StressLog stressLog = new(Target, target.ReadGlobalPointer(Constants.Globals.StressLog)); + StressLog stressLog = new(Target, target.ReadGlobalPointer("StressLog")); return new TargetPointer(stressLog.ModuleOffset + formatOffset); } @@ -227,7 +227,7 @@ protected TargetPointer GetFormatPointer(ulong formatOffset) moduleTable = stressLog.Modules ?? throw new InvalidOperationException("StressLogModuleTable is not set and StressLog does not contain a ModuleTable offset, but StressLogHasModuleTable is set to 1."); } uint moduleEntrySize = target.GetTypeInfo(DataType.StressLogModuleDesc).Size!.Value; - uint maxModules = target.ReadGlobal(Constants.Globals.StressLogMaxModules); + uint maxModules = target.ReadGlobal("StressLogMaxModules"); for (uint i = 0; i < maxModules; ++i) { StressLogModuleDesc module = new(Target, moduleTable.Value + i * moduleEntrySize); @@ -322,7 +322,7 @@ The format offset refers to the cummulative offset into a module referred to in ```csharp StressMsgData GetStressMsgData(StressMsg msg) { - StressLog stressLog = new(Target, target.ReadGlobalPointer(Constants.Globals.StressLog)); + StressLog stressLog = new(Target, target.ReadGlobalPointer("StressLog")); uint pointerSize = Target.GetTypeInfo(DataType.pointer).Size!.Value; ulong payload1 = target.Read(msg.Header); diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 995625f801e514..31a52f9571e81d 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -276,6 +276,12 @@ CDAC_TYPE_FIELD(MethodTable, /*pointer*/, PerInstInfo, cdac_data::P CDAC_TYPE_FIELD(MethodTable, /*pointer*/, AuxiliaryData, cdac_data::AuxiliaryData) CDAC_TYPE_END(MethodTable) +CDAC_TYPE_BEGIN(DynamicStaticsInfo) +CDAC_TYPE_SIZE(sizeof(DynamicStaticsInfo)) +CDAC_TYPE_FIELD(DynamicStaticsInfo, /*uint32*/, GCStatics, offsetof(DynamicStaticsInfo, m_pGCStatics)) +CDAC_TYPE_FIELD(DynamicStaticsInfo, /*uint32*/, NonGCStatics, offsetof(DynamicStaticsInfo, m_pNonGCStatics)) +CDAC_TYPE_END(DynamicStaticsInfo) + CDAC_TYPE_BEGIN(MethodTableAuxiliaryData) CDAC_TYPE_INDETERMINATE(MethodTableAuxiliaryData) CDAC_TYPE_FIELD(MethodTableAuxiliaryData, /*pointer*/, LoaderModule, offsetof(MethodTableAuxiliaryData, m_pLoaderModule)) @@ -935,6 +941,7 @@ CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE) CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data::SyncBlockValue) CDAC_GLOBAL(StubCodeBlockLast, uint8, STUB_CODE_BLOCK_LAST) CDAC_GLOBAL(DefaultADID, uint32, DefaultADID) +CDAC_GLOBAL(StaticsPointerMask, uintptr_t, DynamicStaticsInfo::STATICSPOINTERMASK) CDAC_GLOBAL(PtrArrayOffsetToDataArray, uintptr_t, offsetof(PtrArray, m_Array)) CDAC_GLOBAL(NumberOfTlsOffsetsNotUsedInNoncollectibleArray, uint8, NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY) CDAC_GLOBAL(MaxClrNotificationArgs, uint32, MAX_CLR_NOTIFICATION_ARGS) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index 553b40f5de2692..57016478c8a312 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -112,6 +112,8 @@ public interface IRuntimeTypeSystem : IContract ushort GetNumStaticFields(TypeHandle typeHandle) => throw new NotImplementedException(); ushort GetNumThreadStaticFields(TypeHandle typeHandle) => throw new NotImplementedException(); TargetPointer GetFieldDescList(TypeHandle typeHandle) => throw new NotImplementedException(); + TargetPointer GetGCStaticsBasePointer(TypeHandle typeHandle) => throw new NotImplementedException(); + TargetPointer GetNonGCStaticsBasePointer(TypeHandle typeHandle) => throw new NotImplementedException(); TargetPointer GetGCThreadStaticsBasePointer(TypeHandle typeHandle, TargetPointer threadPtr) => throw new NotImplementedException(); TargetPointer GetNonGCThreadStaticsBasePointer(TypeHandle typeHandle, TargetPointer threadPtr) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index db3077fb61309e..c2619f97b7b679 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -46,6 +46,7 @@ public enum DataType CGrowableSymbolStream, ProbeExtensionResult, MethodTable, + DynamicStaticsInfo, EEClass, ArrayClass, MethodTableAuxiliaryData, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index bc572218e8de34..3418712a4422e1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -56,6 +56,7 @@ public static class Globals public const string ExecutionManagerCodeRangeMapAddress = nameof(ExecutionManagerCodeRangeMapAddress); public const string StubCodeBlockLast = nameof(StubCodeBlockLast); public const string DefaultADID = nameof(DefaultADID); + public const string StaticsPointerMask = nameof(StaticsPointerMask); public const string PtrArrayOffsetToDataArray = nameof(PtrArrayOffsetToDataArray); public const string NumberOfTlsOffsetsNotUsedInNoncollectibleArray = nameof(NumberOfTlsOffsetsNotUsedInNoncollectibleArray); public const string MaxClrNotificationArgs = nameof(MaxClrNotificationArgs); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index dc756b561ae173..c14ee7b76e8462 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -410,6 +410,18 @@ public uint GetTypeDefToken(TypeHandle typeHandle) public ushort GetNumStaticFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumStaticFields; public ushort GetNumThreadStaticFields(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? (ushort)0 : GetClassData(typeHandle).NumThreadStaticFields; public TargetPointer GetFieldDescList(TypeHandle typeHandle) => !typeHandle.IsMethodTable() ? TargetPointer.Null : GetClassData(typeHandle).FieldDescList; + private TargetPointer GetDynamicStaticsInfo(TypeHandle typeHandle) + { + if (!typeHandle.IsMethodTable()) + return default; + + MethodTable methodTable = _methodTables[typeHandle.Address]; + if (!methodTable.Flags.IsDynamicStatics) + return default; + TargetPointer dynamicStaticsInfoSize = _target.GetTypeInfo(DataType.DynamicStaticsInfo).Size!.Value; + TargetPointer dynamicStaticsInfoAddr = methodTable.AuxiliaryData - dynamicStaticsInfoSize; + return dynamicStaticsInfoAddr; + } private Data.ThreadStaticsInfo GetThreadStaticsInfo(TypeHandle typeHandle) { @@ -438,6 +450,24 @@ public TargetPointer GetNonGCThreadStaticsBasePointer(TypeHandle typeHandle, Tar return threadContract.GetThreadLocalStaticBase(threadPtr, tlsIndexPtr); } + public TargetPointer GetGCStaticsBasePointer(TypeHandle typeHandle) + { + TargetPointer dynamicStaticsInfoAddr = GetDynamicStaticsInfo(typeHandle); + if (dynamicStaticsInfoAddr == TargetPointer.Null) + return TargetPointer.Null; + Data.DynamicStaticsInfo dynamicStaticsInfo = _target.ProcessedData.GetOrAdd(dynamicStaticsInfoAddr); + return dynamicStaticsInfo.GCStatics; + } + + public TargetPointer GetNonGCStaticsBasePointer(TypeHandle typeHandle) + { + TargetPointer dynamicStaticsInfoAddr = GetDynamicStaticsInfo(typeHandle); + if (dynamicStaticsInfoAddr == TargetPointer.Null) + return TargetPointer.Null; + Data.DynamicStaticsInfo dynamicStaticsInfo = _target.ProcessedData.GetOrAdd(dynamicStaticsInfoAddr); + return dynamicStaticsInfo.NonGCStatics; + } + public ReadOnlySpan GetInstantiation(TypeHandle typeHandle) { if (!typeHandle.IsMethodTable()) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/DynamicStaticsInfo.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/DynamicStaticsInfo.cs new file mode 100644 index 00000000000000..649c9ecdd881e3 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/DynamicStaticsInfo.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class DynamicStaticsInfo : IData +{ + static DynamicStaticsInfo IData.Create(Target target, TargetPointer address) => new DynamicStaticsInfo(target, address); + public DynamicStaticsInfo(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.DynamicStaticsInfo); + TargetPointer mask = target.ReadGlobalPointer(Constants.Globals.StaticsPointerMask); + GCStatics = target.ReadPointer(address + (ulong)type.Fields[nameof(GCStatics)].Offset) & mask; + NonGCStatics = target.ReadPointer(address + (ulong)type.Fields[nameof(NonGCStatics)].Offset) & mask; + } + public TargetPointer GCStatics { get; init; } + public TargetPointer NonGCStatics { get; init; } +} diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 740e14dc44194b..50be4e4c28581e 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -2407,7 +2407,46 @@ int ISOSDacInterface13.LockedFlush() #region ISOSDacInterface14 int ISOSDacInterface14.GetStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress) - => _legacyImpl14 is not null ? _legacyImpl14.GetStaticBaseAddress(methodTable, nonGCStaticsAddress, GCStaticsAddress) : HResults.E_NOTIMPL; + { + int hr = HResults.S_OK; + if (nonGCStaticsAddress == null && GCStaticsAddress == null) + hr = HResults.E_POINTER; + else if (methodTable == 0) + hr = HResults.E_INVALIDARG; + else + { + try + { + Contracts.IRuntimeTypeSystem rtsContract = _target.Contracts.RuntimeTypeSystem; + Contracts.TypeHandle typeHandle = rtsContract.GetTypeHandle(methodTable.ToTargetPointer(_target)); + if (GCStaticsAddress != null) + *GCStaticsAddress = rtsContract.GetGCStaticsBasePointer(typeHandle).ToClrDataAddress(_target); + if (nonGCStaticsAddress != null) + *nonGCStaticsAddress = rtsContract.GetNonGCStaticsBasePointer(typeHandle).ToClrDataAddress(_target); + } + catch (System.Exception ex) + { + hr = ex.HResult; + } + } +#if DEBUG + if (_legacyImpl14 is not null) + { + ClrDataAddress nonGCStaticsAddressLocal; + ClrDataAddress GCStaticsAddressLocal; + int hrLocal = _legacyImpl14.GetStaticBaseAddress(methodTable, &nonGCStaticsAddressLocal, &GCStaticsAddressLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK) + { + if (GCStaticsAddress != null) + Debug.Assert(*GCStaticsAddress == GCStaticsAddressLocal); + if (nonGCStaticsAddress != null) + Debug.Assert(*nonGCStaticsAddress == nonGCStaticsAddressLocal); + } + } +#endif + return hr; + } int ISOSDacInterface14.GetThreadStaticBaseAddress(ClrDataAddress methodTable, ClrDataAddress thread, ClrDataAddress* nonGCStaticsAddress, ClrDataAddress* GCStaticsAddress) { int hr = HResults.S_OK;