Skip to content
53 changes: 38 additions & 15 deletions docs/design/datacontracts/Loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,15 @@ ModuleFlags GetFlags(ModuleHandle handle);
string GetPath(ModuleHandle handle);
string GetFileName(ModuleHandle handle);
TargetPointer GetLoaderAllocator(ModuleHandle handle);
TargetPointer GetThunkHeap(ModuleHandle handle);
TargetPointer GetILBase(ModuleHandle handle);
ModuleLookupTables GetLookupTables(ModuleHandle handle);
TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags);
bool IsCollectible(ModuleHandle handle);
bool IsAssemblyLoaded(ModuleHandle handle);
TargetPointer GetGlobalLoaderAllocator();
TargetPointer GetHighFrequencyHeap(TargetPointer loaderAllocatorPointer);
TargetPointer GetLowFrequencyHeap(TargetPointer loaderAllocatorPointer);
TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer);
```

## Version 1
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 Expand Up @@ -411,20 +413,41 @@ TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out Tar
} while (table != TargetPointer.Null);
return TargetPointer.Null;
}
```

```csharp
bool ILoader.IsCollectible(ModuleHandle handle)
bool IsCollectible(ModuleHandle handle)
{
TargetPointer assembly = _target.ReadPointer(handle.Address + /*Module::Assembly*/);
byte isCollectible = _target.Read<byte>(assembly + /* Assembly::IsCollectible*/);
TargetPointer assembly = target.ReadPointer(handle.Address + /*Module::Assembly*/);
byte isCollectible = target.Read<byte>(assembly + /* Assembly::IsCollectible*/);
return isCollectible != 0;
}

bool ILoader.IsAssemblyLoaded(ModuleHandle handle)
bool IsAssemblyLoaded(ModuleHandle handle)
{
TargetPointer assembly = _target.ReadPointer(handle.Address + /*Module::Assembly*/);
uint loadLevel = _target.Read<uint>(assembly + /* Assembly::Level*/);
TargetPointer assembly = target.ReadPointer(handle.Address + /*Module::Assembly*/);
uint loadLevel = target.Read<uint>(assembly + /* Assembly::Level*/);
return assembly.Level >= ASSEMBLY_LEVEL_LOADED;
}

TargetPointer GetGlobalLoaderAllocator()
{
TargetPointer systemDomainPointer = target.ReadGlobalPointer(Constants.Globals.SystemDomain);
TargetPointer systemDomain = target.ReadPointer(systemDomainPointer);
return target.ReadPointer(systemDomain + /* SystemDomain::GlobalLoaderAllocator offset */);
}

TargetPointer GetHighFrequencyHeap(TargetPointer loaderAllocatorPointer)
{
return target.ReadPointer(loaderAllocatorPointer + /* LoaderAllocator::HighFrequencyHeap offset */);
}

TargetPointer GetLowFrequencyHeap(TargetPointer loaderAllocatorPointer)
{
return target.ReadPointer(loaderAllocatorPointer + /* LoaderAllocator::LowFrequencyHeap offset */);
}

TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer)
{
return target.ReadPointer(loaderAllocatorPointer + /* LoaderAllocator::StubHeap offset */);
}

```
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 @@ -963,6 +971,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(MaxClrNotificationArgs, uint32, MAX_CLR_NOTIFICATION_ARGS)
CDAC_GLOBAL_POINTER(ClrNotificationArguments, &::g_clrNotificationArguments)
CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data<ArrayBase>::ArrayBoundsZero)
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,14 +88,17 @@ 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 GetILBase(ModuleHandle handle) => throw new NotImplementedException();
ModuleLookupTables GetLookupTables(ModuleHandle handle) => throw new NotImplementedException();

TargetPointer GetModuleLookupMapElement(TargetPointer table, uint token, out TargetNUInt flags) => throw new NotImplementedException();
bool IsCollectible(ModuleHandle handle) => throw new NotImplementedException();
bool IsAssemblyLoaded(ModuleHandle handle) => throw new NotImplementedException();

TargetPointer GetGlobalLoaderAllocator() => throw new NotImplementedException();
TargetPointer GetHighFrequencyHeap(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException();
TargetPointer GetLowFrequencyHeap(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException();
TargetPointer GetStubHeap(TargetPointer loaderAllocatorPointer) => throw new NotImplementedException();
}

public readonly struct Loader : ILoader
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 @@ -54,6 +54,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 MaxClrNotificationArgs = nameof(MaxClrNotificationArgs);
public const string ClrNotificationArguments = nameof(ClrNotificationArguments);
public const string PlatformMetadata = nameof(PlatformMetadata);
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 @@ -353,4 +354,29 @@ bool ILoader.IsAssemblyLoaded(ModuleHandle handle)
Data.Assembly assembly = _target.ProcessedData.GetOrAdd<Data.Assembly>(module.Assembly);
return assembly.Level >= ASSEMBLY_LEVEL_LOADED /* IsLoaded */;
}

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 loaderAllocatorPointer)
{
Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(loaderAllocatorPointer);
return loaderAllocator.HighFrequencyHeap;
}

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

TargetPointer ILoader.GetStubHeap(TargetPointer loaderAllocatorPointer)
{
Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(loaderAllocatorPointer);
return loaderAllocator.StubHeap;
}
}
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,41 @@ internal struct DacpThreadStoreData
public int fHostConfig; // Uses hosting flags defined above
};

internal enum DacpAppDomainDataStage : uint
{
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 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 DacpAppDomainDataStage appDomainStage;
};

internal struct DacpAppDomainStoreData
{
public ClrDataAddress sharedDomain;
Expand Down Expand Up @@ -210,7 +245,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
Loading
Loading