Skip to content

Commit

Permalink
[cdac] Read types from contract descriptor (#101994)
Browse files Browse the repository at this point in the history
- Read/store types from contract descriptor into an internal representation of the type layout info
- Add functions to `Target` for getting type info and contract version
- Add placeholder for thread contract and getting thread store data
  - Add ThreadStore to data descriptor
  - `GetThreadStoreData` continues to return E_NOTIMPL, but will go through trying to use the contract
- Store processed data for the target by its address and data model type
- Add unit tests for getting type info
  • Loading branch information
elinor-fung authored May 13, 2024
1 parent 2385d08 commit 4b8fcd5
Show file tree
Hide file tree
Showing 12 changed files with 394 additions and 20 deletions.
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 @@ -4300,6 +4300,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 = [];
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

0 comments on commit 4b8fcd5

Please sign in to comment.