Skip to content
16 changes: 9 additions & 7 deletions docs/design/datacontracts/Loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ ModuleFlags GetFlags(ModuleHandle handle);
string GetPath(ModuleHandle handle);
string GetFileName(ModuleHandle handle);
TargetPointer GetLoaderAllocator(ModuleHandle handle);
TargetPointer GetThunkHeap(ModuleHandle handle);
TargetPointer GetGlobalLoaderAllocator();
TargetPointer GetHighFrequencyHeap(TargetPointer globalLoaderAllocator);
TargetPointer GetLowFrequencyHeap(TargetPointer globalLoaderAllocator);
TargetPointer GetStubHeap(TargetPointer globalLoaderAllocator);
TargetPointer GetILBase(ModuleHandle handle);
ModuleLookupTables GetLookupTables(ModuleHandle handle);
TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags);
Expand All @@ -84,7 +87,6 @@ bool IsAssemblyLoaded(ModuleHandle handle);
| `Module` | `Base` | Pointer to start of PE file in memory |
| `Module` | `Flags` | Assembly of the Module |
| `Module` | `LoaderAllocator` | LoaderAllocator of the Module |
| `Module` | `ThunkHeap` | Pointer to the thunk heap |
| `Module` | `Path` | Path of the Module (UTF-16, null-terminated) |
| `Module` | `FileName` | File name of the Module (UTF-16, null-terminated) |
| `Module` | `GrowableSymbolStream` | Pointer to the in memory symbol stream |
Expand Down Expand Up @@ -115,17 +117,22 @@ bool IsAssemblyLoaded(ModuleHandle handle);
| `AppDomain` | `RootAssembly` | Pointer to the root assembly |
| `AppDomain` | `DomainAssemblyList` | ArrayListBase of assemblies in the AppDomain |
| `LoaderAllocator` | `ReferenceCount` | Reference count of LoaderAllocator |
| `LoaderAllocator` | `HighFrequencyHeap` | High-frequency heap of LoaderAllocator |
| `LoaderAllocator` | `LowFrequencyHeap` | Low-frequency heap of LoaderAllocator |
| `LoaderAllocator` | `StubHeap` | Stub heap of LoaderAllocator |
| `ArrayListBase` | `Count` | Total number of elements in the ArrayListBase |
| `ArrayListBase` | `FirstBlock` | First ArrayListBlock |
| `ArrayListBlock` | `Next` | Next ArrayListBlock in chain |
| `ArrayListBlock` | `Size` | Size of data section in block |
| `ArrayListBlock` | `ArrayStart` | Start of data section in block |
| `SystemDomain` | `GlobalLoaderAllocator` | global LoaderAllocator |


### Global variables used:
| Global Name | Type | Purpose |
| --- | --- | --- |
| `AppDomain` | TargetPointer | Pointer to the global AppDomain |
| `SystemDomain` | TargetPointer | Pointer to the global SystemDomain |


### Contract Constants:
Expand Down Expand Up @@ -361,11 +368,6 @@ TargetPointer GetLoaderAllocator(ModuleHandle handle)
return target.ReadPointer(handle.Address + /* Module::LoaderAllocator offset */);
}

TargetPointer GetThunkHeap(ModuleHandle handle)
{
return target.ReadPointer(handle.Address + /* Module::ThunkHeap offset */);
}

TargetPointer GetILBase(ModuleHandle handle)
{
return target.ReadPointer(handle.Address + /* Module::Base offset */);
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,9 @@ CDAC_TYPE_END(Assembly)
CDAC_TYPE_BEGIN(LoaderAllocator)
CDAC_TYPE_INDETERMINATE(LoaderAllocator)
CDAC_TYPE_FIELD(LoaderAllocator, /*uint32*/, ReferenceCount, cdac_data<LoaderAllocator>::ReferenceCount)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, HighFrequencyHeap, cdac_data<LoaderAllocator>::HighFrequencyHeap)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, LowFrequencyHeap, cdac_data<LoaderAllocator>::LowFrequencyHeap)
CDAC_TYPE_FIELD(LoaderAllocator, /*pointer*/, StubHeap, cdac_data<LoaderAllocator>::StubHeap)
CDAC_TYPE_END(LoaderAllocator)

