Skip to content
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

[cdac] Read types from contract descriptor #101994

Merged
merged 10 commits into from
May 13, 2024
1 change: 1 addition & 0 deletions src/coreclr/debug/runtimeinfo/contracts.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// cdac-build-tool can take multiple "-c contract_file" arguments
// so to conditionally include contracts, put additional contracts in a separate file
{
"Thread": 1,
"SOSBreakingChangeVersion": 1 // example contract: "runtime exports an SOS breaking change version global"
}

18 changes: 12 additions & 6 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,17 @@
CDAC_BASELINE("empty")
CDAC_TYPES_BEGIN()

CDAC_TYPE_BEGIN(ManagedThread)
CDAC_TYPE_INDETERMINATE(ManagedThread)
CDAC_TYPE_FIELD(ManagedThread, GCHandle, GCHandle, cdac_offsets<Thread>::ExposedObject)
CDAC_TYPE_FIELD(ManagedThread, pointer, LinkNext, cdac_offsets<Thread>::Link)
CDAC_TYPE_END(ManagedThread)
CDAC_TYPE_BEGIN(Thread)
CDAC_TYPE_INDETERMINATE(Thread)
CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_offsets<Thread>::ExposedObject)
CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_offsets<Thread>::Link)
CDAC_TYPE_END(Thread)

CDAC_TYPE_BEGIN(ThreadStore)
CDAC_TYPE_INDETERMINATE(ThreadStore)
CDAC_TYPE_FIELD(ThreadStore, /*omit type*/, ThreadCount, cdac_offsets<ThreadStore>::ThreadCount)
CDAC_TYPE_FIELD(ThreadStore, /*omit type*/, ThreadList, cdac_offsets<ThreadStore>::ThreadList)
CDAC_TYPE_END(ThreadStore)

CDAC_TYPE_BEGIN(GCHandle)
CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE))
Expand All @@ -116,7 +122,7 @@ CDAC_TYPE_END(GCHandle)
CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()
CDAC_GLOBAL_POINTER(ManagedThreadStore, &ThreadStore::s_pThreadStore)
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
#if FEATURE_EH_FUNCLETS
CDAC_GLOBAL(FeatureEHFunclets, uint8, 1)
#else
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/vm/threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -4335,6 +4335,15 @@ class ThreadStore
void OnMaxGenerationGCStarted();
bool ShouldTriggerGCForDeadThreads();
void TriggerGCForDeadThreadsIfNecessary();

template<typename T> friend struct ::cdac_offsets;
};

template<>
struct cdac_offsets<ThreadStore>
{
static constexpr size_t ThreadList = offsetof(ThreadStore, m_ThreadList);
static constexpr size_t ThreadCount = offsetof(ThreadStore, m_ThreadCount);
};

struct TSSuspendHelper {
Expand Down
1 change: 1 addition & 0 deletions src/native/managed/cdacreader/src/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ internal static class Constants
internal static class Globals
{
// See src/coreclr/debug/runtimeinfo/datadescriptor.h
internal const string ThreadStore = nameof(ThreadStore);
internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
}
}
12 changes: 12 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/IContract.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// 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 interface IContract
{
static virtual string Name => throw new NotImplementedException();
static virtual IContract Create(Target target, int version) => throw new NotImplementedException();
}
39 changes: 39 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Registry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// 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.Contracts;

internal sealed class Registry
{
// Contracts that have already been created for a target.
// Items should not be removed from this, only added.
private readonly Dictionary<Type, IContract> _contracts = [];
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
private readonly Target _target;

public Registry(Target target)
{
_target = target;
}

public IThread Thread => GetContract<IThread>();

private T GetContract<T>() where T : IContract
{
if (_contracts.TryGetValue(typeof(T), out IContract? contractMaybe))
return (T)contractMaybe;

if (!_target.TryGetContractVersion(T.Name, out int version))
throw new NotImplementedException();

// Create and register the contract
IContract contract = T.Create(_target, version);
if (_contracts.TryAdd(typeof(T), contract))
return (T)contract;

// Contract was already registered by someone else
return (T)_contracts[typeof(T)];
}
}
58 changes: 58 additions & 0 deletions src/native/managed/cdacreader/src/Contracts/Thread.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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;

