Skip to content

Commit 4b8fcd5

Browse files
authored
[cdac] Read types from contract descriptor (#101994)
- 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
1 parent 2385d08 commit 4b8fcd5

File tree

12 files changed

+394
-20
lines changed

12 files changed

+394
-20
lines changed

src/coreclr/debug/runtimeinfo/contracts.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// cdac-build-tool can take multiple "-c contract_file" arguments
1010
// so to conditionally include contracts, put additional contracts in a separate file
1111
{
12+
"Thread": 1,
1213
"SOSBreakingChangeVersion": 1 // example contract: "runtime exports an SOS breaking change version global"
1314
}
1415

src/coreclr/debug/runtimeinfo/datadescriptor.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,17 @@
103103
CDAC_BASELINE("empty")
104104
CDAC_TYPES_BEGIN()
105105

106-
CDAC_TYPE_BEGIN(ManagedThread)
107-
CDAC_TYPE_INDETERMINATE(ManagedThread)
108-
CDAC_TYPE_FIELD(ManagedThread, GCHandle, GCHandle, cdac_offsets<Thread>::ExposedObject)
109-
CDAC_TYPE_FIELD(ManagedThread, pointer, LinkNext, cdac_offsets<Thread>::Link)
110-
CDAC_TYPE_END(ManagedThread)
106+
CDAC_TYPE_BEGIN(Thread)
107+
CDAC_TYPE_INDETERMINATE(Thread)
108+
CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_offsets<Thread>::ExposedObject)
109+
CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_offsets<Thread>::Link)
110+
CDAC_TYPE_END(Thread)
111+
112+
CDAC_TYPE_BEGIN(ThreadStore)
113+
CDAC_TYPE_INDETERMINATE(ThreadStore)
114+
CDAC_TYPE_FIELD(ThreadStore, /*omit type*/, ThreadCount, cdac_offsets<ThreadStore>::ThreadCount)
115+
CDAC_TYPE_FIELD(ThreadStore, /*omit type*/, ThreadList, cdac_offsets<ThreadStore>::ThreadList)
116+
CDAC_TYPE_END(ThreadStore)
111117

112118
CDAC_TYPE_BEGIN(GCHandle)
113119
CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE))
@@ -116,7 +122,7 @@ CDAC_TYPE_END(GCHandle)
116122
CDAC_TYPES_END()
117123

118124
CDAC_GLOBALS_BEGIN()
119-
CDAC_GLOBAL_POINTER(ManagedThreadStore, &ThreadStore::s_pThreadStore)
125+
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
120126
#if FEATURE_EH_FUNCLETS
121127
CDAC_GLOBAL(FeatureEHFunclets, uint8, 1)
122128
#else

src/coreclr/vm/threads.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4300,6 +4300,15 @@ class ThreadStore
43004300
void OnMaxGenerationGCStarted();
43014301
bool ShouldTriggerGCForDeadThreads();
43024302
void TriggerGCForDeadThreadsIfNecessary();
4303+
4304+
template<typename T> friend struct ::cdac_offsets;
4305+
};
4306+
4307+
template<>
4308+
struct cdac_offsets<ThreadStore>
4309+
{
4310+
static constexpr size_t ThreadList = offsetof(ThreadStore, m_ThreadList);
4311+
static constexpr size_t ThreadCount = offsetof(ThreadStore, m_ThreadCount);
43034312
};
43044313