CDAC_TYPE_BEGIN(PEAssembly)
Expand Down Expand Up @@ -294,6 +297,11 @@ CDAC_TYPE_FIELD(AppDomain, /*pointer*/, RootAssembly, cdac_data<AppDomain>::Root
CDAC_TYPE_FIELD(AppDomain, /*DomainAssemblyList*/, DomainAssemblyList, cdac_data<AppDomain>::DomainAssemblyList)
CDAC_TYPE_END(AppDomain)

CDAC_TYPE_BEGIN(SystemDomain)
CDAC_TYPE_INDETERMINATE(SystemDomain)
CDAC_TYPE_FIELD(SystemDomain, /*GlobalLoaderAllocator*/, GlobalLoaderAllocator, cdac_data<SystemDomain>::GlobalLoaderAllocator)
CDAC_TYPE_END(SystemDomain)

CDAC_TYPE_BEGIN(ArrayListBase)
CDAC_TYPE_INDETERMINATE(ArrayListBase)
CDAC_TYPE_FIELD(ArrayListBase, /*uint32*/, Count, cdac_data<ArrayListBase>::Count)
Expand Down Expand Up @@ -959,6 +967,7 @@ CDAC_GLOBAL(MethodDescAlignment, uint64, MethodDesc::ALIGNMENT)
CDAC_GLOBAL(ObjectHeaderSize, uint64, OBJHEADER_SIZE)
CDAC_GLOBAL(SyncBlockValueToObjectOffset, uint16, OBJHEADER_SIZE - cdac_data<ObjHeader>::SyncBlockValue)
CDAC_GLOBAL(StubCodeBlockLast, uint8, STUB_CODE_BLOCK_LAST)
CDAC_GLOBAL(DefaultADID, uint32, DefaultADID)
CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data<ArrayBase>::ArrayBoundsZero)
CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass)
CDAC_GLOBAL_POINTER(FreeObjectMethodTable, &::g_pFreeObjectMethodTable)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/appdomain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,7 @@ template<>
struct cdac_data<SystemDomain>
{
static constexpr PTR_SystemDomain* SystemDomainPtr = &SystemDomain::m_pSystemDomain;
static constexpr size_t GlobalLoaderAllocator = offsetof(SystemDomain, m_GlobalAllocator);
};
#endif // DACCESS_COMPILE

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/loaderallocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,9 @@ template<>
struct cdac_data<LoaderAllocator>
{
static constexpr size_t ReferenceCount = offsetof(LoaderAllocator, m_cReferences);
static constexpr size_t HighFrequencyHeap = offsetof(LoaderAllocator, m_pHighFrequencyHeap);
static constexpr size_t LowFrequencyHeap = offsetof(LoaderAllocator, m_pLowFrequencyHeap);
static constexpr size_t StubHeap = offsetof(LoaderAllocator, m_pStubHeap);
};

typedef VPTR(LoaderAllocator) PTR_LoaderAllocator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,6 @@ public enum ModuleFlags
BeingUnloaded = 0x100000,
}

public record struct ModuleLookupTables(
TargetPointer FieldDefToDesc,
TargetPointer ManifestModuleReferences,
TargetPointer MemberRefToDesc,
TargetPointer MethodDefToDesc,
TargetPointer TypeDefToMethodTable,
TargetPointer TypeRefToMethodTable,
TargetPointer MethodDefToILCodeVersioningState);

[Flags]
public enum AssemblyIterationFlags
{
Expand All @@ -71,6 +62,15 @@ public enum AssemblyIterationFlags
IncludeCollected = 0x00000080, // Include all collectible assemblies that have been collected
}

public record struct ModuleLookupTables(
TargetPointer FieldDefToDesc,
TargetPointer ManifestModuleReferences,
TargetPointer MemberRefToDesc,
TargetPointer MethodDefToDesc,
TargetPointer TypeDefToMethodTable,
TargetPointer TypeRefToMethodTable,
TargetPointer MethodDefToILCodeVersioningState);

