diff --git a/docs/design/datacontracts/GCHandle.md b/docs/design/datacontracts/GCHandle.md new file mode 100644 index 0000000000000..e2a69e227a00c --- /dev/null +++ b/docs/design/datacontracts/GCHandle.md @@ -0,0 +1,23 @@ +# Contract GCHandle + +This contract allows decoding and reading of GCHandles. This will also include handle enumeration in the future + +## Data structures defined by contract +``` csharp +``` + +## Apis of contract +``` csharp +TargetPointer GetObject(TargetPointer gcHandle); +``` + +## Version 1 + +``` csharp +TargetPointer GetObject(TargetPointer gcHandle) +{ + if (gcHandle == TargetPointer.Null) + return TargetPointer.Null; + return Target.ReadTargetPointer(gcHandle); +} +``` diff --git a/docs/design/datacontracts/SList.md b/docs/design/datacontracts/SList.md new file mode 100644 index 0000000000000..ee1e9c66e06be --- /dev/null +++ b/docs/design/datacontracts/SList.md @@ -0,0 +1,78 @@ +# Contract SList + +This contract allows reading and iterating over an SList data structure. + +## Data structures defined by contract +``` csharp +class SListReader +{ + public abstract TargetPointer GetHead(TargetPointer slistPointer); + public abstract TargetPointer GetNext(TargetPointer entryInSList); + public IEnumerator EnumerateList(TargetPointer slistPointer) + { + TargetPointer current = GetHead(slistPointer); + + while (current != TargetPointer.Null) + { + yield return current; + current = GetNext(current); + } + } + public IEnumerator EnumerateListFromEntry(TargetPointer entryInSList) + { + TargetPointer current = entryInSList; + + while (current != TargetPointer.Null) + { + yield return current; + current = GetNext(current); + } + } +} +``` + +## Apis of contract +``` csharp +SListReader GetReader(string typeOfDataStructure); +``` + +## Version 1 + +``` csharp +private class SListReaderV1 : SListReader +{ + uint _offsetToSLinkField; + Target Target; + + SListReaderV1(Target target, string typeToEnumerate) + { + Target = target; + _offsetToSLinkField = Target.Contracts.GetFieldLayout(typeToEnumerate, "m_Link").Offset; + } + public override TargetPointer GetHead(TargetPointer slistPointer) + { + TargetPointer headPointer = new SListBase(Target, slistPointer).m_pHead; + TargetPointer slinkInHeadObject = new SLink(Target, headPointer).m_pNext; + if (slinkInHeadObject == TargetPointer.Null) + return TargetPointer.Null; + return slinkInHeadObject - _offsetToSLinkField; + } + + public override TargetPointer GetNext(TargetPointer entryInSList) + { + if (entryInSList == TargetPointer.Null) + throw new ArgumentException(); + + TargetPointer slinkPointer = entryInSList + _offsetToSLinkField; + TargetPointer slinkInObject = new SLink(Target, slinkPointer).m_pNext; + if (slinkInObject == TargetPointer.Null) + return TargetPointer.Null; + return slinkInHeadObject - _offsetToSLinkField; + } +} + +SListReader GetReader(string typeOfDataStructure) +{ + return new SListReaderV1(typeOfDataStructure); +} +``` diff --git a/docs/design/datacontracts/Thread.md b/docs/design/datacontracts/Thread.md new file mode 100644 index 0000000000000..22b5781bb8077 --- /dev/null +++ b/docs/design/datacontracts/Thread.md @@ -0,0 +1,174 @@ +# Contract Thread + +This contract is for reading and iterating the threads of the process. + +## Data structures defined by contract +``` csharp +record struct DacThreadStoreData ( + int ThreadCount, + int UnstartedThreadCount, + int BackgroundThreadCount, + int PendingThreadCount, + int DeadThreadCount, + TargetPointer FirstThread, + TargetPointer FinalizerThread, + TargetPointer GcThread); + +enum ThreadState +{ + TS_Unknown = 0x00000000, // threads are initialized this way + + TS_AbortRequested = 0x00000001, // Abort the thread + + TS_GCSuspendPending = 0x00000002, // ThreadSuspend::SuspendRuntime watches this thread to leave coop mode. + TS_GCSuspendRedirected = 0x00000004, // ThreadSuspend::SuspendRuntime has redirected the thread to suspention routine. + TS_GCSuspendFlags = TS_GCSuspendPending | TS_GCSuspendRedirected, // used to track suspension progress. Only SuspendRuntime writes/resets these. + + TS_DebugSuspendPending = 0x00000008, // Is the debugger suspending threads? + TS_GCOnTransitions = 0x00000010, // Force a GC on stub transitions (GCStress only) + + TS_LegalToJoin = 0x00000020, // Is it now legal to attempt a Join() + + TS_ExecutingOnAltStack = 0x00000040, // Runtime is executing on an alternate stack located anywhere in the memory + + TS_Hijacked = 0x00000080, // Return address has been hijacked + + // unused = 0x00000100, + TS_Background = 0x00000200, // Thread is a background thread + TS_Unstarted = 0x00000400, // Thread has never been started + TS_Dead = 0x00000800, // Thread is dead + + TS_WeOwn = 0x00001000, // Exposed object initiated this thread + TS_CoInitialized = 0x00002000, // CoInitialize has been called for this thread + + TS_InSTA = 0x00004000, // Thread hosts an STA + TS_InMTA = 0x00008000, // Thread is part of the MTA + + // Some bits that only have meaning for reporting the state to clients. + TS_ReportDead = 0x00010000, // in WaitForOtherThreads() + TS_FullyInitialized = 0x00020000, // Thread is fully initialized and we are ready to broadcast its existence to external clients + + TS_TaskReset = 0x00040000, // The task is reset + + TS_SyncSuspended = 0x00080000, // Suspended via WaitSuspendEvent + TS_DebugWillSync = 0x00100000, // Debugger will wait for this thread to sync + + TS_StackCrawlNeeded = 0x00200000, // A stackcrawl is needed on this thread, such as for thread abort + // See comment for s_pWaitForStackCrawlEvent for reason. + + // unused = 0x00400000, + + // unused = 0x00800000, + TS_TPWorkerThread = 0x01000000, // is this a threadpool worker thread? + + TS_Interruptible = 0x02000000, // sitting in a Sleep(), Wait(), Join() + TS_Interrupted = 0x04000000, // was awakened by an interrupt APC. !!! This can be moved to TSNC + + TS_CompletionPortThread = 0x08000000, // Completion port thread + + TS_AbortInitiated = 0x10000000, // set when abort is begun + + TS_Finalized = 0x20000000, // The associated managed Thread object has been finalized. + // We can clean up the unmanaged part now. + + TS_FailStarted = 0x40000000, // The thread fails during startup. + TS_Detached = 0x80000000, // Thread was detached by DllMain +} + +record struct DacThreadData ( + uint ThreadId; + uint OsThreadId; + ThreadState State; + bool PreemptiveGCDisabled + TargetPointer AllocContextPointer; + TargetPointer AllocContextLimit; + TargetPointer Frame; + TargetPointer FirstNestedException; + TargetPointer TEB; + TargetPointer LastThrownObjectHandle; + TargetPointer NextThread; +); +``` + +## Apis of contract +``` csharp +DacThreadStoreData GetThreadStoreData(); +``` + +## Version 1 + + + +``` csharp +SListReader ThreadListReader = Contracts.SList.GetReader("Thread"); + +DacThreadStoreData GetThreadStoreData() +{ + TargetPointer threadStore = Target.ReadGlobalTargetPointer("s_pThreadStore"); + var runtimeThreadStore = new ThreadStore(Target, threadStore); + + TargetPointer firstThread = ThreadListReader.GetHead(runtimeThreadStore.SList.Pointer); + + return new DacThreadStoreData( + ThreadCount : runtimeThreadStore.m_ThreadCount, + UnstartedThreadCount : runtimeThreadStore.m_UnstartedThreadCount, + BackgroundThreadCount : runtimeThreadStore.m_BackgroundThreadCount, + PendingThreadCount : runtimeThreadStore.m_PendingThreadCount, + DeadThreadCount: runtimeThreadStore.m_DeadThreadCount, + FirstThread: firstThread, + FinalizerThread: Target.ReadGlobalTargetPointer("g_pFinalizerThread"), + GcThread: Target.ReadGlobalTargetPointer("g_pSuspensionThread")); +} + +DacThreadData GetThreadData(TargetPointer threadPointer) +{ + var runtimeThread = new Thread(Target, threadPointer); + + TargetPointer firstNestedException = TargetPointer.Null; + if (Target.ReadGlobalInt32("FEATURE_EH_FUNCLETS")) + { + if (runtimeThread.m_ExceptionState.m_pCurrentTracker != TargetPointer.Null) + { + firstNestedException = new ExceptionTrackerBase(Target, runtimeThread.m_ExceptionState.m_pCurrentTracker).m_pPrevNestedInfo; + } + } + else + { + firstNestedException = runtimeThread.m_ExceptionState.m_currentExInfo.m_pPrevNestedInfo; + } + + return new DacThread( + ThreadId : runtimeThread.m_ThreadId, + OsThreadId : runtimeThread.m_OSThreadId, + State : (ThreadState)runtimeThread.m_State, + PreemptiveGCDisabled : thread.m_fPreemptiveGCDisabled != 0, + AllocContextPointer : thread.m_alloc_context.alloc_ptr, + AllocContextLimit : thread.m_alloc_context.alloc_limit, + Frame : thread.m_pFrame, + TEB : thread.Has_m_pTEB ? thread.m_pTEB : TargetPointer.Null, + LastThreadObjectHandle : thread.m_LastThrownObjectHandle, + FirstNestedException : firstNestedException, + NextThread : ThreadListReader.GetHead.GetNext(threadPointer) + ); +} + +TargetPointer GetNestedExceptionInfo(TargetPointer nestedExceptionPointer, out TargetPointer nextNestedException) +{ + if (nestedExceptionPointer == TargetPointer.Null) + { + throw new InvalidArgumentException(); + } + if (Target.ReadGlobalInt32("FEATURE_EH_FUNCLETS")) + { + var exData = new ExceptionTrackerBase(Target, nestedExceptionPointer); + nextNestedException = exData.m_pPrevNestedInfo; + return Contracts.GCHandle.GetObject(exData.m_hThrowable); + } + else + { + var exData = new ExInfo(Target, nestedExceptionPointer); + nextNestedException = exData.m_pPrevNestedInfo; + return Contracts.GCHandle.GetObject(exData.m_hThrowable); + } +} +``` diff --git a/docs/design/datacontracts/contract_csharp_api_design.cs b/docs/design/datacontracts/contract_csharp_api_design.cs index e5df4e4b8b84b..062c04806003c 100644 --- a/docs/design/datacontracts/contract_csharp_api_design.cs +++ b/docs/design/datacontracts/contract_csharp_api_design.cs @@ -33,6 +33,7 @@ class DataContractAlgorithmAttribute : System.Attribute struct TargetPointer { public ulong Value; + public static TargetPointer Null = new TargetPointer(0); // Add a full set of operators to support pointer arithmetic }