43054314
struct TSSuspendHelper {

src/native/managed/cdacreader/src/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ internal static class Constants
88
internal static class Globals
99
{
1010
// See src/coreclr/debug/runtimeinfo/datadescriptor.h
11+
internal const string ThreadStore = nameof(ThreadStore);
1112
internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
1213
}
1314
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
6+
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
7+
8+
internal interface IContract
9+
{
10+
static virtual string Name => throw new NotImplementedException();
11+
static virtual IContract Create(Target target, int version) => throw new NotImplementedException();
12+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
8+
9+
internal sealed class Registry
10+
{
11+
// Contracts that have already been created for a target.
12+
// Items should not be removed from this, only added.
13+
private readonly Dictionary<Type, IContract> _contracts = [];
14+
private readonly Target _target;
15+
16+
public Registry(Target target)
17+
{
18+
_target = target;
19+
}
20+
21+
public IThread Thread => GetContract<IThread>();
22+
23+
private T GetContract<T>() where T : IContract
24+
{
25+
if (_contracts.TryGetValue(typeof(T), out IContract? contractMaybe))
26+
return (T)contractMaybe;
27+
28+
if (!_target.TryGetContractVersion(T.Name, out int version))
29+
throw new NotImplementedException();
30+
31+
// Create and register the contract
32+
IContract contract = T.Create(_target, version);
33+
if (_contracts.TryAdd(typeof(T), contract))
34+
return (T)contract;
35+
36+
// Contract was already registered by someone else
37+
return (T)_contracts[typeof(T)];
38+
}
39+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
6+
namespace Microsoft.Diagnostics.DataContractReader.Contracts;
7+
8+
// TODO: [cdac] Add other counts / threads
9+
internal record struct ThreadStoreData(
10+
int ThreadCount,
11+
TargetPointer FirstThread);
12+
13+
internal interface IThread : IContract
14+
{
15+
static string IContract.Name { get; } = nameof(Thread);
16+
static IContract IContract.Create(Target target, int version)
17+
{
18+
TargetPointer threadStore = target.ReadGlobalPointer(Constants.Globals.ThreadStore);
19+
return version switch
20+
{
21+
1 => new Thread_1(target, threadStore),
22+
_ => default(Thread),
23+
};
24+
}
25+
26+
public virtual ThreadStoreData GetThreadStoreData() => throw new NotImplementedException();
27+
}
28+
29+
internal readonly struct Thread : IThread
30+
{
31+
// Everything throws NotImplementedException
32+
}
33+
34+
internal readonly struct Thread_1 : IThread
35+
{
36+
private readonly Target _target;
37+
private readonly TargetPointer _threadStoreAddr;
38+
39+
internal Thread_1(Target target, TargetPointer threadStore)
40+
{
41+
_target = target;
42+
_threadStoreAddr = threadStore;
43+
}
44+
45+
ThreadStoreData IThread.GetThreadStoreData()
46+
{
47+
Data.ThreadStore? threadStore;
48+
if (!_target.ProcessedData.TryGet(_threadStoreAddr.Value, out threadStore))
49+
{
50+
threadStore = new Data.ThreadStore(_target, _threadStoreAddr);
51+
52+
// Still okay if processed data is already registered by someone else
53+
_ = _target.ProcessedData.TryRegister(_threadStoreAddr.Value, threadStore);
54+
}
55+
56+
return new ThreadStoreData(threadStore.ThreadCount, threadStore.FirstThread);
57+
}
58+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.Diagnostics.DataContractReader.Data;
5+
6+
internal sealed class ThreadStore
7+
{
8+
public ThreadStore(Target target, TargetPointer pointer)
9+
{
10+
Target.TypeInfo type = target.GetTypeInfo(DataType.ThreadStore);
11+
TargetPointer addr = target.ReadPointer(pointer.Value);
12+
13+
ThreadCount = target.Read<int>(addr.Value + (ulong)type.Fields[nameof(ThreadCount)].Offset);
14+
FirstThread = TargetPointer.Null;
15+
}
16+
17+
public int ThreadCount { get; init; }
18+
19+
public TargetPointer FirstThread { get; init; }
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.Diagnostics.DataContractReader;
5+
6+
public enum DataType
7+
{
8+
Unknown = 0,
9+
10+
int8,
11+
uint8,
12+
int16,
13+
uint16,
14+
int32,
15+
uint32,
16+
int64,
17+
uint64,
18+
nint,
19+
nuint,
20+
pointer,
21+
22+
GCHandle,
23+
Thread,
24+
ThreadStore,
25+
}

src/native/managed/cdacreader/src/Legacy/SOSDacImpl.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,25 @@ public int GetBreakingChangeVersion()
106106
public unsafe int GetThreadFromThinlockID(uint thinLockId, ulong* pThread) => HResults.E_NOTIMPL;
107107
public unsafe int GetThreadLocalModuleData(ulong thread, uint index, void* data) => HResults.E_NOTIMPL;
108108
public unsafe int GetThreadpoolData(void* data) => HResults.E_NOTIMPL;
109-
public unsafe int GetThreadStoreData(DacpThreadStoreData* data) => HResults.E_NOTIMPL;
109+
110+
public unsafe int GetThreadStoreData(DacpThreadStoreData* data)
111+
{
112+
try
113+
{
114+
Contracts.IThread thread = _target.Contracts.Thread;
115+
Contracts.ThreadStoreData threadStoreData = thread.GetThreadStoreData();
116+
data->threadCount = threadStoreData.ThreadCount;
117+
data->firstThread = threadStoreData.FirstThread.Value;
118+
data->fHostConfig = 0;
119+
}
120+
catch (Exception ex)
121+
{
122+
return ex.HResult;
123+
}
124+
125+
return HResults.E_NOTIMPL;
126+
}
127+
110128
public unsafe int GetTLSIndex(uint* pIndex) => HResults.E_NOTIMPL;
111129
public unsafe int GetUsefulGlobals(void* data) => HResults.E_NOTIMPL;
112130
public unsafe int GetWorkRequestData(ulong addrWorkRequest, void* data) => HResults.E_NOTIMPL;

0 commit comments

Comments
 (0)