Skip to content

Commit 547e69e

Browse files
authored
[cdac] Implement ISOSDacInterface.GetThreadData (#103324)
- Implement `GetThreadData` in `Thread` contract - Finish implementing `ISOSDacInterface::GetThreadData` in cDAC - Add `ExceptionInfo` to cDAC types
1 parent 01172db commit 547e69e

File tree

13 files changed

+204
-65
lines changed

13 files changed

+204
-65
lines changed

docs/design/datacontracts/Thread.md

Lines changed: 6 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,61 +19,12 @@ record struct ThreadStoreCounts (
1919

2020
enum ThreadState
2121
{
22-
TS_Unknown = 0x00000000, // threads are initialized this way
23-
24-
TS_AbortRequested = 0x00000001, // Abort the thread
25-
26-
TS_GCSuspendRedirected = 0x00000004, // ThreadSuspend::SuspendRuntime has redirected the thread to suspention routine.
27-
28-
TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads?
29-
TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only)
30-
31-
TS_LegalToJoin = 0x00000020, // Is it now legal to attempt a Join()
32-
33-
TS_ExecutingOnAltStack = 0x00000040, // Runtime is executing on an alternate stack located anywhere in the memory
34-
35-
TS_Hijacked = 0x00000080, // Return address has been hijacked
36-
37-
// unused = 0x00000100,
38-
TS_Background = 0x00000200, // Thread is a background thread
39-
TS_Unstarted = 0x00000400, // Thread has never been started
40-
TS_Dead = 0x00000800, // Thread is dead
41-
42-
TS_WeOwn = 0x00001000, // Exposed object initiated this thread
43-
TS_CoInitialized = 0x00002000, // CoInitialize has been called for this thread
44-
45-
TS_InSTA = 0x00004000, // Thread hosts an STA
46-
TS_InMTA = 0x00008000, // Thread is part of the MTA
47-
48-
// Some bits that only have meaning for reporting the state to clients.
49-
TS_ReportDead = 0x00010000, // in WaitForOtherThreads()
50-
TS_FullyInitialized = 0x00020000, // Thread is fully initialized and we are ready to broadcast its existence to external clients
51-
52-
TS_TaskReset = 0x00040000, // The task is reset
53-
54-
TS_SyncSuspended = 0x00080000, // Suspended via WaitSuspendEvent
55-
TS_DebugWillSync = 0x00100000, // Debugger will wait for this thread to sync
56-
57-
TS_StackCrawlNeeded = 0x00200000, // A stackcrawl is needed on this thread, such as for thread abort
58-
// See comment for s_pWaitForStackCrawlEvent for reason.
59-
60-
// unused = 0x00400000,
61-
62-
// unused = 0x00800000,
63-
TS_TPWorkerThread = 0x01000000, // is this a threadpool worker thread?
64-
65-
TS_Interruptible = 0x02000000, // sitting in a Sleep(), Wait(), Join()
66-
TS_Interrupted = 0x04000000, // was awakened by an interrupt APC. !!! This can be moved to TSNC
67-
68-
TS_CompletionPortThread = 0x08000000, // Completion port thread
69-
70-
TS_AbortInitiated = 0x10000000, // set when abort is begun
71-
72-
TS_Finalized = 0x20000000, // The associated managed Thread object has been finalized.
73-
// We can clean up the unmanaged part now.
74-
75-
TS_FailStarted = 0x40000000, // The thread fails during startup.
76-
TS_Detached = 0x80000000, // Thread was detached by DllMain
22+
Unknown = 0x00000000, // threads are initialized this way
23+
Hijacked = 0x00000080, // Return address has been hijacked
24+
Background = 0x00000200, // Thread is a background thread
25+
Unstarted = 0x00000400, // Thread has never been started
26+
Dead = 0x00000800, // Thread is dead
27+
ThreadPoolWorker = 0x01000000, // is this a threadpool worker thread?
7728
}
7829

7930
record struct ThreadData (

src/coreclr/debug/runtimeinfo/datadescriptor.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,17 @@ CDAC_TYPE_BEGIN(Thread)
107107
CDAC_TYPE_INDETERMINATE(Thread)
108108
CDAC_TYPE_FIELD(Thread, /*uint32*/, Id, cdac_offsets<Thread>::Id)
109109
CDAC_TYPE_FIELD(Thread, /*nuint*/, OSId, cdac_offsets<Thread>::OSId)
110+
CDAC_TYPE_FIELD(Thread, /*uint32*/, State, cdac_offsets<Thread>::State)
111+
CDAC_TYPE_FIELD(Thread, /*uint32*/, PreemptiveGCDisabled, cdac_offsets<Thread>::PreemptiveGCDisabled)
112+
CDAC_TYPE_FIELD(Thread, /*pointer*/, AllocContext, cdac_offsets<Thread>::AllocContext)
113+
CDAC_TYPE_FIELD(Thread, /*pointer*/, Frame, cdac_offsets<Thread>::Frame)
114+
CDAC_TYPE_FIELD(Thread, /*pointer*/, ExceptionTracker, cdac_offsets<Thread>::ExceptionTracker)
110115
CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_offsets<Thread>::ExposedObject)
111116
CDAC_TYPE_FIELD(Thread, GCHandle, LastThrownObject, cdac_offsets<Thread>::LastThrownObject)
112117
CDAC_TYPE_FIELD(Thread, pointer, LinkNext, cdac_offsets<Thread>::Link)
118+
#ifndef TARGET_UNIX
119+
CDAC_TYPE_FIELD(Thread, /*pointer*/, TEB, cdac_offsets<Thread>::TEB)
120+
#endif
113121
CDAC_TYPE_END(Thread)
114122

115123
CDAC_TYPE_BEGIN(ThreadStore)
@@ -122,13 +130,29 @@ CDAC_TYPE_FIELD(ThreadStore, /*int32*/, PendingCount, cdac_offsets<ThreadStore>:
122130
CDAC_TYPE_FIELD(ThreadStore, /*int32*/, DeadCount, cdac_offsets<ThreadStore>::DeadCount)
123131
CDAC_TYPE_END(ThreadStore)
124132

133+
CDAC_TYPE_BEGIN(GCAllocContext)
134+
CDAC_TYPE_INDETERMINATE(GCAllocContext)
135+
CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Pointer, offsetof(gc_alloc_context, alloc_ptr))
136+
CDAC_TYPE_FIELD(GCAllocContext, /*pointer*/, Limit, offsetof(gc_alloc_context, alloc_limit))
137+
CDAC_TYPE_END(GCAllocContext)
138+
139+
CDAC_TYPE_BEGIN(ExceptionInfo)
140+
CDAC_TYPE_INDETERMINATE(ExceptionInfo)
141+
#if FEATURE_EH_FUNCLETS
142+
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExceptionTrackerBase, m_pPrevNestedInfo))
143+
#else
144+
CDAC_TYPE_FIELD(PreviousNestedInfo, /*pointer*/, PreviousNestedInfo, offsetof(ExInfo, m_pPrevNestedInfo))
145+
#endif
146+
CDAC_TYPE_END(ExceptionInfo)
147+
125148
CDAC_TYPE_BEGIN(GCHandle)
126149
CDAC_TYPE_SIZE(sizeof(OBJECTHANDLE))
127150
CDAC_TYPE_END(GCHandle)
128151