// TODO: [cdac] Add other counts / threads
internal record struct ThreadStoreData(
int ThreadCount,
TargetPointer FirstThread);

internal interface IThread : IContract
{
static string IContract.Name { get; } = nameof(Thread);
static IContract IContract.Create(Target target, int version)
{
TargetPointer threadStore = target.ReadGlobalPointer(Constants.Globals.ThreadStore);
return version switch
{
1 => new Thread_1(target, threadStore),
_ => default(Thread),
};
}

public virtual ThreadStoreData GetThreadStoreData() => throw new NotImplementedException();
}

internal readonly struct Thread : IThread
{
// Everything throws NotImplementedException
}

internal readonly struct Thread_1 : IThread
{
private readonly Target _target;
private readonly TargetPointer _threadStoreAddr;

internal Thread_1(Target target, TargetPointer threadStore)
{
_target = target;
_threadStoreAddr = threadStore;
}

ThreadStoreData IThread.GetThreadStoreData()
{
Data.ThreadStore? threadStore;
if (!_target.ProcessedData.TryGet(_threadStoreAddr.Value, out threadStore))
{
threadStore = new Data.ThreadStore(_target, _threadStoreAddr);

// Still okay if processed data is already registered by someone else
_ = _target.ProcessedData.TryRegister(_threadStoreAddr.Value, threadStore);
}

return new ThreadStoreData(threadStore.ThreadCount, threadStore.FirstThread);
}
}
20 changes: 20 additions & 0 deletions src/native/managed/cdacreader/src/Data/ThreadStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Diagnostics.DataContractReader.Data;

internal sealed class ThreadStore
{
public ThreadStore(Target target, TargetPointer pointer)
{
Target.TypeInfo type = target.GetTypeInfo(DataType.ThreadStore);
TargetPointer addr = target.ReadPointer(pointer.Value);

ThreadCount = target.Read<int>(addr.Value + (ulong)type.Fields[nameof(ThreadCount)].Offset);
FirstThread = TargetPointer.Null;
}

public int ThreadCount { get; init; }

public TargetPointer FirstThread { get; init; }
}
25 changes: 25 additions & 0 deletions src/native/managed/cdacreader/src/DataType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Diagnostics.DataContractReader;

public enum DataType
{
Unknown = 0,

int8,
uint8,
int16,
uint16,
int32,
uint32,
int64,
uint64,
nint,
nuint,
pointer,

GCHandle,
Thread,
ThreadStore,
}
20 changes: 19 additions & 1 deletion src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,25 @@ public int GetBreakingChangeVersion()
public unsafe int GetThreadFromThinlockID(uint thinLockId, ulong* pThread) => HResults.E_NOTIMPL;
public unsafe int GetThreadLocalModuleData(ulong thread, uint index, void* data) => HResults.E_NOTIMPL;
public unsafe int GetThreadpoolData(void* data) => HResults.E_NOTIMPL;
public unsafe int GetThreadStoreData(DacpThreadStoreData* data) => HResults.E_NOTIMPL;

public unsafe int GetThreadStoreData(DacpThreadStoreData* data)
{
try
{
Contracts.IThread thread = _target.Contracts.Thread;
Contracts.ThreadStoreData threadStoreData = thread.GetThreadStoreData();
data->threadCount = threadStoreData.ThreadCount;
data->firstThread = threadStoreData.FirstThread.Value;
data->fHostConfig = 0;
}
catch (Exception ex)
{
return ex.HResult;
}

return HResults.E_NOTIMPL;
}

public unsafe int GetTLSIndex(uint* pIndex) => HResults.E_NOTIMPL;
public unsafe int GetUsefulGlobals(void* data) => HResults.E_NOTIMPL;
public unsafe int GetWorkRequestData(ulong addrWorkRequest, void* data) => HResults.E_NOTIMPL;
Expand Down
Loading