Skip to content

Adding GetAppDomainData cDAC API #117827

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 15 commits into from
Jul 24, 2025
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