Skip to content

[cDAC] Implement ISOSDacImpl.GetAssemblyList #114800

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 24 additions & 0 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,17 @@ CDAC_TYPE_INDETERMINATE(Assembly)
#ifdef FEATURE_COLLECTIBLE_TYPES
CDAC_TYPE_FIELD(Assembly, /*uint8*/, IsCollectible, cdac_data<Assembly>::IsCollectible)
#endif
CDAC_TYPE_FIELD(Assembly, /*pointer*/, Module, cdac_data<Assembly>::Module)
CDAC_TYPE_FIELD(Assembly, /*pointer*/, Error, cdac_data<Assembly>::Error)
CDAC_TYPE_FIELD(Assembly, /*uint32*/, NotifyFlags, cdac_data<Assembly>::NotifyFlags)
CDAC_TYPE_FIELD(Assembly, /*uint32*/, Level, cdac_data<Assembly>::Level)
CDAC_TYPE_END(Assembly)

CDAC_TYPE_BEGIN(LoaderAllocator)
CDAC_TYPE_INDETERMINATE(LoaderAllocator)
CDAC_TYPE_FIELD(LoaderAllocator, /*uint32*/, ReferenceCount, cdac_data<LoaderAllocator>::ReferenceCount)
CDAC_TYPE_END(LoaderAllocator)

CDAC_TYPE_BEGIN(PEAssembly)
CDAC_TYPE_INDETERMINATE(PEAssembly)
CDAC_TYPE_FIELD(PEAssembly, /*pointer*/, PEImage, cdac_data<PEAssembly>::PEImage)
Expand Down Expand Up @@ -282,8 +291,22 @@ CDAC_TYPE_END(ProbeExtensionResult)
CDAC_TYPE_BEGIN(AppDomain)
CDAC_TYPE_INDETERMINATE(AppDomain)
CDAC_TYPE_FIELD(AppDomain, /*pointer*/, RootAssembly, cdac_data<AppDomain>::RootAssembly)
CDAC_TYPE_FIELD(AppDomain, /*DomainAssemblyList*/, DomainAssemblyList, cdac_data<AppDomain>::DomainAssemblyList)
CDAC_TYPE_END(AppDomain)

CDAC_TYPE_BEGIN(ArrayListBase)
CDAC_TYPE_INDETERMINATE(ArrayListBase)
CDAC_TYPE_FIELD(ArrayListBase, /*uint32*/, Count, cdac_data<ArrayListBase>::Count)
CDAC_TYPE_FIELD(ArrayListBase, /*pointer*/, FirstBlock, cdac_data<ArrayListBase>::FirstBlock)
CDAC_TYPE_END(ArrayListBase)

CDAC_TYPE_BEGIN(ArrayListBlock)
CDAC_TYPE_INDETERMINATE(ArrayListBlock)
CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, Next, cdac_data<ArrayListBase>::Next)
CDAC_TYPE_FIELD(ArrayListBlock, /*uint32*/, Size, cdac_data<ArrayListBase>::Size)
CDAC_TYPE_FIELD(ArrayListBlock, /*pointer*/, ArrayStart, cdac_data<ArrayListBase>::ArrayStart)
CDAC_TYPE_END(ArrayListBlock)

// RuntimeTypeSystem

CDAC_TYPE_BEGIN(MethodTable)
Expand Down Expand Up @@ -818,6 +841,7 @@ CDAC_GLOBAL_STRING(Architecture, riscv64)
CDAC_GLOBAL_STRING(RID, RID_STRING)

CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain)
CDAC_GLOBAL_POINTER(SystemDomain, cdac_data<SystemDomain>::SystemDomain)
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread)
CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread)
Expand Down
16 changes: 16 additions & 0 deletions src/coreclr/inc/arraylist.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include <contract.h>
#include <stddef.h> // offsetof

// Forward Declarations
template<typename T> struct cdac_data;

//
// ArrayList is a simple class which is used to contain a growable
// list of pointers, stored in chunks. Modification is by appending
Expand Down Expand Up @@ -263,8 +266,21 @@ class ArrayListBase
return BlockIterator((ArrayListBlock *) &m_firstBlock, m_count);
}

friend struct cdac_data<ArrayListBase>;
};