public interface ILoader : IContract
{
static string IContract.Name => nameof(Loader);
Expand All @@ -88,8 +88,11 @@ public interface ILoader : IContract
ModuleFlags GetFlags(ModuleHandle handle) => throw new NotImplementedException();
string GetPath(ModuleHandle handle) => throw new NotImplementedException();
string GetFileName(ModuleHandle handle) => throw new NotImplementedException();

TargetPointer GetLoaderAllocator(ModuleHandle handle) => throw new NotImplementedException();
TargetPointer GetGlobalLoaderAllocator() => throw new NotImplementedException();
TargetPointer GetHighFrequencyHeap(TargetPointer globalLoaderAllocator) => throw new NotImplementedException();
TargetPointer GetLowFrequencyHeap(TargetPointer globalLoaderAllocator) => throw new NotImplementedException();
TargetPointer GetStubHeap(TargetPointer globalLoaderAllocator) => throw new NotImplementedException();
TargetPointer GetILBase(ModuleHandle handle) => throw new NotImplementedException();
ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum DataType
Module,
ModuleLookupMap,
AppDomain,
SystemDomain,
Assembly,
LoaderAllocator,
PEAssembly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,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 PlatformMetadata = nameof(PlatformMetadata);
public const string ProfilerControlBlock = nameof(ProfilerControlBlock);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ TargetPointer ILoader.GetModule(ModuleHandle handle)
{
return handle.Address;
}

TargetPointer ILoader.GetAssembly(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
Expand Down Expand Up @@ -289,6 +290,31 @@ TargetPointer ILoader.GetLoaderAllocator(ModuleHandle handle)
return module.LoaderAllocator;
}

TargetPointer ILoader.GetGlobalLoaderAllocator()
{
TargetPointer systemDomainPointer = _target.ReadGlobalPointer(Constants.Globals.SystemDomain);
Data.SystemDomain systemDomain = _target.ProcessedData.GetOrAdd<Data.SystemDomain>(_target.ReadPointer(systemDomainPointer));
return systemDomain.GlobalLoaderAllocator;
}

TargetPointer ILoader.GetHighFrequencyHeap(TargetPointer globalLoaderAllocator)
{
Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(globalLoaderAllocator);
return loaderAllocator.HighFrequencyHeap;
}

TargetPointer ILoader.GetLowFrequencyHeap(TargetPointer globalLoaderAllocator)
{
Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(globalLoaderAllocator);
return loaderAllocator.LowFrequencyHeap;
}

TargetPointer ILoader.GetStubHeap(TargetPointer globalLoaderAllocator)
{
Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(globalLoaderAllocator);
return loaderAllocator.StubHeap;
}

TargetPointer ILoader.GetILBase(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ public LoaderAllocator(Target target, TargetPointer address)
Target.TypeInfo type = target.GetTypeInfo(DataType.LoaderAllocator);

ReferenceCount = target.Read<uint>(address + (ulong)type.Fields[nameof(ReferenceCount)].Offset);
HighFrequencyHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(HighFrequencyHeap)].Offset);
LowFrequencyHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(LowFrequencyHeap)].Offset);
StubHeap = target.ReadPointer(address + (ulong)type.Fields[nameof(StubHeap)].Offset);

}

public uint ReferenceCount { get; init; }
public TargetPointer HighFrequencyHeap { get; init; }
public TargetPointer LowFrequencyHeap { get; init; }
public TargetPointer StubHeap { get; init; }

public bool IsAlive => ReferenceCount != 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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 SystemDomain : IData<SystemDomain>
{
static SystemDomain IData<SystemDomain>.Create(Target target, TargetPointer address) => new SystemDomain(target, address);
public SystemDomain(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.SystemDomain);
GlobalLoaderAllocator = address + (ulong)type.Fields[nameof(GlobalLoaderAllocator)].Offset;
}

public TargetPointer GlobalLoaderAllocator { get; init; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,24 @@ internal struct DacpThreadStoreData
public int fHostConfig; // Uses hosting flags defined above
};

internal struct DacpAppDomainData
{
// The pointer to the AppDomain or SystemDomain.
// It's useful to keep this around in the structure
public ClrDataAddress AppDomainPtr;
public ClrDataAddress AppSecDesc;
public ClrDataAddress pLowFrequencyHeap;
public ClrDataAddress pHighFrequencyHeap;
public ClrDataAddress pStubHeap;
public ClrDataAddress DomainLocalBlock;
public ClrDataAddress pDomainLocalModules;
// The creation sequence number of this app domain (starting from 1)
public uint dwId;
public int AssemblyCount;
public int FailedAssemblyCount;
public uint appDomainStage;
};

internal struct DacpAppDomainStoreData
{
public ClrDataAddress sharedDomain;
Expand Down Expand Up @@ -107,6 +125,23 @@ internal enum DacpObjectType
OBJ_OTHER
};

internal enum DacpAppDomainDataStage
{
STAGE_CREATING,
STAGE_READYFORMANAGEDCODE,
STAGE_ACTIVE,
STAGE_OPEN,
STAGE_UNLOAD_REQUESTED,
STAGE_EXITING,
STAGE_EXITED,
STAGE_FINALIZING,
STAGE_FINALIZED,
STAGE_HANDLETABLE_NOACCESS,
STAGE_CLEARED,
STAGE_COLLECTED,
STAGE_CLOSED
};

