/*++ Copyright (c) 1996 Microsoft Corporation Module Name: perfthrd.c Abstract: This file implements an Performance Object that presents Thread performance object data Created: Bob Watson 22-Oct-1996 Revision History --*/ // // Include Files // #include #include #include #include #include #include #include #include #include "perfsprc.h" #include "perfmsg.h" #include "datathrd.h" extern DWORD PerfSprc_dwThreadNameFormat; DWORD APIENTRY CollectThreadObjectData ( IN OUT LPVOID *lppData, IN OUT LPDWORD lpcbTotalBytes, IN OUT LPDWORD lpNumObjectTypes ) /*++ Routine Description: This routine will return the data for the processor object Arguments: IN OUT LPVOID *lppData IN: pointer to the address of the buffer to receive the completed PerfDataBlock and subordinate structures. This routine will append its data to the buffer starting at the point referenced by *lppData. OUT: points to the first byte after the data structure added by this routine. This routine updated the value at lppdata after appending its data. IN OUT LPDWORD lpcbTotalBytes IN: the address of the DWORD that tells the size in bytes of the buffer referenced by the lppData argument OUT: the number of bytes added by this routine is writted to the DWORD pointed to by this argument IN OUT LPDWORD NumObjectTypes IN: the address of the DWORD to receive the number of objects added by this routine OUT: the number of objects added by this routine is writted to the DWORD pointed to by this argument Returns: 0 if successful, else Win 32 error code of failure --*/ { LONG lReturn = ERROR_SUCCESS; DWORD TotalLen; // Length of the total return block THREAD_DATA_DEFINITION *pThreadDataDefinition; PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition; PTHREAD_COUNTER_DATA pTCD; THREAD_COUNTER_DATA tcdTotal; PSYSTEM_PROCESS_INFORMATION ProcessInfo; PSYSTEM_THREAD_INFORMATION ThreadInfo; ULONG ProcessNumber; ULONG NumThreadInstances; ULONG ThreadNumber; ULONG ProcessBufferOffset; BOOLEAN NullProcess; BOOL bMoreThreads; // total thread accumulator variables UNICODE_STRING ThreadName; WCHAR ThreadNameBuffer[MAX_THREAD_NAME_LENGTH+1]; pThreadDataDefinition = (THREAD_DATA_DEFINITION *) *lppData; // // Check for sufficient space for Thread object type definition // TotalLen = sizeof(THREAD_DATA_DEFINITION) + sizeof(PERF_INSTANCE_DEFINITION) + sizeof(THREAD_COUNTER_DATA); if ( *lpcbTotalBytes < TotalLen ) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_MORE_DATA; } // // Define Thread data block // ThreadName.Length = ThreadName.MaximumLength = (MAX_THREAD_NAME_LENGTH + 1) * sizeof(WCHAR); ThreadName.Buffer = ThreadNameBuffer; memcpy(pThreadDataDefinition, &ThreadDataDefinition, sizeof(THREAD_DATA_DEFINITION)); pThreadDataDefinition->ThreadObjectType.PerfTime = PerfTime; ProcessBufferOffset = 0; // Now collect data for each Thread ProcessNumber = 0; NumThreadInstances = 0; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer; pPerfInstanceDefinition = (PPERF_INSTANCE_DEFINITION)&pThreadDataDefinition[1]; TotalLen = sizeof(THREAD_DATA_DEFINITION); // clear total accumulator memset (&tcdTotal, 0, sizeof (tcdTotal)); bMoreThreads = FALSE; if (ProcessInfo) { if (ProcessInfo->NextEntryOffset != 0) { bMoreThreads = TRUE; } } while ( bMoreThreads && (ProcessInfo != NULL)) { if ( ProcessInfo->ImageName.Buffer != NULL || ProcessInfo->NumberOfThreads > 0 ) { NullProcess = FALSE; } else { NullProcess = TRUE; } ThreadNumber = 0; // Thread number of this process ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1); while ( !NullProcess && ThreadNumber < ProcessInfo->NumberOfThreads ) { TotalLen += sizeof(PERF_INSTANCE_DEFINITION) + (MAX_THREAD_NAME_LENGTH+1+sizeof(DWORD))* sizeof(WCHAR) + sizeof (THREAD_COUNTER_DATA); if ( *lpcbTotalBytes < TotalLen ) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_MORE_DATA; } if (PerfSprc_dwThreadNameFormat == NAME_FORMAT_ID) { PerfIntegerToWString( HandleToUlong(ThreadInfo->ClientId.UniqueThread), 10, MAX_THREAD_NAME_LENGTH+1, ThreadNameBuffer); } else { // The only name we've got is the thread number RtlIntegerToUnicodeString(ThreadNumber, 10, &ThreadName); } MonBuildInstanceDefinition(pPerfInstanceDefinition, (PVOID *) &pTCD, PROCESS_OBJECT_TITLE_INDEX, ProcessNumber, (DWORD)-1, ThreadName.Buffer); // test structure for Quadword Alignment assert (((DWORD)(pTCD) & 0x00000007) == 0); // // // Format and collect Thread data // pTCD->CounterBlock.ByteLength = sizeof(THREAD_COUNTER_DATA); // // Convert User time from 100 nsec units to counter // frequency. // tcdTotal.ProcessorTime += pTCD->ProcessorTime = ThreadInfo->KernelTime.QuadPart + ThreadInfo->UserTime.QuadPart; tcdTotal.UserTime += pTCD->UserTime = ThreadInfo->UserTime.QuadPart; tcdTotal.KernelTime += pTCD->KernelTime = ThreadInfo->KernelTime.QuadPart; tcdTotal.ContextSwitches += pTCD->ContextSwitches = ThreadInfo->ContextSwitches; pTCD->ThreadElapsedTime = ThreadInfo->CreateTime.QuadPart; pTCD->ThreadPriority = (ThreadInfo->ClientId.UniqueProcess == 0) ? 0 : ThreadInfo->Priority; pTCD->ThreadBasePriority = ThreadInfo->BasePriority; pTCD->ThreadStartAddr = ThreadInfo->StartAddress; pTCD->ThreadState = (DWORD)((ThreadInfo->ThreadState > 7) ? 7 : ThreadInfo->ThreadState); pTCD->WaitReason = (DWORD)ThreadInfo->WaitReason; // now stuff in the process and thread id's pTCD->ProcessId = HandleToUlong(ThreadInfo->ClientId.UniqueProcess); pTCD->ThreadId = HandleToUlong(ThreadInfo->ClientId.UniqueThread); pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pTCD[1]; NumThreadInstances++; ThreadNumber++; ThreadInfo++; } if ( !NullProcess ) { ProcessNumber++; } if (ProcessInfo->NextEntryOffset == 0) { bMoreThreads = FALSE; continue; } ProcessBufferOffset += ProcessInfo->NextEntryOffset; ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &pProcessBuffer[ProcessBufferOffset]; } if (NumThreadInstances > 0) { // See if the total instance will fit TotalLen += sizeof(PERF_INSTANCE_DEFINITION) + (MAX_THREAD_NAME_LENGTH+1+sizeof(DWORD))* sizeof(WCHAR) + sizeof (THREAD_COUNTER_DATA); if ( *lpcbTotalBytes < TotalLen ) { *lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; return ERROR_MORE_DATA; } // set the Total Elapsed Time to be the current time so that it will // show up as 0 when displayed. tcdTotal.ThreadElapsedTime = pThreadDataDefinition->ThreadObjectType.PerfTime.QuadPart; // use the "total" for this instance MonBuildInstanceDefinition(pPerfInstanceDefinition, (PVOID *) &pTCD, PROCESS_OBJECT_TITLE_INDEX, ProcessNumber, (DWORD)-1, wszTotal); // test structure for Quadword Alignment assert (((DWORD)(pTCD) & 0x00000007) == 0); // // // Format and collect Thread data // memcpy (pTCD, &tcdTotal, sizeof(tcdTotal)); pTCD->CounterBlock.ByteLength = sizeof(THREAD_COUNTER_DATA); pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pTCD[1]; NumThreadInstances++; } // Note number of Thread instances pThreadDataDefinition->ThreadObjectType.NumInstances = NumThreadInstances; // // Now we know how large an area we used for the // Thread definition, so we can update the offset // to the next object definition // *lpcbTotalBytes = pThreadDataDefinition->ThreadObjectType.TotalByteLength = (DWORD)((PCHAR) pPerfInstanceDefinition - (PCHAR) pThreadDataDefinition); #if DBG if (*lpcbTotalBytes > TotalLen ) { DbgPrint ("\nPERFPROC: Thread Perf Ctr. Instance Size Underestimated:"); DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes); } #endif *lppData = (LPVOID)pPerfInstanceDefinition; *lpNumObjectTypes = 1; return lReturn; }