template<>
struct cdac_data<ArrayListBase>
{
static constexpr size_t Count = offsetof(ArrayListBase, m_count);
static constexpr size_t FirstBlock = offsetof(ArrayListBase, m_firstBlock);

static constexpr size_t Next = offsetof(ArrayListBase::ArrayListBlock, m_next);
static constexpr size_t Size = offsetof(ArrayListBase::ArrayListBlock, m_blockSize);
static constexpr size_t ArrayStart = offsetof(ArrayListBase::ArrayListBlock, m_array);
};


class ArrayList : public ArrayListBase
{
public:
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/vm/appdomain.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,8 @@ class AppDomain final
{
return m_array.Iterate();
}

friend struct cdac_data<AppDomain>;
}; // class DomainAssemblyList

// Conceptually a list of code:Assembly structures, protected by lock code:GetAssemblyListLock
Expand Down Expand Up @@ -1621,6 +1623,7 @@ template<>
struct cdac_data<AppDomain>
{
static constexpr size_t RootAssembly = offsetof(AppDomain, m_pRootAssembly);
static constexpr size_t DomainAssemblyList = offsetof(AppDomain, m_Assemblies) + offsetof(AppDomain::DomainAssemblyList, m_array);
};

typedef DPTR(class SystemDomain) PTR_SystemDomain;
Expand Down Expand Up @@ -1953,8 +1956,15 @@ inline static BOOL IsUnderDomainLock() { LIMITED_METHOD_CONTRACT; return m_Syste
bool enumThis);
#endif

friend struct ::cdac_data<SystemDomain>;
}; // class SystemDomain

template<>
struct cdac_data<SystemDomain>
{
static constexpr void* const SystemDomain = (void*)&SystemDomain::m_pSystemDomain;
};

#include "comreflectioncache.inl"

#endif
4 changes: 4 additions & 0 deletions src/coreclr/vm/assembly.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,10 @@ struct cdac_data<Assembly>
#ifdef FEATURE_COLLECTIBLE_TYPES
static constexpr size_t IsCollectible = offsetof(Assembly, m_isCollectible);
#endif
static constexpr size_t Module = offsetof(Assembly, m_pModule);
static constexpr size_t Error = offsetof(Assembly, m_pError);
static constexpr size_t NotifyFlags = offsetof(Assembly, m_notifyFlags);
static constexpr size_t Level = offsetof(Assembly, m_level);
};

#ifndef DACCESS_COMPILE
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/loaderallocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -877,8 +877,16 @@ class LoaderAllocator
virtual void UnregisterDependentHandleToNativeObjectFromCleanup(LADependentHandleToNativeObject *dependentHandle) {};
virtual void CleanupDependentHandlesToNativeObjects() {};
#endif

friend struct ::cdac_data<LoaderAllocator>;
}; // class LoaderAllocator

template<>
struct cdac_data<LoaderAllocator>
{
static constexpr size_t ReferenceCount = offsetof(LoaderAllocator, m_cReferences);
};

typedef VPTR(LoaderAllocator) PTR_LoaderAllocator;

extern "C" BOOL QCALLTYPE LoaderAllocator_Destroy(QCall::LoaderAllocatorHandle pLoaderAllocator);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public ModuleHandle(TargetPointer address)
[Flags]
public enum ModuleFlags
{
Tenured = 0x00000001, // Set once we know for sure the Module will not be freed until the appdomain itself exits
EditAndContinue = 0x00000008, // Edit and Continue is enabled for this module
ReflectionEmit = 0x00000040, // Reflection.Emit was used to create this module
}
Expand All @@ -32,12 +33,34 @@ public record struct ModuleLookupTables(
TargetPointer TypeRefToMethodTable,
TargetPointer MethodDefToILCodeVersioningState);

[Flags]
public enum AssemblyIterationFlags
{
// load status flags
IncludeLoaded = 0x00000001, // include assemblies that are already loaded
// (m_level >= code:FILE_LOAD_DELIVER_EVENTS)
IncludeLoading = 0x00000002, // include assemblies that are still in the process of loading
// (all m_level values)
IncludeAvailableToProfilers = 0x00000020, // include assemblies available to profilers
// See comment at code:DomainAssembly::IsAvailableToProfilers

// Execution / introspection flags
IncludeExecution = 0x00000004, // include assemblies that are loaded for execution only

IncludeFailedToLoad = 0x00000010, // include assemblies that failed to load

// Collectible assemblies flags
ExcludeCollectible = 0x00000040, // Exclude all collectible assemblies
IncludeCollected = 0x00000080, // Include all collectible assemblies that have been collected
}

