Skip to content

[cdac] Implement GetThreadStoreData in cDAC #102404

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

Merged
merged 4 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions docs/design/datacontracts/Thread.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

This contract is for reading and iterating the threads of the process.

## Data structures defined by contract
## APIs of contract

``` csharp
record struct DacThreadStoreData (
record struct ThreadStoreData (
int ThreadCount,
TargetPointer FirstThread,
TargetPointer FinalizerThread,
TargetPointer GcThread);

record struct DacThreadStoreCounts (
record struct ThreadStoreCounts (
int UnstartedThreadCount,
int BackgroundThreadCount,
int PendingThreadCount,
Expand Down Expand Up @@ -75,7 +76,7 @@ enum ThreadState
TS_Detached = 0x80000000, // Thread was detached by DllMain
}

record struct DacThreadData (
record struct ThreadData (
uint ThreadId;
TargetNUint OsThreadId;
ThreadState State;
Expand All @@ -90,11 +91,10 @@ record struct DacThreadData (
);
```

## Apis of contract
``` csharp
DacThreadStoreData GetThreadStoreData();
DacThreadStoreCounts GetThreadCounts();
DacThreadData GetThreadData(TargetPointer threadPointer);
ThreadStoreData GetThreadStoreData();
ThreadStoreCounts GetThreadCounts();
ThreadData GetThreadData(TargetPointer threadPointer);
TargetPointer GetNestedExceptionInfo(TargetPointer nestedExceptionPointer, out TargetPointer nextNestedException);
TargetPointer GetManagedThreadObject(TargetPointer threadPointer);
```
Expand All @@ -106,34 +106,34 @@ TargetPointer GetManagedThreadObject(TargetPointer threadPointer);
``` csharp
SListReader ThreadListReader = Contracts.SList.GetReader("Thread");

DacThreadStoreData GetThreadStoreData()
ThreadStoreData GetThreadStoreData()
{
TargetPointer threadStore = Target.ReadGlobalTargetPointer("s_pThreadStore");
TargetPointer threadStore = Target.ReadGlobalPointer("s_pThreadStore");
var runtimeThreadStore = new ThreadStore(Target, threadStore);

TargetPointer firstThread = ThreadListReader.GetHead(runtimeThreadStore.SList.Pointer);

return new DacThreadStoreData(
return new ThreadStoreData(
ThreadCount : runtimeThreadStore.m_ThreadCount,
FirstThread: firstThread,
FinalizerThread: Target.ReadGlobalTargetPointer("g_pFinalizerThread"),
GcThread: Target.ReadGlobalTargetPointer("g_pSuspensionThread"));
FinalizerThread: Target.ReadGlobalPointer("g_pFinalizerThread"),
GCThread: Target.ReadGlobalPointer("g_pSuspensionThread"));
}

DacThreadStoreCounts GetThreadCounts()
{
TargetPointer threadStore = Target.ReadGlobalTargetPointer("s_pThreadStore");
TargetPointer threadStore = Target.ReadGlobalPointer("s_pThreadStore");
var runtimeThreadStore = new ThreadStore(Target, threadStore);

return new DacThreadStoreCounts(
return new ThreadStoreCounts(
ThreadCount : runtimeThreadStore.m_ThreadCount,
UnstartedThreadCount : runtimeThreadStore.m_UnstartedThreadCount,
BackgroundThreadCount : runtimeThreadStore.m_BackgroundThreadCount,
PendingThreadCount : runtimeThreadStore.m_PendingThreadCount,
DeadThreadCount: runtimeThreadStore.m_DeadThreadCount,
}

DacThreadData GetThreadData(TargetPointer threadPointer)
ThreadData GetThreadData(TargetPointer threadPointer)
{
var runtimeThread = new Thread(Target, threadPointer);

Expand All @@ -150,7 +150,7 @@ DacThreadData GetThreadData(TargetPointer threadPointer)
firstNestedException = runtimeThread.m_ExceptionState.m_currentExInfo.m_pPrevNestedInfo;
}

return new DacThread(
return new ThreadData(
ThreadId : runtimeThread.m_ThreadId,
OsThreadId : (OsThreadId)runtimeThread.m_OSThreadId,
State : (ThreadState)runtimeThread.m_State,
Expand All @@ -159,7 +159,7 @@ DacThreadData GetThreadData(TargetPointer threadPointer)
AllocContextLimit : thread.m_alloc_context.alloc_limit,
Frame : thread.m_pFrame,
TEB : thread.Has_m_pTEB ? thread.m_pTEB : TargetPointer.Null,
LastThreadObjectHandle : new DacGCHandle(thread.m_LastThrownObjectHandle),
LastThrownObjectHandle : new DacGCHandle(thread.m_LastThrownObjectHandle),
FirstNestedException : firstNestedException,
NextThread : ThreadListReader.GetHead.GetNext(threadPointer)
);
Expand Down
94 changes: 19 additions & 75 deletions docs/design/datacontracts/contract_csharp_api_design.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct TargetPointer
struct TargetNInt
{
public long Value;
// Add a full set of operators to support arithmetic as well as casting to/from TargetPointer
// Add a full set of operators to support arithmetic as well as casting to/from TargetPointer
}

struct TargetNUInt
Expand Down Expand Up @@ -72,109 +72,53 @@ struct FieldLayout
public FieldType Type;
}

interface IAlgorithmContract
{
void Init();
}

interface IContract
{
string Name { get; }
uint Version { get; }
}
class Target

sealed class Target
{
// Users of the data contract may adjust this number to force re-reading of all data
public int CurrentEpoch = 0;

sbyte ReadInt8(TargetPointer pointer);
byte ReadUInt8(TargetPointer pointer);
short ReadInt16(TargetPointer pointer);
ushort ReadUInt16(TargetPointer pointer);
int ReadInt32(TargetPointer pointer);
uint ReadUInt32(TargetPointer pointer);
long ReadInt64(TargetPointer pointer);
ulong ReadUInt64(TargetPointer pointer);
TargetPointer ReadTargetPointer(TargetPointer pointer);
TargetNInt ReadNInt(TargetPointer pointer);
TargetNUInt ReadNUint(TargetPointer pointer);
public T Read<T>(ulong address) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;
TargetPointer ReadPointer(ulong address);

byte[] ReadByteArray(TargetPointer pointer, ulong size);
void FillByteArray(TargetPointer pointer, byte[] array, ulong size);

bool TryReadInt8(TargetPointer pointer, out sbyte value);
bool TryReadUInt8(TargetPointer pointer, out byte value);
bool TryReadInt16(TargetPointer pointer, out short value);
bool TryReadUInt16(TargetPointer pointer, out ushort value);
bool TryReadInt32(TargetPointer pointer, out int value);
bool TryReadUInt32(TargetPointer pointer, out uint value);
bool TryReadInt64(TargetPointer pointer, out long value);
bool TryReadUInt64(TargetPointer pointer, out ulong value);
bool TryReadTargetPointer(TargetPointer pointer, out TargetPointer value);
bool TryReadNInt(TargetPointer pointer, out TargetNInt value);
bool TryReadNUInt(TargetPointer pointer, out TargetNUInt value);
bool TryReadByteArray(TargetPointer pointer, ulong size, out byte[] value);
bool TryFillByteArray(TargetPointer pointer, byte[] array, ulong size);

// If pointer is 0, then the return value will be 0
TargetPointer GetTargetPointerForField(TargetPointer pointer, FieldLayout fieldLayout);

sbyte ReadGlobalInt8(string globalName);
byte ReadGlobalUInt8(string globalName);
short ReadGlobalInt16(string globalName);
ushort ReadGlobalUInt16(string globalName);
int ReadGlobalInt32(string globalName);
uint ReadGlobalUInt32(string globalName);
long ReadGlobalInt64(string globalName);
ulong ReadGlobalUInt64(string globalName);
TargetPointer ReadGlobalTargetPointer(string globalName);

bool TryReadGlobalInt8(string globalName, out sbyte value);
bool TryReadGlobalUInt8(string globalName, out byte value);
bool TryReadGlobalInt16(string globalName, out short value);
bool TryReadGlobalUInt16(string globalName, out ushort value);
bool TryReadGlobalInt32(string globalName, out int value);
bool TryReadGlobalUInt32(string globalName, out uint value);
bool TryReadGlobalInt64(string globalName, out long value);
bool TryReadGlobalUInt64(string globalName, out ulong value);
bool TryReadGlobalTargetPointer(string globalName, out TargetPointer value);

Contracts Contract { get; }

partial class Contracts
{
FieldLayout GetFieldLayout(string typeName, string fieldName);
bool TryGetFieldLayout(string typeName, string fieldName, out FieldLayout layout);
int GetTypeSize(string typeName);
bool TryGetTypeSize(string typeName, out int size);
T ReadGlobal<T>(string globalName) where T : unmanaged, IBinaryInteger<T>, IMinMaxValue<T>;
TargetPointer ReadGlobalPointer(string globalName);

object GetContract(string contractName);
bool TryGetContract(string contractName, out object contract);
Contracts.Registry Contracts { get; }
}

// Types defined by contracts live here
namespace Contracts
{
class Registry
{
// Every contract that is defined has a field here. As an example this document defines a MethodTableContract
// If the contract is not supported by the runtime in use, then the implementation of the contract will be the base type which
// is defined to throw if it is ever used.

// List of contracts will be inserted here by source generator
MethodTableContract MethodTableContract;
}
}

// Types defined by contracts live here
namespace ContractDefinitions
{
class CompositeContract
{
List<Tuple<string, uint>> Subcontracts;
}

class DataStructureContract
{
string MethodTableName {get;}
List<Tuple<string, FieldLayout>> FieldData;
}

// Insert Algorithmic Contract definitions here
class MethodTableContract
// Insert contract definitions here
interface MethodTableContract : IContract
{
public virtual int DynamicTypeID(TargetPointer methodTablePointer) { throw new NotImplementedException(); }
public virtual int BaseSize(TargetPointer methodTablePointer) { throw new NotImplementedException(); }
Expand Down Expand Up @@ -207,7 +151,7 @@ public class FeatureFlags_2
}

[DataContractAlgorithm(1)]
class MethodTableContract_1 : ContractDefinitions.MethodTableContract, IAlgorithmContract
readonly struct MethodTableContract_1 : Contracts.MethodTableContract
{
DataContracts.Target Target;
readonly uint ContractVersion;
Expand All @@ -219,7 +163,7 @@ class MethodTableContract_1 : ContractDefinitions.MethodTableContract, IAlgorith

// This is used for version 2 and 3 of the contract, where the dynamic type id is no longer present, and baseSize has a new limitation in that it can only be a value up to 0x1FFFFFFF in v3
[DataContractAlgorithm(2, 3)]
class MethodTableContract_2 : ContractDefinitions.MethodTableContract, IAlgorithmContract
readonly struct MethodTableContract_2 : Contracts.MethodTableContract
{
DataContracts.Target Target;
readonly uint ContractVersion;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/debug/daccess/dacimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,7 @@ class ClrDataAccess

HRESULT Initialize(void);

HRESULT GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData);
HRESULT GetThreadStoreDataImpl(struct DacpThreadStoreData *data);

BOOL IsExceptionFromManagedCode(EXCEPTION_RECORD * pExceptionRecord);
Expand Down
48 changes: 44 additions & 4 deletions src/coreclr/debug/daccess/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,11 +761,52 @@ ClrDataAccess::GetHeapAllocData(unsigned int count, struct DacpGenerationAllocDa
return hr;
}

HRESULT
ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData)
HRESULT ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData* threadData)
{
SOSDacEnter();

if (m_cdacSos != NULL)
{
hr = m_cdacSos->GetThreadData(threadAddr, threadData);
if (FAILED(hr))
{
hr = GetThreadDataImpl(threadAddr, threadData);
}
#ifdef _DEBUG
else
{
DacpThreadData threadDataLocal;
HRESULT hrLocal = GetThreadDataImpl(threadAddr, &threadDataLocal);
_ASSERTE(hr == hrLocal);
_ASSERTE(threadData->corThreadId == threadDataLocal.corThreadId);
_ASSERTE(threadData->osThreadId == threadDataLocal.osThreadId);
_ASSERTE(threadData->state == threadDataLocal.state);
_ASSERTE(threadData->preemptiveGCDisabled == threadDataLocal.preemptiveGCDisabled);
_ASSERTE(threadData->allocContextPtr == threadDataLocal.allocContextPtr);
_ASSERTE(threadData->allocContextLimit == threadDataLocal.allocContextLimit);
_ASSERTE(threadData->context == threadDataLocal.context);
_ASSERTE(threadData->domain == threadDataLocal.domain);
_ASSERTE(threadData->pFrame == threadDataLocal.pFrame);
_ASSERTE(threadData->lockCount == threadDataLocal.lockCount);
_ASSERTE(threadData->firstNestedException == threadDataLocal.firstNestedException);
_ASSERTE(threadData->teb == threadDataLocal.teb);
_ASSERTE(threadData->fiberData == threadDataLocal.fiberData);
_ASSERTE(threadData->lastThrownObjectHandle == threadDataLocal.lastThrownObjectHandle);
_ASSERTE(threadData->nextThread == threadDataLocal.nextThread);;
}
#endif
}
else
{
hr = GetThreadDataImpl(threadAddr, threadData);
}

SOSDacLeave();
return hr;
}

HRESULT ClrDataAccess::GetThreadDataImpl(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *threadData)
{
// marshal the Thread object from the target
Thread* thread = PTR_Thread(TO_TADDR(threadAddr));

Expand Down Expand Up @@ -804,8 +845,7 @@ ClrDataAccess::GetThreadData(CLRDATA_ADDRESS threadAddr, struct DacpThreadData *
thread->m_ExceptionState.m_currentExInfo.m_pPrevNestedInfo);
#endif // FEATURE_EH_FUNCLETS

SOSDacLeave();
return hr;
return S_OK;
}

#ifdef FEATURE_REJIT
Expand Down
13 changes: 11 additions & 2 deletions src/coreclr/debug/runtimeinfo/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,21 @@ CDAC_TYPES_BEGIN()

CDAC_TYPE_BEGIN(Thread)
CDAC_TYPE_INDETERMINATE(Thread)
CDAC_TYPE_FIELD(Thread, /*uint32*/, Id, cdac_offsets<Thread>::Id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this comment type. Is that intentional going forward or a temporary mechanism?

Copy link
Member Author

@elinor-fung elinor-fung May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type is optional as a mechanism for making the descriptor more compact. I left it off for these (and just put the comment), since I wasn't checking them.

CDAC_TYPE_FIELD(Thread, /*nuint*/, OSId, cdac_offsets<Thread>::OSId)
CDAC_TYPE_FIELD(Thread, GCHandle, GCHandle, cdac_offsets<Thread>::ExposedObject)
CDAC_TYPE_FIELD(Thread, GCHandle, LastThrownObject, cdac_offsets<Thread>::LastThrownObject)
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_FIELD(ThreadStore, /*SLink*/, FirstThreadLink, cdac_offsets<ThreadStore>::FirstThreadLink)
CDAC_TYPE_FIELD(ThreadStore, /*int32*/, ThreadCount, cdac_offsets<ThreadStore>::ThreadCount)
CDAC_TYPE_FIELD(ThreadStore, /*int32*/, UnstartedCount, cdac_offsets<ThreadStore>::UnstartedCount)
CDAC_TYPE_FIELD(ThreadStore, /*int32*/, BackgroundCount, cdac_offsets<ThreadStore>::BackgroundCount)
CDAC_TYPE_FIELD(ThreadStore, /*int32*/, PendingCount, cdac_offsets<ThreadStore>::PendingCount)
CDAC_TYPE_FIELD(ThreadStore, /*int32*/, DeadCount, cdac_offsets<ThreadStore>::DeadCount)
CDAC_TYPE_END(ThreadStore)

CDAC_TYPE_BEGIN(GCHandle)
Expand All @@ -123,6 +130,8 @@ CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()
CDAC_GLOBAL_POINTER(ThreadStore, &ThreadStore::s_pThreadStore)
CDAC_GLOBAL_POINTER(FinalizerThread, &::g_pFinalizerThread)
CDAC_GLOBAL_POINTER(GCThread, &::g_pSuspensionThread)
#if FEATURE_EH_FUNCLETS
CDAC_GLOBAL(FeatureEHFunclets, uint8, 1)
#else
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/inc/slist.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#ifndef _H_SLIST_
#define _H_SLIST_

#include "cdacoffsets.h"

//------------------------------------------------------------------
// struct SLink, to use a singly linked list
// have a data member m_Link of type SLink in your class
Expand Down Expand Up @@ -118,6 +120,8 @@ class SList
PTR_SLink m_pHead;
PTR_SLink m_pTail;

template<typename U> friend struct ::cdac_offsets;

// get the list node within the object
static SLink* GetLink (T* pLink)
{
Expand Down
Loading
Loading