Skip to content

Commit

Permalink
[cdac] break up cdacreader into 4 separate assemblies (#108156)
Browse files Browse the repository at this point in the history
Break up the monolithic cdacreader assembly into four parts:

1. `Microsoft.Diagnostics.DataContractReader.Abstractions` just the API surface for contract implementations and clients. **Note**: everything is internal for now (with IVT for the other assemblies) - we're not committing to a public API surface yet
2. `Microsoft.Diagnostics.DataContractReader.Contracts`: the concrete implementations of the contracts and data
3. `Microsoft.Diagnostics.DataContractReader`: a concrete Target that ties everything together
4. `cdacreader`: just the unmanaged entrypoints and the legacy DAC API surface `SOSDacImpl`

To untangle things I had to add a new `IContractFactory<TContract>` interface - this is what the target's ContractRegistry uses to instantiate specific versions of contracts.

Goals:

* Make it possible to mock a Target and its ContractRegistry so that concrete contracts can be tested in isolation for example by making dummy dependent contracts that return canned answers.
* Eventually make it possible to inject additional contract implementations into a ContractRegistry implementation
Make it possible to consume just the Target and Contracts without the unmanaged entrypoints or the legacy interfaces


Changes:
* [cdac] break up cdacreader into 4 separate assemblies
* rename the contract factories using libraries naming convention
* removed unused usings
* document all abstract Target members
* rename Target -> ContractDescriptorTarget
* Add ReadTargetPointerFromSpan to abstract Target
   Allows the TypeNameBuidler and SigFormat to depend on the abstract target
* change SOSDacImpl to depend on the abstract Target
* fixup filenames and namespaces
  • Loading branch information
lambdageek authored Sep 30, 2024
1 parent 13e55a4 commit a7e5426
Show file tree
Hide file tree
Showing 75 changed files with 673 additions and 287 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Diagnostics.DataContractReader.Contracts;


namespace Microsoft.Diagnostics.DataContractReader;

/// <summary>
/// A registry of all the contracts that may be provided by a target.
/// </summary>
internal abstract class ContractRegistry
{
/// <summary>
/// Gets an instance of the Exception contract for the target.
/// </summary>
public abstract IException Exception { get;}
/// <summary>
/// Gets an instance of the Loader contract for the target.
/// </summary>
public abstract ILoader Loader { get; }
/// <summary>
/// Gets an instance of the EcmaMetadata contract for the target.
/// </summary>
public abstract IEcmaMetadata EcmaMetadata { get; }
/// <summary>
/// Gets an instance of the Object contract for the target.
/// </summary>
public abstract IObject Object { get; }
/// <summary>
/// Gets an instance of the Thread contract for the target.
/// </summary>
public abstract IThread Thread { get; }
/// <summary>
/// Gets an instance of the RuntimeTypeSystem contract for the target.
/// </summary>
public abstract IRuntimeTypeSystem RuntimeTypeSystem { get; }
/// <summary>
/// Gets an instance of the DacStreams contract for the target.
/// </summary>
public abstract IDacStreams DacStreams { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts;
internal interface IContract
{
static virtual string Name => throw new NotImplementedException();
static virtual IContract Create(Target target, int version) => throw new NotImplementedException();
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Diagnostics.DataContractReader.Contracts;

internal interface IDacStreams : IContract
{
static string IContract.Name { get; } = nameof(DacStreams);
static IContract IContract.Create(Target target, int version)
{
return version switch
{
1 => new DacStreams_1(target),
_ => default(DacStreams),
};
}

public virtual string? StringFromEEAddress(TargetPointer address) => throw new NotImplementedException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts;
internal interface IEcmaMetadata : IContract
{
static string IContract.Name { get; } = nameof(EcmaMetadata);
static IContract IContract.Create(Target target, int version)
{
return version switch
{
1 => new EcmaMetadata_1(target),
_ => default(EcmaMetadata),
};
}

public virtual TargetSpan GetReadOnlyMetadataAddress(ModuleHandle handle) => throw new NotImplementedException();

public virtual MetadataReader? GetMetadata(ModuleHandle module) => throw new NotImplementedException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@ internal record struct ExceptionData(
internal interface IException : IContract
{
static string IContract.Name { get; } = nameof(Exception);
static IContract IContract.Create(Target target, int version)
{
return version switch
{
1 => new Exception_1(target),
_ => default(Exception),
};
}

public virtual TargetPointer GetNestedExceptionInfo(TargetPointer exception, out TargetPointer nextNestedException) => throw new NotImplementedException();
public virtual ExceptionData GetExceptionData(TargetPointer managedException) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@ internal record struct ModuleLookupTables(
internal interface ILoader : IContract
{
static string IContract.Name => nameof(Loader);
static IContract IContract.Create(Target target, int version)
{
return version switch
{
1 => new Loader_1(target),
_ => default(Loader),
};
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,7 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts;
internal interface IObject : IContract
{
static string IContract.Name { get; } = nameof(Object);
static IContract IContract.Create(Target target, int version)
{
ulong methodTableOffset = (ulong)target.GetTypeInfo(DataType.Object).Fields["m_pMethTab"].Offset;
byte objectToMethodTableUnmask = target.ReadGlobal<byte>(Constants.Globals.ObjectToMethodTableUnmask);
TargetPointer stringMethodTable = target.ReadPointer(
target.ReadGlobalPointer(Constants.Globals.StringMethodTable));
TargetPointer syncTableEntries = target.ReadPointer(
target.ReadGlobalPointer(Constants.Globals.SyncTableEntries));
return version switch
{
1 => new Object_1(target, methodTableOffset, objectToMethodTableUnmask, stringMethodTable, syncTableEntries),
_ => default(Object),
};
}

public virtual TargetPointer GetMethodTableAddress(TargetPointer address) => throw new NotImplementedException();

public virtual string GetStringValue(TargetPointer address) => throw new NotImplementedException();
public virtual TargetPointer GetArrayData(TargetPointer address, out uint count, out TargetPointer boundsStart, out TargetPointer lowerBounds) => throw new NotImplementedException();
public virtual bool GetBuiltInComData(TargetPointer address, out TargetPointer rcw, out TargetPointer ccw) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,6 @@ public enum ArrayFunctionType
internal interface IRuntimeTypeSystem : IContract
{
static string IContract.Name => nameof(RuntimeTypeSystem);
static IContract IContract.Create(Target target, int version)
{
TargetPointer targetPointer = target.ReadGlobalPointer(Constants.Globals.FreeObjectMethodTable);
TargetPointer freeObjectMethodTable = target.ReadPointer(targetPointer);
ulong methodDescAlignment = target.ReadGlobal<ulong>(Constants.Globals.MethodDescAlignment);
return version switch
{
1 => new RuntimeTypeSystem_1(target, freeObjectMethodTable, methodDescAlignment),
_ => default(RuntimeTypeSystem),
};
}

#region TypeHandle inspection APIs
public virtual TypeHandle GetTypeHandle(TargetPointer address) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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.Contracts;

internal record struct ThreadStoreData(
int ThreadCount,
TargetPointer FirstThread,
TargetPointer FinalizerThread,
TargetPointer GCThread);

internal record struct ThreadStoreCounts(
int UnstartedThreadCount,
int BackgroundThreadCount,
int PendingThreadCount,
int DeadThreadCount);

[Flags]
internal enum ThreadState
{
Unknown = 0x00000000,
Hijacked = 0x00000080, // Return address has been hijacked
Background = 0x00000200, // Thread is a background thread
Unstarted = 0x00000400, // Thread has never been started
Dead = 0x00000800, // Thread is dead
ThreadPoolWorker = 0x01000000, // Thread is a thread pool worker thread
}

internal record struct ThreadData(
uint Id,
TargetNUInt OSId,
ThreadState State,
bool PreemptiveGCDisabled,
TargetPointer AllocContextPointer,
TargetPointer AllocContextLimit,
TargetPointer Frame,
TargetPointer FirstNestedException,
TargetPointer TEB,
TargetPointer LastThrownObjectHandle,
TargetPointer NextThread);

internal interface IThread : IContract
{
static string IContract.Name { get; } = nameof(Thread);

public virtual ThreadStoreData GetThreadStoreData() => throw new NotImplementedException();
public virtual ThreadStoreCounts GetThreadCounts() => throw new NotImplementedException();
public virtual ThreadData GetThreadData(TargetPointer thread) => throw new NotImplementedException();
}

internal readonly struct Thread : IThread
{
// Everything throws NotImplementedException
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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;

internal interface IContractFactory<out TContract> where TContract : Contracts.IContract
{
TContract CreateContract(Target target, int version);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
<RootNamespace>Microsoft.Diagnostics.DataContractReader</RootNamespace>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Do not produce a public package. This ships as part of the runtime -->
<IsShippingPackage>false</IsShippingPackage>
<InvariantGlobalization>true</InvariantGlobalization>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="Microsoft.Diagnostics.DataContractReader.Tests" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.DataContractReader.Contracts" />
<InternalsVisibleTo Include="Microsoft.Diagnostics.DataContractReader" />
<InternalsVisibleTo Include="cdacreader" Condition="'$(TargetsWindows)' == 'true'"/>
<InternalsVisibleTo Include="libcdacreader" Condition="'$(TargetsWindows)' != 'true'"/>
</ItemGroup>
</Project>
Loading

0 comments on commit a7e5426

Please sign in to comment.