129152
CDAC_TYPES_END()
130153

131154
CDAC_GLOBALS_BEGIN()
155+
CDAC_GLOBAL_POINTER(AppDomain, &AppDomain::m_pTheAppDomain)
132156
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
133157
CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread)
134158
CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread)

src/coreclr/vm/appdomain.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1675,10 +1675,10 @@ class AppDomain : public BaseDomain
16751675
}
16761676
#endif
16771677

1678-
private:
16791678
// The one and only AppDomain
16801679
SPTR_DECL(AppDomain, m_pTheAppDomain);
16811680

1681+
private:
16821682
SString m_friendlyName;
16831683
PTR_Assembly m_pRootAssembly;
16841684

src/coreclr/vm/exstate.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class DebuggerExState;
1212
class EHClauseInfo;
1313

1414
#include "exceptionhandling.h"
15+
#include "cdacoffsets.h"
1516

1617
#if !defined(FEATURE_EH_FUNCLETS)
1718
// ExInfo contains definitions for 32bit
@@ -50,6 +51,8 @@ class ThreadExceptionState
5051
// ExceptionTracker or the ExInfo as appropriate for the platform
5152
friend class ProfToEEInterfaceImpl;
5253

54+
template<typename T> friend struct ::cdac_offsets;
55+
5356
#ifdef FEATURE_EH_FUNCLETS
5457
friend class ExceptionTracker;
5558
friend struct ExInfo;

