SOS Internals – threads Command

Here I want to explain how the !threads command in SOS.DLL is implemented. This is done by performing the actual debugging the .NET Framework v2.0’s sos.dll and try to gather more information by gleaning its SSCLI v2.0’s source code, especially inside strike.cpp file.

This command is a very familiar one for all of you who performs the development and debugging of .NET Framework application.

This is the typical output for .NET Framework v2.0 when the !threads command is executed (this is after the mscorjit.dll is loaded) :

ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ State GC Context Domain Count APT Exception
0 1 e50 00182f78 a020 Enabled 015f2c1c:015f3fe8 0014dcf8 2 MTA
2 2 26c 00184730 b220 Enabled 00000000:00000000 0014dcf8 0 MTA (Finalizer)

Internally, the thread header and each detail records is implemented using sos!PrintThreadsFromThreadStore. For the header or summary data (the ThreadCount, UnstartedThread, etc) is done via the data request to the ThreadStore using its request method :

Status = ThreadStore.Request(g_clrData)

As for the ThreadStore is the instance of DacpThreadStoreData structure inside dacprivate.h header file. This is actually the structure to store all internal data access that is related to the CLR’s thread summary information.

The above statement requires that the ThreadStore structure is already created and also depends on the successful interface request to the CLR’s internal data structure. If any of this condition is not fulfilled then the !threads command will exits after reporting this message :

“Failed to request ThreadStore”

The function prototype for the Request function is located within the DacpThreadStoreData structure as follows :

HRESULT Request(IXCLRDataProcess* dac)

This function, in turn just forward the call to the passed parameter and returned the structure to the caller :

return dac->Request(DACPRIV_REQUEST_THREAD_STORE_DATA,0,NULL,sizeof(*this),(PBYTE)this)

So, you see that at the time of !threads invoke, the sos.dll module already initialize the g_clrData so that the ThreadStore can be initialized using the method call to instances of IXCLRDataProcess’s dac.

Where the g_clrData is initialized ? Well, at the beginning of the !threads function, there is a call using define declaration INIT_API() at exts.h.

Inside this declaration there is call to LoadClrDebugDll, and it is inside this function that the g_clrData is initialized :

g_clrData = (IXCLRDataProcess*)Query.Iface;

As for the Query record is declared as :

WDBGEXTS_CLR_DATA_INTERFACE Query;

The definition of WDBGEXTS_CLR_DATA_INTERFACE is actually a part of WinDBG’s debugger extension API infrastructure inside wdbgexts.h :

typedef struct _WDBGEXTS_CLR_DATA_INTERFACE {
// Interface requested.
const IID* Iid;
// Interface pointer return.
PVOID Iface;
} WDBGEXTS_CLR_DATA_INTERFACE, *PWDBGEXTS_CLR_DATA_INTERFACE;

Meanwhile, the actual access to IXCLRDataProcess interface is through documented Ioctl in the debugger extension API, that has the function prototype as follows :

ULONG Ioctl (USHORT IoctlType, PVOID lpvData, ULONG cbSizeOfContext)

This is a multipurpose function to access any structures outside the WinDBG, provided that the type for IoctlType is declared in official documentation. To access the CLR data interface the sos module use this kind of call to the Ioctl :

Ioctl(IG_GET_CLR_DATA_INTERFACE, &Query, sizeof(Query))

So the IoctlType here is IG_GET_CLR_DATA_INTERFACE. And the requested interface is initialized just before the call to this extension API :

Query.Iid = &__uuidof(IXCLRDataProcess)

You can see that it’s similar to QueryInterface method, but is implemented in the WinDBG’s way, through sos.dll to access the interface to any of the CLR’s internal data structure.

There is a very minimal documentation regarding IXCLRDataProcess, from the comment inside the xclrdata.idl, the “IX” means that the interface is still work inprogress, and it will be transformed to “I” at the time of release, but searching through the MSDN for existence of ICLRDataProcess founds nothing, only by peering to the comment at the beginning of idl declaration we can find some short info about the nature of this interface :

“Interface representing the process itself. Can be obtained via * CLRDataCreateInstance.”

Although undocumented, the Request method of IXCLRDataProcess has this prototype :

HRESULT Request([in] ULONG32 reqCode,[in] ULONG32 inBufferSize,[in, size_is(inBufferSize)] BYTE* inBuffer,[in] ULONG32 outBufferSize,[out, size_is(outBufferSize)] BYTE* outBuffer)

At the above call, the sos is interested only to the output, so this explain the zero and null value for inBuffer record, and passed the size and pointer of DacpThreadStoreData.

After getting through ClrDataAccess::Request and ClrDataAccess::RequestThreadStoreData, the implementation of Request method eventually arrived at final data access :

ThreadStore* threadStore = ThreadStore::s_pThreadStore
threadStoreData->threadCount = threadStore->m_ThreadCount

So, basically the access to thread summary data is distilled to passing of the ThreadStore internal CLR structure fields to threadStoreData record.

After the ThreadStore information, we arrived at the detail of each thread information, and it is requested via DacpThreadData, and in turns, requesting the data via its method :

DacpThreadData Thread;
Status = Thread.Request(g_clrData, CurThread, !bMiniDump);

At first, the CurThread value contains ThreadStore.firstThread, for the next thread it is accessed via its Thread.nextThread.

Let’s see the function implementation of the above Request method :

HRESULT Request(IXCLRDataProcess* dac, CLRDATA_ADDRESS addr, BOOL isFullDump=TRUE)
{
DacpThreadArgs dta = { addr, isFullDump };
return dac->Request(DACPRIV_REQUEST_THREAD_DATA, sizeof(DacpThreadArgs), (PBYTE)&dta, sizeof(*this), (PBYTE)this);
}

The detail of each thread is passed as an argument called addr, and this is first wrapped up to DacpThreadArgs called dta before passed it again to Request method of through IXCLRDataProcess interface, together with the dta as its inBuffer arguments.

This time, after getting through ClrDataAccess::Request and ClrDataAccess::RequestThreadData, the implementation of Request method eventually arrived at final thread detail data access :

DacpThreadArgs *args = (DacpThreadArgs *) inBuffer;
DacpThreadData* threadData = (DacpThreadData*)outBuffer;
Thread* thread = PTR_Thread(TO_TADDR(args->addrThread));

ZeroMemory (threadData, sizeof(DacpThreadData));
threadData->corThreadId = thread->m_ThreadId;
threadData->osThreadId = thread->m_OSThreadId;

First, there is the conversion from the passed thread address to Thread structure, before passing each field to the threadData as the outBuffer. This is done via PTR_Thread function, and through the macro transformation, it will eventually arrived at DacInstantiateClassByVTable(TADDR addr, bool throwEx) function prototype.

Basically, the DacInstantiateClassByVTable will perform searching for class instances based on given address to return the proper class instance to the caller, and in this case, the class instance is of Thread type.

The loops inside the threads API call ends with the status return to the WinDBG’s console.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: