From c3f9d377ea79d1ccafe31e0b005dc66930df2aac Mon Sep 17 00:00:00 2001 From: wj32 Date: Fri, 19 Aug 2011 02:40:51 +0000 Subject: [PATCH] added CPU column to Threads tab git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4470 21ef857c-d57f-4fe0-8362-d861dc6d29cd --- 2.x/trunk/CHANGELOG.txt | 1 + 2.x/trunk/ProcessHacker/include/providers.h | 16 +++++- 2.x/trunk/ProcessHacker/include/uimodels.h | 12 ++-- 2.x/trunk/ProcessHacker/procprp.c | 4 +- 2.x/trunk/ProcessHacker/procprv.c | 30 ++++++---- 2.x/trunk/ProcessHacker/thrdlist.c | 47 +++++++++++++++- 2.x/trunk/ProcessHacker/thrdprv.c | 62 ++++++++++----------- 7 files changed, 117 insertions(+), 55 deletions(-) diff --git a/2.x/trunk/CHANGELOG.txt b/2.x/trunk/CHANGELOG.txt index baf3d5c85..7628affbd 100644 --- a/2.x/trunk/CHANGELOG.txt +++ b/2.x/trunk/CHANGELOG.txt @@ -3,6 +3,7 @@ Process Hacker 2.20 * NEW/IMPROVED: * Added column selection for handle list + * Added CPU column to threads list * Implemented Properties button for Thread handles * Set descending sort as the default for most numeric columns * Extended header context menu diff --git a/2.x/trunk/ProcessHacker/include/providers.h b/2.x/trunk/ProcessHacker/include/providers.h index ce34c7b08..46aab90cb 100644 --- a/2.x/trunk/ProcessHacker/include/providers.h +++ b/2.x/trunk/ProcessHacker/include/providers.h @@ -22,6 +22,8 @@ extern BOOLEAN PhEnableProcessQueryStage2; extern BOOLEAN PhEnablePurgeProcessRecords; extern BOOLEAN PhEnableCycleCpuUsage; +extern PVOID PhProcessInformation; // only can be used if running on same thread as process provider +extern ULONG PhProcessInformationSequenceNumber; extern SYSTEM_PERFORMANCE_INFORMATION PhPerfInformation; extern PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuInformation; extern SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuTotals; @@ -29,6 +31,12 @@ extern ULONG PhTotalProcesses; extern ULONG PhTotalThreads; extern ULONG PhTotalHandles; +extern ULONG64 PhCpuTotalCycleDelta; +extern PLARGE_INTEGER PhCpuIdleCycleTime; // cycle time for Idle +extern PLARGE_INTEGER PhCpuSystemCycleTime; // cycle time for DPCs and Interrupts +extern PH_UINT64_DELTA PhCpuIdleCycleDelta; +extern PH_UINT64_DELTA PhCpuSystemCycleDelta; + extern FLOAT PhCpuKernelUsage; extern FLOAT PhCpuUserUsage; extern PFLOAT PhCpusKernelUsage; @@ -36,11 +44,11 @@ extern PFLOAT PhCpusUserUsage; extern PH_UINT64_DELTA PhCpuKernelDelta; extern PH_UINT64_DELTA PhCpuUserDelta; -extern PH_UINT64_DELTA PhCpuOtherDelta; +extern PH_UINT64_DELTA PhCpuIdleDelta; extern PPH_UINT64_DELTA PhCpusKernelDelta; extern PPH_UINT64_DELTA PhCpusUserDelta; -extern PPH_UINT64_DELTA PhCpusOtherDelta; +extern PPH_UINT64_DELTA PhCpusIdleDelta; extern PH_UINT64_DELTA PhIoReadDelta; extern PH_UINT64_DELTA PhIoWriteDelta; @@ -556,6 +564,10 @@ typedef struct _PH_THREAD_ITEM LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; + FLOAT CpuUsage; + PH_UINT64_DELTA CpuKernelDelta; + PH_UINT64_DELTA CpuUserDelta; + PH_UINT32_DELTA ContextSwitchesDelta; PH_UINT64_DELTA CyclesDelta; LONG Priority; diff --git a/2.x/trunk/ProcessHacker/include/uimodels.h b/2.x/trunk/ProcessHacker/include/uimodels.h index 778d51107..aa9b1c240 100644 --- a/2.x/trunk/ProcessHacker/include/uimodels.h +++ b/2.x/trunk/ProcessHacker/include/uimodels.h @@ -550,12 +550,13 @@ VOID PhWriteNetworkList( // Columns #define PHTHTLC_TID 0 -#define PHTHTLC_CYCLESDELTA 1 -#define PHTHTLC_STARTADDRESS 2 -#define PHTHTLC_PRIORITY 3 -#define PHTHTLC_SERVICE 4 +#define PHTHTLC_CPU 1 +#define PHTHTLC_CYCLESDELTA 2 +#define PHTHTLC_STARTADDRESS 3 +#define PHTHTLC_PRIORITY 4 +#define PHTHTLC_SERVICE 5 -#define PHTHTLC_MAXIMUM 5 +#define PHTHTLC_MAXIMUM 6 typedef struct _PH_THREAD_NODE { @@ -570,6 +571,7 @@ typedef struct _PH_THREAD_NODE ULONG ValidMask; + WCHAR CpuUsageText[PH_INT32_STR_LEN_1]; PPH_STRING CyclesDeltaText; // used for Context Switches Delta as well PPH_STRING PriorityText; } PH_THREAD_NODE, *PPH_THREAD_NODE; diff --git a/2.x/trunk/ProcessHacker/procprp.c b/2.x/trunk/ProcessHacker/procprp.c index f2e85a3dd..16ba46041 100644 --- a/2.x/trunk/ProcessHacker/procprp.c +++ b/2.x/trunk/ProcessHacker/procprp.c @@ -2346,11 +2346,13 @@ INT_PTR CALLBACK PhpProcessThreadsDlgProc( threadsContext = propPageContext->Context = PhAllocate(sizeof(PH_THREADS_CONTEXT)); + // The thread provider must execute on the primary provider thread because + // it depends on data from the process provider. threadsContext->Provider = PhCreateThreadProvider( processItem->ProcessId ); PhRegisterProvider( - &PhSecondaryProviderThread, + &PhPrimaryProviderThread, PhThreadProviderUpdate, threadsContext->Provider, &threadsContext->ProviderRegistration diff --git a/2.x/trunk/ProcessHacker/procprv.c b/2.x/trunk/ProcessHacker/procprv.c index d21e24f04..dbe6b83a1 100644 --- a/2.x/trunk/ProcessHacker/procprv.c +++ b/2.x/trunk/ProcessHacker/procprv.c @@ -169,6 +169,8 @@ BOOLEAN PhEnableProcessQueryStage2 = FALSE; BOOLEAN PhEnablePurgeProcessRecords = TRUE; BOOLEAN PhEnableCycleCpuUsage = TRUE; +PVOID PhProcessInformation; // only can be used if running on same thread as process provider +ULONG PhProcessInformationSequenceNumber = 0; SYSTEM_PERFORMANCE_INFORMATION PhPerfInformation; PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuInformation; SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PhCpuTotals; @@ -179,6 +181,7 @@ ULONG PhTotalHandles; SYSTEM_PROCESS_INFORMATION PhDpcsProcessInformation; SYSTEM_PROCESS_INFORMATION PhInterruptsProcessInformation; +ULONG64 PhCpuTotalCycleDelta; // real cycle time delta for this period PLARGE_INTEGER PhCpuIdleCycleTime; // cycle time for Idle PLARGE_INTEGER PhCpuSystemCycleTime; // cycle time for DPCs and Interrupts PH_UINT64_DELTA PhCpuIdleCycleDelta; @@ -276,17 +279,14 @@ BOOLEAN PhProcessProviderInitialization() (ULONG)PhSystemBasicInformation.NumberOfProcessors ); - if (WindowsVersion >= WINDOWS_7) - { - PhCpuIdleCycleTime = PhAllocate( - sizeof(LARGE_INTEGER) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors - ); - PhCpuSystemCycleTime = PhAllocate( - sizeof(LARGE_INTEGER) * - (ULONG)PhSystemBasicInformation.NumberOfProcessors - ); - } + PhCpuIdleCycleTime = PhAllocate( + sizeof(LARGE_INTEGER) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors + ); + PhCpuSystemCycleTime = PhAllocate( + sizeof(LARGE_INTEGER) * + (ULONG)PhSystemBasicInformation.NumberOfProcessors + ); usageBuffer = PhAllocate( sizeof(FLOAT) * @@ -1968,6 +1968,8 @@ VOID PhProcessProviderUpdate( if (sysTotalCycleTime == 0) sysTotalCycleTime = -1; + PhCpuTotalCycleDelta = sysTotalCycleTime; + // Look for new processes and update existing ones. process = PH_FIRST_PROCESS(processes); @@ -2215,7 +2217,11 @@ VOID PhProcessProviderUpdate( } } - PhFree(processes); + if (PhProcessInformation) + PhFree(PhProcessInformation); + + PhProcessInformation = processes; + PhProcessInformationSequenceNumber++; if (PhpTsProcesses) { diff --git a/2.x/trunk/ProcessHacker/thrdlist.c b/2.x/trunk/ProcessHacker/thrdlist.c index cbc41fed7..f90b65165 100644 --- a/2.x/trunk/ProcessHacker/thrdlist.c +++ b/2.x/trunk/ProcessHacker/thrdlist.c @@ -90,9 +90,10 @@ VOID PhInitializeThreadList( // Default columns PhAddTreeNewColumn(hwnd, PHTHTLC_TID, TRUE, L"TID", 50, PH_ALIGN_LEFT, 0, 0); - PhAddTreeNewColumnEx(hwnd, PHTHTLC_CYCLESDELTA, TRUE, L"Cycles Delta", 80, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); - PhAddTreeNewColumn(hwnd, PHTHTLC_STARTADDRESS, TRUE, L"Start Address", 180, PH_ALIGN_LEFT, 2, 0); - PhAddTreeNewColumnEx(hwnd, PHTHTLC_PRIORITY, TRUE, L"Priority", 80, PH_ALIGN_LEFT, 3, 0, TRUE); + PhAddTreeNewColumnEx(hwnd, PHTHTLC_CPU, TRUE, L"CPU", 45, PH_ALIGN_RIGHT, 1, DT_RIGHT, TRUE); + PhAddTreeNewColumnEx(hwnd, PHTHTLC_CYCLESDELTA, TRUE, L"Cycles Delta", 80, PH_ALIGN_RIGHT, 2, DT_RIGHT, TRUE); + PhAddTreeNewColumn(hwnd, PHTHTLC_STARTADDRESS, TRUE, L"Start Address", 180, PH_ALIGN_LEFT, 3, 0); + PhAddTreeNewColumnEx(hwnd, PHTHTLC_PRIORITY, TRUE, L"Priority", 80, PH_ALIGN_LEFT, 4, 0, TRUE); PhAddTreeNewColumn(hwnd, PHTHTLC_SERVICE, FALSE, L"Service", 100, PH_ALIGN_LEFT, -1, 0); TreeNew_SetRedraw(hwnd, TRUE); @@ -385,6 +386,20 @@ BEGIN_SORT_FUNCTION(Tid) } END_SORT_FUNCTION +BEGIN_SORT_FUNCTION(Cpu) +{ + sortResult = singlecmp(threadItem1->CpuUsage, threadItem2->CpuUsage); + + if (sortResult == 0) + { + if (context->UseCycleTime) + sortResult = uint64cmp(threadItem1->CyclesDelta.Delta, threadItem2->CyclesDelta.Delta); + else + sortResult = uintcmp(threadItem1->ContextSwitchesDelta.Delta, threadItem2->ContextSwitchesDelta.Delta); + } +} +END_SORT_FUNCTION + BEGIN_SORT_FUNCTION(CyclesDelta) { if (context->UseCycleTime) @@ -436,6 +451,7 @@ BOOLEAN NTAPI PhpThreadTreeNewCallback( static PVOID sortFunctions[] = { SORT_FUNCTION(Tid), + SORT_FUNCTION(Cpu), SORT_FUNCTION(CyclesDelta), SORT_FUNCTION(StartAddress), SORT_FUNCTION(Priority), @@ -491,6 +507,31 @@ BOOLEAN NTAPI PhpThreadTreeNewCallback( case PHTHTLC_TID: PhInitializeStringRef(&getCellText->Text, threadItem->ThreadIdString); break; + case PHTHTLC_CPU: + { + FLOAT cpuUsage; + + cpuUsage = threadItem->CpuUsage * 100; + + if (cpuUsage >= 0.01) + { + PH_FORMAT format; + SIZE_T returnLength; + + PhInitFormatF(&format, cpuUsage, 2); + + if (PhFormatToBuffer(&format, 1, node->CpuUsageText, sizeof(node->CpuUsageText), &returnLength)) + { + getCellText->Text.Buffer = node->CpuUsageText; + getCellText->Text.Length = (USHORT)(returnLength - sizeof(WCHAR)); // minus null terminator + } + } + else if (cpuUsage != 0 && PhCsShowCpuBelow001) + { + PhInitializeStringRef(&getCellText->Text, L"< 0.01"); + } + } + break; case PHTHTLC_CYCLESDELTA: if (context->UseCycleTime) { diff --git a/2.x/trunk/ProcessHacker/thrdprv.c b/2.x/trunk/ProcessHacker/thrdprv.c index 341662e19..204a3d313 100644 --- a/2.x/trunk/ProcessHacker/thrdprv.c +++ b/2.x/trunk/ProcessHacker/thrdprv.c @@ -598,7 +598,6 @@ PPH_STRING PhpGetThreadBasicStartAddress( static NTSTATUS PhpGetThreadCycleTime( __in PPH_THREAD_PROVIDER ThreadProvider, - __in_opt PULARGE_INTEGER IdleThreadCycleTimes, __in PPH_THREAD_ITEM ThreadItem, __out PULONG64 CycleTime ) @@ -609,12 +608,9 @@ static NTSTATUS PhpGetThreadCycleTime( } else { - if ( - IdleThreadCycleTimes && - (ULONG)ThreadItem->ThreadId < (ULONG)PhSystemBasicInformation.NumberOfProcessors - ) + if ((ULONG)ThreadItem->ThreadId < (ULONG)PhSystemBasicInformation.NumberOfProcessors) { - *CycleTime = IdleThreadCycleTimes[(ULONG)ThreadItem->ThreadId].QuadPart; + *CycleTime = PhCpuIdleCycleTime[(ULONG)ThreadItem->ThreadId].QuadPart; return STATUS_SUCCESS; } } @@ -653,15 +649,22 @@ VOID PhThreadProviderUpdate( __in PVOID Object ) { + static ULONG lastSequenceNumber = -1; + PPH_THREAD_PROVIDER threadProvider = (PPH_THREAD_PROVIDER)Object; PVOID processes; PSYSTEM_PROCESS_INFORMATION process; PSYSTEM_THREAD_INFORMATION threads; ULONG numberOfThreads; ULONG i; - PULARGE_INTEGER idleThreadCycleTimes = NULL; - if (!NT_SUCCESS(PhEnumProcesses(&processes))) + if (PhProcessInformationSequenceNumber == lastSequenceNumber) + return; // processes have not be updated, probably because the user has unticked Update Automatically + + processes = PhProcessInformation; + lastSequenceNumber = PhProcessInformationSequenceNumber; + + if (!processes) return; process = PhFindProcessInformation(processes, threadProvider->ProcessId); @@ -688,25 +691,6 @@ VOID PhThreadProviderUpdate( { threads[i].ClientId.UniqueThread = (HANDLE)i; } - - // Get the cycle times if we're on Vista. - if (WINDOWS_HAS_CYCLE_TIME) - { - idleThreadCycleTimes = PhAllocate( - sizeof(ULARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors - ); - - if (!NT_SUCCESS(NtQuerySystemInformation( - SystemProcessorIdleCycleTimeInformation, - idleThreadCycleTimes, - sizeof(ULARGE_INTEGER) * (ULONG)PhSystemBasicInformation.NumberOfProcessors, - NULL - ))) - { - PhFree(idleThreadCycleTimes); - idleThreadCycleTimes = NULL; - } - } } // Look for dead threads. @@ -833,7 +817,6 @@ VOID PhThreadProviderUpdate( if (NT_SUCCESS(PhpGetThreadCycleTime( threadProvider, - idleThreadCycleTimes, threadItem, &cycles ))) @@ -842,6 +825,10 @@ VOID PhThreadProviderUpdate( } } + // Initialize the CPU time deltas. + PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); + PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); + // Try to get the start address. if (threadItem->ThreadHandle) @@ -998,7 +985,6 @@ VOID PhThreadProviderUpdate( if (NT_SUCCESS(PhpGetThreadCycleTime( threadProvider, - idleThreadCycleTimes, threadItem, &cycles ))) @@ -1012,6 +998,21 @@ VOID PhThreadProviderUpdate( } } + // Update the CPU time deltas. + PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart); + PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart); + + // Update the CPU usage. + if (WINDOWS_HAS_CYCLE_TIME && PhEnableCycleCpuUsage) + { + threadItem->CpuUsage = (FLOAT)threadItem->CyclesDelta.Delta / PhCpuTotalCycleDelta; + } + else + { + threadItem->CpuUsage = (FLOAT)(threadItem->CpuKernelDelta.Delta + threadItem->CpuUserDelta.Delta) / + (PhCpuKernelDelta.Delta + PhCpuUserDelta.Delta + PhCpuIdleDelta.Delta); + } + // Update the Win32 priority. { LONG oldPriorityWin32 = threadItem->PriorityWin32; @@ -1060,8 +1061,5 @@ VOID PhThreadProviderUpdate( } } - PhFree(processes); - if (idleThreadCycleTimes) PhFree(idleThreadCycleTimes); - PhInvokeCallback(&threadProvider->UpdatedEvent, NULL); }