internal struct DacpObjectData
{
public ClrDataAddress MethodTable;
Expand Down Expand Up @@ -200,7 +235,7 @@ internal unsafe partial interface ISOSDacInterface
[PreserveSig]
int GetAppDomainList(uint count, [In, Out, MarshalUsing(CountElementName = nameof(count))] ClrDataAddress[] values, uint* pNeeded);
[PreserveSig]
int GetAppDomainData(ClrDataAddress addr, /*struct DacpAppDomainData*/ void* data);
int GetAppDomainData(ClrDataAddress addr, DacpAppDomainData* data);
[PreserveSig]
int GetAppDomainName(ClrDataAddress addr, uint count, char* name, uint* pNeeded);
[PreserveSig]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,82 @@ int ISOSDacInterface.GetAppDomainConfigFile(ClrDataAddress appDomain, int count,

return hr;
}
int ISOSDacInterface.GetAppDomainData(ClrDataAddress addr, void* data)
=> _legacyImpl is not null ? _legacyImpl.GetAppDomainData(addr, data) : HResults.E_NOTIMPL;
int ISOSDacInterface.GetAppDomainData(ClrDataAddress addr, DacpAppDomainData* data)
{
int hr = HResults.S_OK;
try
{
if (addr == 0)
{
hr = HResults.E_INVALIDARG;
}
else
{
NativeMemory.Clear(data, (nuint)sizeof(DacpAppDomainData));
data->AppDomainPtr = addr;
TargetPointer systemDomainPointer = _target.ReadGlobalPointer(Constants.Globals.SystemDomain);
ClrDataAddress systemDomain = _target.ReadPointer(systemDomainPointer).ToClrDataAddress(_target);
Contracts.ILoader loader = _target.Contracts.Loader;
TargetPointer globalLoaderAllocator = loader.GetGlobalLoaderAllocator();
data->pHighFrequencyHeap = loader.GetHighFrequencyHeap(globalLoaderAllocator).ToClrDataAddress(_target);
data->pLowFrequencyHeap = loader.GetLowFrequencyHeap(globalLoaderAllocator).ToClrDataAddress(_target);
data->pStubHeap = loader.GetStubHeap(globalLoaderAllocator).ToClrDataAddress(_target);
data->appDomainStage = (uint)DacpAppDomainDataStage.STAGE_OPEN;
if (addr != systemDomain)
{
TargetPointer pAppDomain = addr.ToTargetPointer(_target);
data->dwId = _target.ReadGlobal<uint>(Constants.Globals.DefaultADID);

List<Contracts.ModuleHandle> modules = loader.GetModuleHandles(
pAppDomain,
AssemblyIterationFlags.IncludeLoading |
AssemblyIterationFlags.IncludeLoaded |
AssemblyIterationFlags.IncludeExecution).ToList();

for (int i = 0; i < modules.Count; i++)
{
Contracts.ModuleHandle module = modules[i];
if (loader.IsAssemblyLoaded(module))
{
data->AssemblyCount++;
}
}

List<Contracts.ModuleHandle> failedModules = loader.GetModuleHandles(
pAppDomain,
AssemblyIterationFlags.IncludeFailedToLoad).ToList();
data->FailedAssemblyCount = failedModules.Count;
}
}
}
catch (System.Exception ex)
{
hr = ex.HResult;
}
#if DEBUG
if (_legacyImpl is not null)
{
DacpAppDomainData dataLocal = default;
int hrLocal = _legacyImpl.GetAppDomainData(addr, &dataLocal);
Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}");
if (hr == HResults.S_OK)
{
Debug.Assert(data->AppDomainPtr == dataLocal.AppDomainPtr);
Debug.Assert(data->pHighFrequencyHeap == dataLocal.pHighFrequencyHeap);
Debug.Assert(data->pLowFrequencyHeap == dataLocal.pLowFrequencyHeap);
Debug.Assert(data->pStubHeap == dataLocal.pStubHeap);
Debug.Assert(data->DomainLocalBlock == dataLocal.DomainLocalBlock);
Debug.Assert(data->pDomainLocalModules == dataLocal.pDomainLocalModules);
Debug.Assert(data->dwId == dataLocal.dwId);
Debug.Assert(data->appDomainStage == dataLocal.appDomainStage);
Debug.Assert(data->AssemblyCount == dataLocal.AssemblyCount);
Debug.Assert(data->FailedAssemblyCount == dataLocal.FailedAssemblyCount);
}
}
#endif
return hr;

}
int ISOSDacInterface.GetAppDomainList(uint count, [In, MarshalUsing(CountElementName = "count"), Out] ClrDataAddress[] values, uint* pNeeded)
=> _legacyImpl is not null ? _legacyImpl.GetAppDomainList(count, values, pNeeded) : HResults.E_NOTIMPL;
int ISOSDacInterface.GetAppDomainName(ClrDataAddress addr, uint count, char* name, uint* pNeeded)
Expand Down
Loading