src/coreclr/vm/threads.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3971,9 +3971,25 @@ struct cdac_offsets<Thread>
39713971
{
39723972
static constexpr size_t Id = offsetof(Thread, m_ThreadId);
39733973
static constexpr size_t OSId = offsetof(Thread, m_OSThreadId);
3974+
static constexpr size_t State = offsetof(Thread, m_State);
3975+
static constexpr size_t PreemptiveGCDisabled = offsetof(Thread, m_fPreemptiveGCDisabled);
3976+
static constexpr size_t AllocContext = offsetof(Thread, m_alloc_context);
3977+
static constexpr size_t Frame = offsetof(Thread, m_pFrame);
39743978
static constexpr size_t ExposedObject = offsetof(Thread, m_ExposedObject);
39753979
static constexpr size_t LastThrownObject = offsetof(Thread, m_LastThrownObjectHandle);
39763980
static constexpr size_t Link = offsetof(Thread, m_Link);
3981+
3982+
static_assert(std::is_same<decltype(std::declval<Thread>().m_ExceptionState), ThreadExceptionState>::value,
3983+
"Thread::m_ExceptionState is of type ThreadExceptionState");
3984+
#ifdef FEATURE_EH_FUNCLETS
3985+
static constexpr size_t ExceptionTracker = offsetof(Thread, m_ExceptionState) + offsetof(ThreadExceptionState, m_pCurrentTracker);
3986+
#else
3987+
static constexpr size_t ExceptionTracker = offsetof(Thread, m_ExceptionState) + offsetof(ThreadExceptionState, m_currentExInfo);
3988+
#endif
3989+
3990+
#ifndef TARGET_UNIX
3991+
static constexpr size_t TEB = offsetof(Thread, m_pTEB);
3992+
#endif
39773993
};
39783994

39793995
// End of class Thread

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ internal static class Constants
88
internal static class Globals
99
{
1010
// See src/coreclr/debug/runtimeinfo/datadescriptor.h
11+
internal const string AppDomain = nameof(AppDomain);
1112
internal const string ThreadStore = nameof(ThreadStore);
1213
internal const string FinalizerThread = nameof(FinalizerThread);
1314
internal const string GCThread = nameof(GCThread);
1415

16+
internal const string FeatureEHFunclets = nameof(FeatureEHFunclets);
1517
internal const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion);
1618
}
1719
}

src/native/managed/cdacreader/src/Contracts/Thread.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,28 @@ internal record struct ThreadStoreCounts(
1717
int PendingThreadCount,
1818
int DeadThreadCount);
1919

20+
[Flags]
21+
internal enum ThreadState
22+
{
23+
Unknown = 0x00000000,
24+
Hijacked = 0x00000080, // Return address has been hijacked
25+
Background = 0x00000200, // Thread is a background thread
26+
Unstarted = 0x00000400, // Thread has never been started
27+
Dead = 0x00000800, // Thread is dead
28+
ThreadPoolWorker = 0x01000000, // Thread is a thread pool worker thread
29+
}
30+
2031
internal record struct ThreadData(
2132
uint Id,
33+
TargetNUInt OSId,
34+
ThreadState State,
35+
bool PreemptiveGCDisabled,
36+
TargetPointer AllocContextPointer,
37+
TargetPointer AllocContextLimit,
38+
TargetPointer Frame,
39+
TargetPointer FirstNestedException,
40+
TargetPointer TEB,
41+
TargetPointer LastThrownObjectHandle,
2242
TargetPointer NextThread);
2343