public interface ILoader : IContract
{
static string IContract.Name => nameof(Loader);

ModuleHandle GetModuleHandle(TargetPointer modulePointer) => throw new NotImplementedException();

List<ModuleHandle> GetAssemblies(TargetPointer appDomain, AssemblyIterationFlags iterationFlags) => throw new NotImplementedException();
TargetPointer GetRootAssembly() => throw new NotImplementedException();
TargetPointer GetAssembly(ModuleHandle handle) => throw new NotImplementedException();
TargetPointer GetPEAssembly(ModuleHandle handle) => throw new NotImplementedException();
Expand All @@ -54,6 +77,7 @@ public interface ILoader : IContract

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();
}

public readonly struct Loader : ILoader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public enum DataType
ModuleLookupMap,
AppDomain,
Assembly,
LoaderAllocator,
PEAssembly,
PEImage,
PEImageLayout,
Expand Down Expand Up @@ -96,6 +97,9 @@ public enum DataType
MethodImpl,
NativeCodeSlot,
GCCoverageInfo,
ArrayListBase,
ArrayListBlock,

TransitionBlock,
DebuggerEval,
CalleeSavedRegisters,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public static class Globals
{
// See src/coreclr/debug/runtimeinfo/datadescriptor.h
public const string AppDomain = nameof(AppDomain);
public const string SystemDomain = nameof(SystemDomain);
public const string ThreadStore = nameof(ThreadStore);
public const string FinalizerThread = nameof(FinalizerThread);
public const string GCThread = nameof(GCThread);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using Microsoft.Diagnostics.DataContractReader.Data;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;
Expand All @@ -23,6 +24,71 @@ ModuleHandle ILoader.GetModuleHandle(TargetPointer modulePointer)
return new ModuleHandle(modulePointer);
}

List<ModuleHandle> ILoader.GetAssemblies(TargetPointer appDomain, AssemblyIterationFlags iterationFlags)
{
if (appDomain == TargetPointer.Null)
throw new ArgumentNullException(nameof(appDomain));

Data.AppDomain domain = _target.ProcessedData.GetOrAdd<Data.AppDomain>(appDomain);
ArrayListBase arrayList = _target.ProcessedData.GetOrAdd<ArrayListBase>(domain.DomainAssemblyList);

List<ModuleHandle> handles = [];
foreach (TargetPointer pAssembly in arrayList.Elements)
{
TargetPointer assemblyAddr = _target.ReadPointer(pAssembly);
Data.Assembly assembly = _target.ProcessedData.GetOrAdd<Data.Assembly>(assemblyAddr);

// following logic is based on AppDomain::AssemblyIterator::Next_Unlocked in appdomain.cpp

if (assembly.IsError && !iterationFlags.HasFlag(AssemblyIterationFlags.IncludeFailedToLoad))
continue; // skip assemblies with errors

if ((assembly.NotifyFlags & 0x1 /*PROFILER_NOTIFIED*/) != 0 && !iterationFlags.HasFlag(AssemblyIterationFlags.IncludeAvailableToProfilers))
{
// The assembly has reached the state at which we would notify profilers,
// and we're supposed to include such assemblies in the enumeration. So
// don't reject it (i.e., noop here, and don't bother with the rest of
// the load status checks). Check for this first, since
// IncludeAvailableToProfilers contains some loaded AND loading
// assemblies.
}
else if (assembly.IsLoaded)
{
if (!iterationFlags.HasFlag(AssemblyIterationFlags.IncludeLoaded))
continue; // skip loaded assemblies
}
else
{
if (!iterationFlags.HasFlag(AssemblyIterationFlags.IncludeLoading))
continue; // skip loading assemblies
}

// Next, reject assemblies whose execution status is
// not to be included in the enumeration

if (!iterationFlags.HasFlag(AssemblyIterationFlags.IncludeExecution))
continue; // skip assemblies with execution status

if (assembly.IsCollectible != 0)
{
if (iterationFlags.HasFlag(AssemblyIterationFlags.ExcludeCollectible))
continue; // skip collectible assemblies

Module module = _target.ProcessedData.GetOrAdd<Data.Module>(assembly.Module);
if (((ModuleFlags)module.Flags).HasFlag(ModuleFlags.Tenured))
continue; // skip tenured modules

LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(module.LoaderAllocator);
if (!loaderAllocator.IsAlive && !iterationFlags.HasFlag(AssemblyIterationFlags.IncludeCollected))
continue; // skip collected assemblies
}

handles.Add(new(assembly.Module));
}

return handles;
}

