Process Hacker
thrdprv.c
Go to the documentation of this file.
1 /*
2  * Process Hacker -
3  * thread provider
4  *
5  * Copyright (C) 2010-2011 wj32
6  *
7  * This file is part of Process Hacker.
8  *
9  * Process Hacker is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Process Hacker is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * The thread provider is tied to the process provider, and runs by registering
25  * a callback for the processes-updated event. This is because calculating CPU
26  * usage depends on deltas calculated by the process provider. However, this
27  * does increase the complexity of the thread provider system.
28  */
29 
30 #define PH_THRDPRV_PRIVATE
31 #include <phapp.h>
32 #include <kphuser.h>
33 #include <symprv.h>
34 #include <extmgri.h>
35 
36 typedef struct _PH_THREAD_QUERY_DATA
37 {
38  SLIST_ENTRY ListEntry;
39  PPH_THREAD_PROVIDER ThreadProvider;
40  PPH_THREAD_ITEM ThreadItem;
41  ULONG64 RunId;
42 
43  PPH_STRING StartAddressString;
44  PH_SYMBOL_RESOLVE_LEVEL StartAddressResolveLevel;
45 
48 
49 typedef struct _PH_THREAD_SYMBOL_LOAD_CONTEXT
50 {
51  HANDLE ProcessId;
52  PPH_THREAD_PROVIDER ThreadProvider;
53  PPH_SYMBOL_PROVIDER SymbolProvider;
55 
57  _In_ PVOID Object,
58  _In_ ULONG Flags
59  );
60 
62  _In_ PVOID Object,
63  _In_ ULONG Flags
64  );
65 
67  _In_ PVOID Entry1,
68  _In_ PVOID Entry2
69  );
70 
72  _In_ PVOID Entry
73  );
74 
76  _In_opt_ PVOID Parameter,
77  _In_opt_ PVOID Context
78  );
79 
81  _In_ PPH_THREAD_PROVIDER ThreadProvider,
82  _In_ PVOID ProcessInformation
83  );
84 
87 
90 
92  VOID
93  )
94 {
95  PhThreadProviderType = PhCreateObjectType(L"ThreadProvider", 0, PhpThreadProviderDeleteProcedure);
96  PhThreadItemType = PhCreateObjectType(L"ThreadItem", 0, PhpThreadItemDeleteProcedure);
97 
98  return TRUE;
99 }
100 
102  _In_ PTHREAD_START_ROUTINE Function,
103  _In_opt_ PVOID Context
104  )
105 {
106  if (PhBeginInitOnce(&PhThreadProviderWorkQueueInitOnce))
107  {
108  PhInitializeWorkQueue(&PhThreadProviderWorkQueue, 0, 1, 1000);
109  PhEndInitOnce(&PhThreadProviderWorkQueueInitOnce);
110  }
111 
112  PhQueueItemWorkQueue(&PhThreadProviderWorkQueue, Function, Context);
113 }
114 
116  _In_ HANDLE ProcessId
117  )
118 {
119  PPH_THREAD_PROVIDER threadProvider;
120 
121  threadProvider = PhCreateObject(
123  PhThreadProviderType
124  );
125  memset(threadProvider, 0, sizeof(PH_THREAD_PROVIDER));
126 
127  threadProvider->ThreadHashtable = PhCreateHashtable(
128  sizeof(PPH_THREAD_ITEM),
131  20
132  );
133  PhInitializeFastLock(&threadProvider->ThreadHashtableLock);
134 
135  PhInitializeCallback(&threadProvider->ThreadAddedEvent);
136  PhInitializeCallback(&threadProvider->ThreadModifiedEvent);
137  PhInitializeCallback(&threadProvider->ThreadRemovedEvent);
138  PhInitializeCallback(&threadProvider->UpdatedEvent);
140 
141  threadProvider->ProcessId = ProcessId;
142  threadProvider->SymbolProvider = PhCreateSymbolProvider(ProcessId);
143 
144  if (threadProvider->SymbolProvider)
145  {
146  if (threadProvider->SymbolProvider->IsRealHandle)
147  threadProvider->ProcessHandle = threadProvider->SymbolProvider->ProcessHandle;
148  }
149 
150  RtlInitializeSListHead(&threadProvider->QueryListHead);
151  PhInitializeQueuedLock(&threadProvider->LoadSymbolsLock);
152 
153  threadProvider->RunId = 1;
154  threadProvider->SymbolsLoadedRunId = 0; // Force symbols to be loaded the first time we try to resolve an address
155 
157 
158  return threadProvider;
159 }
160 
162  _In_ PVOID Object,
163  _In_ ULONG Flags
164  )
165 {
166  PPH_THREAD_PROVIDER threadProvider = (PPH_THREAD_PROVIDER)Object;
167 
169 
170  // Dereference all thread items (we referenced them
171  // when we added them to the hashtable).
172  PhDereferenceAllThreadItems(threadProvider);
173 
174  PhDereferenceObject(threadProvider->ThreadHashtable);
175  PhDeleteFastLock(&threadProvider->ThreadHashtableLock);
176  PhDeleteCallback(&threadProvider->ThreadAddedEvent);
177  PhDeleteCallback(&threadProvider->ThreadModifiedEvent);
178  PhDeleteCallback(&threadProvider->ThreadRemovedEvent);
179  PhDeleteCallback(&threadProvider->UpdatedEvent);
180  PhDeleteCallback(&threadProvider->LoadingStateChangedEvent);
181 
182  // Destroy all queue items.
183  {
184  PSLIST_ENTRY entry;
186 
187  entry = RtlInterlockedFlushSList(&threadProvider->QueryListHead);
188 
189  while (entry)
190  {
191  data = CONTAINING_RECORD(entry, PH_THREAD_QUERY_DATA, ListEntry);
192  entry = entry->Next;
193 
194  PhClearReference(&data->StartAddressString);
195  PhClearReference(&data->ServiceName);
196  PhDereferenceObject(data->ThreadItem);
197  PhFree(data);
198  }
199  }
200 
201  // We don't close the process handle because it is owned by
202  // the symbol provider.
203  if (threadProvider->SymbolProvider) PhDereferenceObject(threadProvider->SymbolProvider);
204 }
205 
207  _In_ PPH_THREAD_PROVIDER ThreadProvider,
208  _Out_ PPH_CALLBACK_REGISTRATION CallbackRegistration
209  )
210 {
211  PhReferenceObject(ThreadProvider);
212  PhRegisterCallback(&PhProcessesUpdatedEvent, PhpThreadProviderCallbackHandler, ThreadProvider, CallbackRegistration);
213 }
214 
216  _In_ PPH_THREAD_PROVIDER ThreadProvider,
217  _In_ PPH_CALLBACK_REGISTRATION CallbackRegistration
218  )
219 {
220  PhUnregisterCallback(&PhProcessesUpdatedEvent, CallbackRegistration);
221  PhDereferenceObject(ThreadProvider);
222 }
223 
225  _Inout_ PPH_THREAD_PROVIDER ThreadProvider
226  )
227 {
228  ThreadProvider->Terminating = TRUE;
229 }
230 
231 static BOOLEAN LoadSymbolsEnumGenericModulesCallback(
232  _In_ PPH_MODULE_INFO Module,
233  _In_opt_ PVOID Context
234  )
235 {
236  PPH_THREAD_SYMBOL_LOAD_CONTEXT context = Context;
237  PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider;
238 
239  if (context->ThreadProvider->Terminating)
240  return FALSE;
241 
242  // If we're loading kernel module symbols for a process other than
243  // System, ignore modules which are in user space. This may happen
244  // in Windows 7.
245  if (context->ProcessId == SYSTEM_PROCESS_ID &&
246  context->ThreadProvider->ProcessId != SYSTEM_PROCESS_ID &&
247  (ULONG_PTR)Module->BaseAddress <= PhSystemBasicInformation.MaximumUserModeAddress)
248  {
249  return TRUE;
250  }
251 
253  symbolProvider,
254  Module->FileName->Buffer,
255  (ULONG64)Module->BaseAddress,
256  Module->Size
257  );
258 
259  return TRUE;
260 }
261 
262 static BOOLEAN LoadBasicSymbolsEnumGenericModulesCallback(
263  _In_ PPH_MODULE_INFO Module,
264  _In_opt_ PVOID Context
265  )
266 {
267  PPH_THREAD_SYMBOL_LOAD_CONTEXT context = Context;
268  PPH_SYMBOL_PROVIDER symbolProvider = context->SymbolProvider;
269 
270  if (context->ThreadProvider->Terminating)
271  return FALSE;
272 
273  if (PhEqualString2(Module->Name, L"ntdll.dll", TRUE) ||
274  PhEqualString2(Module->Name, L"kernel32.dll", TRUE))
275  {
277  symbolProvider,
278  Module->FileName->Buffer,
279  (ULONG64)Module->BaseAddress,
280  Module->Size
281  );
282  }
283 
284  return TRUE;
285 }
286 
288  _In_ PPH_THREAD_PROVIDER ThreadProvider
289  )
290 {
291  PH_THREAD_SYMBOL_LOAD_CONTEXT loadContext;
292  ULONG64 runId;
293 
294  loadContext.ThreadProvider = ThreadProvider;
295  loadContext.SymbolProvider = ThreadProvider->SymbolProvider;
296 
297  PhAcquireQueuedLockExclusive(&ThreadProvider->LoadSymbolsLock);
298  runId = ThreadProvider->RunId;
299  PhLoadSymbolProviderOptions(ThreadProvider->SymbolProvider);
300 
301  if (ThreadProvider->ProcessId != SYSTEM_IDLE_PROCESS_ID)
302  {
303  if (ThreadProvider->SymbolProvider->IsRealHandle ||
304  ThreadProvider->ProcessId == SYSTEM_PROCESS_ID)
305  {
306  loadContext.ProcessId = ThreadProvider->ProcessId;
308  ThreadProvider->ProcessId,
309  ThreadProvider->SymbolProvider->ProcessHandle,
310  0,
311  LoadSymbolsEnumGenericModulesCallback,
312  &loadContext
313  );
314  }
315  else
316  {
317  // We can't enumerate the process modules. Load
318  // symbols for ntdll.dll and kernel32.dll.
319  loadContext.ProcessId = NtCurrentProcessId();
321  NtCurrentProcessId(),
322  NtCurrentProcess(),
323  0,
324  LoadBasicSymbolsEnumGenericModulesCallback,
325  &loadContext
326  );
327  }
328 
329  // Load kernel module symbols as well.
330  if (ThreadProvider->ProcessId != SYSTEM_PROCESS_ID)
331  {
332  loadContext.ProcessId = SYSTEM_PROCESS_ID;
335  NULL,
336  0,
337  LoadSymbolsEnumGenericModulesCallback,
338  &loadContext
339  );
340  }
341  }
342  else
343  {
344  // System Idle Process has one thread for each CPU,
345  // each having a start address at KiIdleLoop. We
346  // need to load symbols for the kernel.
347 
348  PRTL_PROCESS_MODULES kernelModules;
349 
350  if (NT_SUCCESS(PhEnumKernelModules(&kernelModules)))
351  {
352  if (kernelModules->NumberOfModules > 0)
353  {
354  PPH_STRING fileName;
355  PPH_STRING newFileName;
356 
357  fileName = PhConvertMultiByteToUtf16(kernelModules->Modules[0].FullPathName);
358  newFileName = PhGetFileName(fileName);
359  PhDereferenceObject(fileName);
360 
362  ThreadProvider->SymbolProvider,
363  newFileName->Buffer,
364  (ULONG64)kernelModules->Modules[0].ImageBase,
365  kernelModules->Modules[0].ImageSize
366  );
367  PhDereferenceObject(newFileName);
368  }
369 
370  PhFree(kernelModules);
371  }
372  }
373 
374  ThreadProvider->SymbolsLoadedRunId = runId;
375  PhReleaseQueuedLockExclusive(&ThreadProvider->LoadSymbolsLock);
376 }
377 
379  _In_ HANDLE ThreadId
380  )
381 {
382  PPH_THREAD_ITEM threadItem;
383 
384  threadItem = PhCreateObject(
386  PhThreadItemType
387  );
388  memset(threadItem, 0, sizeof(PH_THREAD_ITEM));
389  threadItem->ThreadId = ThreadId;
390  PhPrintUInt32(threadItem->ThreadIdString, (ULONG)ThreadId);
391 
393 
394  return threadItem;
395 }
396 
398  _In_ PVOID Object,
399  _In_ ULONG Flags
400  )
401 {
402  PPH_THREAD_ITEM threadItem = (PPH_THREAD_ITEM)Object;
403 
405 
406  if (threadItem->ThreadHandle) NtClose(threadItem->ThreadHandle);
407  if (threadItem->StartAddressString) PhDereferenceObject(threadItem->StartAddressString);
408  if (threadItem->StartAddressFileName) PhDereferenceObject(threadItem->StartAddressFileName);
409  if (threadItem->ServiceName) PhDereferenceObject(threadItem->ServiceName);
410 }
411 
413  _In_ PVOID Entry1,
414  _In_ PVOID Entry2
415  )
416 {
417  return
418  (*(PPH_THREAD_ITEM *)Entry1)->ThreadId ==
419  (*(PPH_THREAD_ITEM *)Entry2)->ThreadId;
420 }
421 
423  _In_ PVOID Entry
424  )
425 {
426  return (ULONG)(*(PPH_THREAD_ITEM *)Entry)->ThreadId / 4;
427 }
428 
430  _In_ PPH_THREAD_PROVIDER ThreadProvider,
431  _In_ HANDLE ThreadId
432  )
433 {
434  PH_THREAD_ITEM lookupThreadItem;
435  PPH_THREAD_ITEM lookupThreadItemPtr = &lookupThreadItem;
436  PPH_THREAD_ITEM *threadItemPtr;
437  PPH_THREAD_ITEM threadItem;
438 
439  lookupThreadItem.ThreadId = ThreadId;
440 
441  PhAcquireFastLockShared(&ThreadProvider->ThreadHashtableLock);
442 
443  threadItemPtr = (PPH_THREAD_ITEM *)PhFindEntryHashtable(
444  ThreadProvider->ThreadHashtable,
445  &lookupThreadItemPtr
446  );
447 
448  if (threadItemPtr)
449  {
450  threadItem = *threadItemPtr;
451  PhReferenceObject(threadItem);
452  }
453  else
454  {
455  threadItem = NULL;
456  }
457 
458  PhReleaseFastLockShared(&ThreadProvider->ThreadHashtableLock);
459 
460  return threadItem;
461 }
462 
464  _In_ PPH_THREAD_PROVIDER ThreadProvider
465  )
466 {
467  ULONG enumerationKey = 0;
468  PPH_THREAD_ITEM *threadItem;
469 
470  PhAcquireFastLockExclusive(&ThreadProvider->ThreadHashtableLock);
471 
472  while (PhEnumHashtable(ThreadProvider->ThreadHashtable, (PVOID *)&threadItem, &enumerationKey))
473  {
474  PhDereferenceObject(*threadItem);
475  }
476 
477  PhReleaseFastLockExclusive(&ThreadProvider->ThreadHashtableLock);
478 }
479 
481  _In_ PPH_THREAD_PROVIDER ThreadProvider,
482  _In_ PPH_THREAD_ITEM ThreadItem
483  )
484 {
485  PhRemoveEntryHashtable(ThreadProvider->ThreadHashtable, &ThreadItem);
486  PhDereferenceObject(ThreadItem);
487 }
488 
490  _In_ PVOID Parameter
491  )
492 {
494  LONG newSymbolsLoading;
495 
496  if (data->ThreadProvider->Terminating)
497  goto Done;
498 
499  newSymbolsLoading = _InterlockedIncrement(&data->ThreadProvider->SymbolsLoading);
500 
501  if (newSymbolsLoading == 1)
502  PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)TRUE);
503 
504  if (data->ThreadProvider->SymbolsLoadedRunId == 0)
505  PhLoadSymbolsThreadProvider(data->ThreadProvider);
506 
507  data->StartAddressString = PhGetSymbolFromAddress(
508  data->ThreadProvider->SymbolProvider,
509  data->ThreadItem->StartAddress,
510  &data->StartAddressResolveLevel,
511  &data->ThreadItem->StartAddressFileName,
512  NULL,
513  NULL
514  );
515 
516  if (data->StartAddressResolveLevel == PhsrlAddress && data->ThreadProvider->SymbolsLoadedRunId < data->RunId)
517  {
518  // The process may have loaded new modules, so load symbols for those and try again.
519 
520  PhLoadSymbolsThreadProvider(data->ThreadProvider);
521 
522  PhClearReference(&data->StartAddressString);
523  PhClearReference(&data->ThreadItem->StartAddressFileName);
524  data->StartAddressString = PhGetSymbolFromAddress(
525  data->ThreadProvider->SymbolProvider,
526  data->ThreadItem->StartAddress,
527  &data->StartAddressResolveLevel,
528  &data->ThreadItem->StartAddressFileName,
529  NULL,
530  NULL
531  );
532  }
533 
534  newSymbolsLoading = _InterlockedDecrement(&data->ThreadProvider->SymbolsLoading);
535 
536  if (newSymbolsLoading == 0)
537  PhInvokeCallback(&data->ThreadProvider->LoadingStateChangedEvent, (PVOID)FALSE);
538 
539  // Check if the process has services - we'll need to know before getting service tag/name
540  // information.
541  if (WINDOWS_HAS_SERVICE_TAGS && !data->ThreadProvider->HasServicesKnown)
542  {
543  PPH_PROCESS_ITEM processItem;
544 
545  if (processItem = PhReferenceProcessItem(data->ThreadProvider->ProcessId))
546  {
547  data->ThreadProvider->HasServices = processItem->ServiceList && processItem->ServiceList->Count != 0;
548  PhDereferenceObject(processItem);
549  }
550 
551  data->ThreadProvider->HasServicesKnown = TRUE;
552  }
553 
554  // Get the service tag, and the service name.
556  data->ThreadProvider->SymbolProvider->IsRealHandle &&
557  data->ThreadItem->ThreadHandle)
558  {
559  PVOID serviceTag;
560 
562  data->ThreadItem->ThreadHandle,
563  data->ThreadProvider->ProcessHandle,
564  &serviceTag
565  )))
566  {
567  data->ServiceName = PhGetServiceNameFromTag(
568  data->ThreadProvider->ProcessId,
569  serviceTag
570  );
571  }
572  }
573 
574 Done:
575  RtlInterlockedPushEntrySList(&data->ThreadProvider->QueryListHead, &data->ListEntry);
576  PhDereferenceObject(data->ThreadProvider);
577 
578  return STATUS_SUCCESS;
579 }
580 
582  _In_ PPH_THREAD_PROVIDER ThreadProvider,
583  _In_ PPH_THREAD_ITEM ThreadItem
584  )
585 {
587 
588  data = PhAllocate(sizeof(PH_THREAD_QUERY_DATA));
589  memset(data, 0, sizeof(PH_THREAD_QUERY_DATA));
590  PhSetReference(&data->ThreadProvider, ThreadProvider);
591  PhSetReference(&data->ThreadItem, ThreadItem);
592  data->RunId = ThreadProvider->RunId;
593 
595 }
596 
598  _In_ PPH_THREAD_PROVIDER ThreadProvider,
599  _In_ ULONG64 Address,
600  _Out_ PPH_SYMBOL_RESOLVE_LEVEL ResolveLevel
601  )
602 {
603  ULONG64 modBase;
604  PPH_STRING fileName = NULL;
605  PPH_STRING baseName = NULL;
606  PPH_STRING symbol;
607 
608  modBase = PhGetModuleFromAddress(
609  ThreadProvider->SymbolProvider,
610  Address,
611  &fileName
612  );
613 
614  if (fileName == NULL)
615  {
616  *ResolveLevel = PhsrlAddress;
617 
618  symbol = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2);
619  PhPrintPointer(symbol->Buffer, (PVOID)Address);
621  }
622  else
623  {
624  PH_FORMAT format[3];
625 
626  baseName = PhGetBaseName(fileName);
627  *ResolveLevel = PhsrlModule;
628 
629  PhInitFormatSR(&format[0], baseName->sr);
630  PhInitFormatS(&format[1], L"+0x");
631  PhInitFormatIX(&format[2], (ULONG_PTR)(Address - modBase));
632 
633  symbol = PhFormat(format, 3, baseName->Length + 6 + 32);
634  }
635 
636  if (fileName)
637  PhDereferenceObject(fileName);
638  if (baseName)
639  PhDereferenceObject(baseName);
640 
641  return symbol;
642 }
643 
644 static NTSTATUS PhpGetThreadCycleTime(
645  _In_ PPH_THREAD_PROVIDER ThreadProvider,
646  _In_ PPH_THREAD_ITEM ThreadItem,
647  _Out_ PULONG64 CycleTime
648  )
649 {
650  if (ThreadProvider->ProcessId != SYSTEM_IDLE_PROCESS_ID)
651  {
652  return PhGetThreadCycleTime(ThreadItem->ThreadHandle, CycleTime);
653  }
654  else
655  {
656  if ((ULONG)ThreadItem->ThreadId < (ULONG)PhSystemBasicInformation.NumberOfProcessors)
657  {
658  *CycleTime = PhCpuIdleCycleTime[(ULONG)ThreadItem->ThreadId].QuadPart;
659  return STATUS_SUCCESS;
660  }
661  }
662 
663  return STATUS_INVALID_PARAMETER;
664 }
665 
667  _In_ LONG PriorityWin32
668  )
669 {
670  switch (PriorityWin32)
671  {
672  case THREAD_PRIORITY_TIME_CRITICAL:
673  return PhCreateString(L"Time Critical");
674  case THREAD_PRIORITY_HIGHEST:
675  return PhCreateString(L"Highest");
676  case THREAD_PRIORITY_ABOVE_NORMAL:
677  return PhCreateString(L"Above Normal");
678  case THREAD_PRIORITY_NORMAL:
679  return PhCreateString(L"Normal");
680  case THREAD_PRIORITY_BELOW_NORMAL:
681  return PhCreateString(L"Below Normal");
682  case THREAD_PRIORITY_LOWEST:
683  return PhCreateString(L"Lowest");
684  case THREAD_PRIORITY_IDLE:
685  return PhCreateString(L"Idle");
686  case THREAD_PRIORITY_ERROR_RETURN:
687  return NULL;
688  default:
689  return PhFormatString(L"%d", PriorityWin32);
690  }
691 }
692 
694  _In_ PPH_THREAD_PROVIDER ThreadProvider
695  )
696 {
697  PVOID processes;
698 
699  if (NT_SUCCESS(PhEnumProcesses(&processes)))
700  {
701  PhpThreadProviderUpdate(ThreadProvider, processes);
702  PhFree(processes);
703  }
704 }
705 
707  _In_opt_ PVOID Parameter,
708  _In_opt_ PVOID Context
709  )
710 {
712  {
714  }
715 }
716 
718  _In_ PPH_THREAD_PROVIDER ThreadProvider,
719  _In_ PVOID ProcessInformation
720  )
721 {
722  PPH_THREAD_PROVIDER threadProvider = ThreadProvider;
724  SYSTEM_PROCESS_INFORMATION localProcess;
726  ULONG numberOfThreads;
727  ULONG i;
728 
729  process = PhFindProcessInformation(ProcessInformation, threadProvider->ProcessId);
730 
731  if (!process)
732  {
733  // The process doesn't exist anymore. Pretend it does but
734  // has no threads.
735  process = &localProcess;
736  process->NumberOfThreads = 0;
737  }
738 
739  threads = process->Threads;
740  numberOfThreads = process->NumberOfThreads;
741 
742  // System Idle Process has one thread per CPU.
743  // They all have a TID of 0, but we can't have
744  // multiple TIDs, so we'll assign unique TIDs.
745  if (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID)
746  {
747  for (i = 0; i < numberOfThreads; i++)
748  {
749  threads[i].ClientId.UniqueThread = (HANDLE)i;
750  }
751  }
752 
753  // Look for dead threads.
754  {
755  PPH_LIST threadsToRemove = NULL;
756  ULONG enumerationKey = 0;
757  PPH_THREAD_ITEM *threadItem;
758 
759  while (PhEnumHashtable(threadProvider->ThreadHashtable, (PVOID *)&threadItem, &enumerationKey))
760  {
761  BOOLEAN found = FALSE;
762 
763  // Check if the thread still exists.
764  for (i = 0; i < numberOfThreads; i++)
765  {
766  PSYSTEM_THREAD_INFORMATION thread = &threads[i];
767 
768  if ((*threadItem)->ThreadId == thread->ClientId.UniqueThread)
769  {
770  found = TRUE;
771  break;
772  }
773  }
774 
775  if (!found)
776  {
777  // Raise the thread removed event.
778  PhInvokeCallback(&threadProvider->ThreadRemovedEvent, *threadItem);
779 
780  if (!threadsToRemove)
781  threadsToRemove = PhCreateList(2);
782 
783  PhAddItemList(threadsToRemove, *threadItem);
784  }
785  }
786 
787  if (threadsToRemove)
788  {
790 
791  for (i = 0; i < threadsToRemove->Count; i++)
792  {
794  threadProvider,
795  (PPH_THREAD_ITEM)threadsToRemove->Items[i]
796  );
797  }
798 
800  PhDereferenceObject(threadsToRemove);
801  }
802  }
803 
804  // Go through the queued thread query data.
805  {
806  PSLIST_ENTRY entry;
808 
809  entry = RtlInterlockedFlushSList(&threadProvider->QueryListHead);
810 
811  while (entry)
812  {
813  data = CONTAINING_RECORD(entry, PH_THREAD_QUERY_DATA, ListEntry);
814  entry = entry->Next;
815 
816  if (data->StartAddressResolveLevel == PhsrlFunction && data->StartAddressString)
817  {
818  PhSwapReference(&data->ThreadItem->StartAddressString, data->StartAddressString);
819  data->ThreadItem->StartAddressResolveLevel = data->StartAddressResolveLevel;
820  }
821 
822  PhMoveReference(&data->ThreadItem->ServiceName, data->ServiceName);
823 
824  data->ThreadItem->JustResolved = TRUE;
825 
826  if (data->StartAddressString) PhDereferenceObject(data->StartAddressString);
827  PhDereferenceObject(data->ThreadItem);
828  PhFree(data);
829  }
830  }
831 
832  // Look for new threads and update existing ones.
833  for (i = 0; i < numberOfThreads; i++)
834  {
835  PSYSTEM_THREAD_INFORMATION thread = &threads[i];
836  PPH_THREAD_ITEM threadItem;
837 
838  threadItem = PhReferenceThreadItem(threadProvider, thread->ClientId.UniqueThread);
839 
840  if (!threadItem)
841  {
842  PVOID startAddress = NULL;
843 
844  threadItem = PhCreateThreadItem(thread->ClientId.UniqueThread);
845 
846  threadItem->CreateTime = thread->CreateTime;
847  threadItem->KernelTime = thread->KernelTime;
848  threadItem->UserTime = thread->UserTime;
849 
850  PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches);
851  threadItem->Priority = thread->Priority;
852  threadItem->BasePriority = thread->BasePriority;
853  threadItem->State = (KTHREAD_STATE)thread->ThreadState;
854  threadItem->WaitReason = thread->WaitReason;
855 
856  // Try to open a handle to the thread.
858  &threadItem->ThreadHandle,
860  threadItem->ThreadId
861  )))
862  {
863  PhOpenThread(
864  &threadItem->ThreadHandle,
866  threadItem->ThreadId
867  );
868  }
869 
870  // Get the cycle count.
872  {
873  ULONG64 cycles;
874 
875  if (NT_SUCCESS(PhpGetThreadCycleTime(
876  threadProvider,
877  threadItem,
878  &cycles
879  )))
880  {
881  PhUpdateDelta(&threadItem->CyclesDelta, cycles);
882  }
883  }
884 
885  // Initialize the CPU time deltas.
886  PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart);
887  PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart);
888 
889  // Try to get the start address.
890 
891  if (threadItem->ThreadHandle)
892  {
893  NtQueryInformationThread(
894  threadItem->ThreadHandle,
895  ThreadQuerySetWin32StartAddress,
896  &startAddress,
897  sizeof(PVOID),
898  NULL
899  );
900  }
901 
902  if (!startAddress)
903  startAddress = thread->StartAddress;
904 
905  threadItem->StartAddress = (ULONG64)startAddress;
906 
907  // Get the Win32 priority.
908  threadItem->PriorityWin32 = GetThreadPriority(threadItem->ThreadHandle);
909 
910  if (threadProvider->SymbolsLoadedRunId != 0)
911  {
913  threadProvider,
914  threadItem->StartAddress,
915  &threadItem->StartAddressResolveLevel
916  );
917  }
918 
919  if (!threadItem->StartAddressString)
920  {
922  threadItem->StartAddressString = PhCreateStringEx(NULL, PH_PTR_STR_LEN * 2);
924  threadItem->StartAddressString->Buffer,
925  (PVOID)threadItem->StartAddress
926  );
928  }
929 
930  PhpQueueThreadQuery(threadProvider, threadItem);
931 
932  // Is it a GUI thread?
933 
934  if (threadItem->ThreadHandle && KphIsConnected())
935  {
936  PVOID win32Thread;
937 
939  threadItem->ThreadHandle,
941  &win32Thread,
942  sizeof(PVOID),
943  NULL
944  )))
945  {
946  threadItem->IsGuiThread = win32Thread != NULL;
947  }
948  }
949  else
950  {
951  GUITHREADINFO info = { sizeof(GUITHREADINFO) };
952 
953  threadItem->IsGuiThread = !!GetGUIThreadInfo((ULONG)threadItem->ThreadId, &info);
954  }
955 
956  // Add the thread item to the hashtable.
958  PhAddEntryHashtable(threadProvider->ThreadHashtable, &threadItem);
960 
961  // Raise the thread added event.
962  PhInvokeCallback(&threadProvider->ThreadAddedEvent, threadItem);
963  }
964  else
965  {
966  BOOLEAN modified = FALSE;
967 
968  if (threadItem->JustResolved)
969  modified = TRUE;
970 
971  threadItem->KernelTime = thread->KernelTime;
972  threadItem->UserTime = thread->UserTime;
973 
974  threadItem->Priority = thread->Priority;
975  threadItem->BasePriority = thread->BasePriority;
976 
977  threadItem->State = (KTHREAD_STATE)thread->ThreadState;
978 
979  if (threadItem->WaitReason != thread->WaitReason)
980  {
981  threadItem->WaitReason = thread->WaitReason;
982  modified = TRUE;
983  }
984 
985  // If the resolve level is only at address, it probably
986  // means symbols weren't loaded the last time we
987  // tried to get the start address. Try again.
988  if (threadItem->StartAddressResolveLevel == PhsrlAddress)
989  {
990  if (threadProvider->SymbolsLoadedRunId != 0)
991  {
992  PPH_STRING newStartAddressString;
993 
994  newStartAddressString = PhpGetThreadBasicStartAddress(
995  threadProvider,
996  threadItem->StartAddress,
997  &threadItem->StartAddressResolveLevel
998  );
999 
1001  &threadItem->StartAddressString,
1002  newStartAddressString
1003  );
1004 
1005  modified = TRUE;
1006  }
1007  }
1008 
1009  // If we couldn't resolve the start address to a
1010  // module+offset, use the StartAddress instead
1011  // of the Win32StartAddress and try again.
1012  // Note that we check the resolve level again
1013  // because we may have changed it in the previous
1014  // block.
1015  if (threadItem->JustResolved &&
1016  threadItem->StartAddressResolveLevel == PhsrlAddress)
1017  {
1018  if (threadItem->StartAddress != (ULONG64)thread->StartAddress)
1019  {
1020  threadItem->StartAddress = (ULONG64)thread->StartAddress;
1021  PhpQueueThreadQuery(threadProvider, threadItem);
1022  }
1023  }
1024 
1025  // Update the context switch count.
1026  {
1027  ULONG oldDelta;
1028 
1029  oldDelta = threadItem->ContextSwitchesDelta.Delta;
1030  PhUpdateDelta(&threadItem->ContextSwitchesDelta, thread->ContextSwitches);
1031 
1032  if (threadItem->ContextSwitchesDelta.Delta != oldDelta)
1033  {
1034  modified = TRUE;
1035  }
1036  }
1037 
1038  // Update the cycle count.
1040  {
1041  ULONG64 cycles;
1042  ULONG64 oldDelta;
1043 
1044  oldDelta = threadItem->CyclesDelta.Delta;
1045 
1046  if (NT_SUCCESS(PhpGetThreadCycleTime(
1047  threadProvider,
1048  threadItem,
1049  &cycles
1050  )))
1051  {
1052  PhUpdateDelta(&threadItem->CyclesDelta, cycles);
1053 
1054  if (threadItem->CyclesDelta.Delta != oldDelta)
1055  {
1056  modified = TRUE;
1057  }
1058  }
1059  }
1060 
1061  // Update the CPU time deltas.
1062  PhUpdateDelta(&threadItem->CpuKernelDelta, threadItem->KernelTime.QuadPart);
1063  PhUpdateDelta(&threadItem->CpuUserDelta, threadItem->UserTime.QuadPart);
1064 
1065  // Update the CPU usage.
1066  // If the cycle time isn't available, we'll fall back to using the CPU time.
1067  if (WINDOWS_HAS_CYCLE_TIME && PhEnableCycleCpuUsage && (threadProvider->ProcessId == SYSTEM_IDLE_PROCESS_ID || threadItem->ThreadHandle))
1068  {
1069  threadItem->CpuUsage = (FLOAT)threadItem->CyclesDelta.Delta / PhCpuTotalCycleDelta;
1070  }
1071  else
1072  {
1073  threadItem->CpuUsage = (FLOAT)(threadItem->CpuKernelDelta.Delta + threadItem->CpuUserDelta.Delta) /
1075  }
1076 
1077  // Update the Win32 priority.
1078  {
1079  LONG oldPriorityWin32 = threadItem->PriorityWin32;
1080 
1081  threadItem->PriorityWin32 = GetThreadPriority(threadItem->ThreadHandle);
1082 
1083  if (threadItem->PriorityWin32 != oldPriorityWin32)
1084  {
1085  modified = TRUE;
1086  }
1087  }
1088 
1089  // Update the GUI thread status.
1090 
1091  if (threadItem->ThreadHandle && KphIsConnected())
1092  {
1093  PVOID win32Thread;
1094 
1096  threadItem->ThreadHandle,
1098  &win32Thread,
1099  sizeof(PVOID),
1100  NULL
1101  )))
1102  {
1103  BOOLEAN oldIsGuiThread = threadItem->IsGuiThread;
1104 
1105  threadItem->IsGuiThread = win32Thread != NULL;
1106 
1107  if (threadItem->IsGuiThread != oldIsGuiThread)
1108  modified = TRUE;
1109  }
1110  }
1111  else
1112  {
1113  GUITHREADINFO info = { sizeof(GUITHREADINFO) };
1114  BOOLEAN oldIsGuiThread = threadItem->IsGuiThread;
1115 
1116  threadItem->IsGuiThread = !!GetGUIThreadInfo((ULONG)threadItem->ThreadId, &info);
1117 
1118  if (threadItem->IsGuiThread != oldIsGuiThread)
1119  modified = TRUE;
1120  }
1121 
1122  threadItem->JustResolved = FALSE;
1123 
1124  if (modified)
1125  {
1126  // Raise the thread modified event.
1127  PhInvokeCallback(&threadProvider->ThreadModifiedEvent, threadItem);
1128  }
1129 
1130  PhDereferenceObject(threadItem);
1131  }
1132  }
1133 
1134  PhInvokeCallback(&threadProvider->UpdatedEvent, NULL);
1135  threadProvider->RunId++;
1136 }