2444
internal interface IThread : IContract
@@ -85,8 +105,29 @@ ThreadStoreCounts IThread.GetThreadCounts()
85105
ThreadData IThread.GetThreadData(TargetPointer threadPointer)
86106
{
87107
Data.Thread thread = _target.ProcessedData.GetOrAdd<Data.Thread>(threadPointer);
108+
109+
// Exception tracker is a pointer when EH funclets are enabled
110+
TargetPointer address = _target.ReadGlobal<byte>(Constants.Globals.FeatureEHFunclets) != 0
111+
? _target.ReadPointer(thread.ExceptionTracker)
112+
: thread.ExceptionTracker;
113+
TargetPointer firstNestedException = TargetPointer.Null;
114+
if (address != TargetPointer.Null)
115+
{
116+
Data.ExceptionInfo exceptionInfo = _target.ProcessedData.GetOrAdd<Data.ExceptionInfo>(address);
117+
firstNestedException = exceptionInfo.PreviousNestedInfo;
118+
}
119+
88120
return new ThreadData(
89121
thread.Id,
122+
thread.OSId,
123+
(ThreadState)thread.State,
124+
(thread.PreemptiveGCDisabled & 0x1) != 0,
125+
thread.AllocContext is null ? TargetPointer.Null : thread.AllocContext.Pointer,
126+
thread.AllocContext is null ? TargetPointer.Null : thread.AllocContext.Limit,
127+
thread.Frame,
128+
firstNestedException,
129+
thread.TEB,
130+
thread.LastThrownObject,
90131
GetThreadFromLink(thread.LinkNext));
91132
}
92133

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 ExceptionInfo : IData<ExceptionInfo>
7+
{
8+
static ExceptionInfo IData<ExceptionInfo>.Create(Target target, TargetPointer address)
9+
=> new ExceptionInfo(target, address);
10+
11+
public ExceptionInfo(Target target, TargetPointer address)
12+
{
13+
Target.TypeInfo type = target.GetTypeInfo(DataType.ExceptionInfo);
14+
15+
PreviousNestedInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(PreviousNestedInfo)].Offset);
16+
}
17+
18+
public TargetPointer PreviousNestedInfo { get; init; }
19+
}
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 GCAllocContext : IData<GCAllocContext>
7+
{
8+
static GCAllocContext IData<GCAllocContext>.Create(Target target, TargetPointer address)
9+
=> new GCAllocContext(target, address);
10+
11+
public GCAllocContext(Target target, TargetPointer address)
12+
{
13+
Target.TypeInfo type = target.GetTypeInfo(DataType.GCAllocContext);
14+
Pointer = target.ReadPointer(address + (ulong)type.Fields[nameof(Pointer)].Offset);
15+
Limit = target.ReadPointer(address + (ulong)type.Fields[nameof(Limit)].Offset);
16+
}
17+
18+
public TargetPointer Pointer { get; init; }
19+
public TargetPointer Limit { get; init; }
20+
}

src/native/managed/cdacreader/src/Data/Thread.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,35 @@ public Thread(Target target, TargetPointer address)
1313
Target.TypeInfo type = target.GetTypeInfo(DataType.Thread);
1414

1515
Id = target.Read<uint>(address + (ulong)type.Fields[nameof(Id)].Offset);
16+
OSId = target.ReadNUInt(address + (ulong)type.Fields[nameof(OSId)].Offset);
17+
State = target.Read<uint>(address + (ulong)type.Fields[nameof(State)].Offset);
18+
PreemptiveGCDisabled = target.Read<uint>(address + (ulong)type.Fields[nameof(PreemptiveGCDisabled)].Offset);
19+
20+
TargetPointer allocContextPointer = target.ReadPointer(address + (ulong)type.Fields[nameof(AllocContext)].Offset);
21+
if (allocContextPointer != TargetPointer.Null)
22+
AllocContext = target.ProcessedData.GetOrAdd<GCAllocContext>(allocContextPointer);
23+
24+
Frame = target.ReadPointer(address + (ulong)type.Fields[nameof(Frame)].Offset);
25+
26+
// TEB does not exist on certain platforms
27+
TEB = type.Fields.TryGetValue(nameof(TEB), out Target.FieldInfo fieldInfo)
28+
? target.ReadPointer(address + (ulong)fieldInfo.Offset)
29+
: TargetPointer.Null;
30+
LastThrownObject = target.ReadPointer(address + (ulong)type.Fields[nameof(LastThrownObject)].Offset);
1631
LinkNext = target.ReadPointer(address + (ulong)type.Fields[nameof(LinkNext)].Offset);
32+
33+
// Address of the exception tracker - how it should be read depends on EH funclets feature global value
34+
ExceptionTracker = address + (ulong)type.Fields[nameof(ExceptionTracker)].Offset;
1735
}
1836

1937
public uint Id { get; init; }
38+
public TargetNUInt OSId { get; init; }
39+
public uint State { get; init; }
40+
public uint PreemptiveGCDisabled { get; init; }
41+
public GCAllocContext? AllocContext { get; init; }
42+
public TargetPointer Frame { get; init; }
43+
public TargetPointer TEB { get; init; }
44+
public TargetPointer LastThrownObject { get; init; }
2045
public TargetPointer LinkNext { get; init; }
46+
public TargetPointer ExceptionTracker { get; init; }
2147
}

0 commit comments

Comments
 (0)