TargetPointer ILoader.GetRootAssembly()
{
TargetPointer appDomainPointer = _target.ReadGlobalPointer(Constants.Globals.AppDomain);
Expand Down Expand Up @@ -192,4 +258,11 @@ bool ILoader.IsCollectible(ModuleHandle handle)
Data.Assembly la = _target.ProcessedData.GetOrAdd<Data.Assembly>(assembly);
return la.IsCollectible != 0;
}

bool ILoader.IsAssemblyLoaded(ModuleHandle handle)
{
Data.Module module = _target.ProcessedData.GetOrAdd<Data.Module>(handle.Address);
Data.Assembly assembly = _target.ProcessedData.GetOrAdd<Data.Assembly>(module.Assembly);
return assembly.IsLoaded;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ public AppDomain(Target target, TargetPointer address)
Target.TypeInfo type = target.GetTypeInfo(DataType.AppDomain);

RootAssembly = target.ReadPointer(address + (ulong)type.Fields[nameof(RootAssembly)].Offset);
DomainAssemblyList = address + (ulong)type.Fields[nameof(DomainAssemblyList)].Offset;
}

public TargetPointer RootAssembly { get; init; }
public TargetPointer DomainAssemblyList { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.Diagnostics.DataContractReader.Data;

/// <summary>
/// Encapsulates structure and logic for ArrayListBase implemented in arraylist.h
/// </summary>
internal sealed class ArrayListBase : IData<ArrayListBase>
{
static ArrayListBase IData<ArrayListBase>.Create(Target target, TargetPointer address) => new ArrayListBase(target, address);
public ArrayListBase(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ArrayListBase);

Count = target.Read<uint>(address + (ulong)type.Fields[nameof(Count)].Offset);
FirstBlock = address + (ulong)type.Fields[nameof(FirstBlock)].Offset;

TargetPointer next = FirstBlock;
while (next != TargetPointer.Null)
{
ArrayListBlock block = target.ProcessedData.GetOrAdd<ArrayListBlock>(next);
Blocks.Add(block);
next = block.Next;
}

uint elementsFound = 0;
foreach (ArrayListBlock block in Blocks)
{
foreach (TargetPointer element in block.Elements)
{
if (elementsFound >= Count)
{
break;
}

Elements.Add(element);
elementsFound++;
}
}
}

public uint Count { get; init; }
public TargetPointer FirstBlock { get; init; }

public List<ArrayListBlock> Blocks { get; init; } = [];
public List<TargetPointer> Elements { get; init; } = [];

internal sealed class ArrayListBlock : IData<ArrayListBlock>
{
static ArrayListBlock IData<ArrayListBlock>.Create(Target target, TargetPointer address) => new ArrayListBlock(target, address);
public ArrayListBlock(Target target, TargetPointer address)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ArrayListBlock);

Next = target.ReadPointer(address + (ulong)type.Fields[nameof(Next)].Offset);
Size = target.Read<uint>(address + (ulong)type.Fields[nameof(Size)].Offset);
ArrayStart = address + (ulong)type.Fields[nameof(ArrayStart)].Offset;

for (ulong i = 0; i < Size; i++)
{
Elements.Add(target.ReadPointer(ArrayStart + (i * (ulong)target.PointerSize)));
}
}

public TargetPointer Next { get; init; }
public uint Size { get; init; }
public TargetPointer ArrayStart { get; init; }

public List<TargetPointer> Elements { get; init; } = [];
}
}
Loading
Loading