Process Hacker
etwprprp.c
Go to the documentation of this file.
1 /*
2  * Process Hacker Extended Tools -
3  * ETW process properties page
4  *
5  * Copyright (C) 2010-2011 wj32
6  * Copyright (C) 2015 dmex
7  *
8  * This file is part of Process Hacker.
9  *
10  * Process Hacker is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Process Hacker is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "exttools.h"
25 #include "resource.h"
26 
27 #define MSG_UPDATE (WM_APP + 1)
28 
29 static RECT NormalGraphTextMargin = { 5, 5, 5, 5 };
30 static RECT NormalGraphTextPadding = { 3, 3, 3, 3 };
31 
32 typedef struct _ET_DISKNET_CONTEXT
33 {
34  HWND WindowHandle;
35  PET_PROCESS_BLOCK Block;
36  PH_CALLBACK_REGISTRATION ProcessesUpdatedRegistration;
37  BOOLEAN Enabled;
38 
39  PH_LAYOUT_MANAGER LayoutManager;
40 
41  HWND DiskGroupBox;
42  HWND NetworkGroupBox;
43 
44  HWND DiskGraphHandle;
45  HWND NetworkGraphHandle;
46  HWND PanelHandle;
47 
48  ULONG64 CurrentDiskRead;
49  ULONG64 CurrentDiskWrite;
50  ULONG64 CurrentNetworkSend;
51  ULONG64 CurrentNetworkReceive;
52 
53  PH_GRAPH_STATE DiskGraphState;
54  PH_GRAPH_STATE NetworkGraphState;
55 
56  PH_CIRCULAR_BUFFER_ULONG64 DiskReadHistory;
57  PH_CIRCULAR_BUFFER_ULONG64 DiskWriteHistory;
58  PH_CIRCULAR_BUFFER_ULONG64 NetworkSendHistory;
59  PH_CIRCULAR_BUFFER_ULONG64 NetworkReceiveHistory;
61 
62 static INT_PTR CALLBACK EtwDiskNetworkPanelDialogProc(
63  _In_ HWND hwndDlg,
64  _In_ UINT uMsg,
65  _In_ WPARAM wParam,
66  _In_ LPARAM lParam
67  )
68 {
69  return FALSE;
70 }
71 
72 static VOID EtwDiskNetworkCreateGraphs(
73  _In_ PET_DISKNET_CONTEXT Context
74  )
75 {
76  Context->DiskGraphHandle = CreateWindow(
78  NULL,
79  WS_VISIBLE | WS_CHILD | WS_BORDER,
80  0,
81  0,
82  3,
83  3,
84  Context->WindowHandle,
85  NULL,
86  NULL,
87  NULL
88  );
89  Graph_SetTooltip(Context->DiskGraphHandle, TRUE);
90 
91  Context->NetworkGraphHandle = CreateWindow(
93  NULL,
94  WS_VISIBLE | WS_CHILD | WS_BORDER,
95  0,
96  0,
97  3,
98  3,
99  Context->WindowHandle,
100  NULL,
101  NULL,
102  NULL
103  );
104  Graph_SetTooltip(Context->NetworkGraphHandle, TRUE);
105 }
106 
107 static VOID EtwDiskNetworkCreatePanel(
108  _In_ PET_DISKNET_CONTEXT Context
109  )
110 {
111  RECT margin;
112 
113  Context->PanelHandle = CreateDialogParam(
115  MAKEINTRESOURCE(IDD_PROCDISKNET_PANEL),
116  Context->WindowHandle,
117  EtwDiskNetworkPanelDialogProc,
118  (LPARAM)Context
119  );
120 
121  SetWindowPos(
122  Context->PanelHandle,
123  NULL,
124  10, 0, 0, 0,
125  SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSIZE | SWP_NOZORDER
126  );
127 
128  ShowWindow(Context->PanelHandle, SW_SHOW);
129 
130  margin.left = 0;
131  margin.top = 0;
132  margin.right = 0;
133  margin.bottom = 10;
134  MapDialogRect(Context->WindowHandle, &margin);
135 
137  &Context->LayoutManager,
138  Context->PanelHandle,
139  NULL,
141  margin
142  );
143 
144  SendMessage(Context->WindowHandle, WM_SIZE, 0, 0);
145 }
146 
147 static VOID EtwDiskNetworkLayoutGraphs(
148  _In_ PET_DISKNET_CONTEXT Context
149  )
150 {
151  HDWP deferHandle;
152  RECT clientRect;
153  RECT panelRect;
154  RECT margin = { 13, 13, 13, 13 };
155  RECT innerMargin = { 10, 20, 10, 10 };
156  LONG between = 3;
157  ULONG graphWidth;
158  ULONG graphHeight;
159 
160  PhLayoutManagerLayout(&Context->LayoutManager);
161 
162  Context->DiskGraphState.Valid = FALSE;
163  Context->NetworkGraphState.Valid = FALSE;
164 
165  GetClientRect(Context->WindowHandle, &clientRect);
166 
167  // Limit the rectangle bottom to the top of the panel.
168  GetWindowRect(Context->PanelHandle, &panelRect);
169  MapWindowPoints(NULL, Context->WindowHandle, (PPOINT)&panelRect, 2);
170  clientRect.bottom = panelRect.top + 10; // +10 removing extra spacing
171 
172  graphWidth = clientRect.right - margin.left - margin.right;
173  graphHeight = (clientRect.bottom - margin.top - margin.bottom - between * 2) / 2;
174 
175  deferHandle = BeginDeferWindowPos(4);
176 
177  deferHandle = DeferWindowPos(deferHandle, Context->DiskGroupBox, NULL, margin.left, margin.top, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER);
178  deferHandle = DeferWindowPos(
179  deferHandle,
180  Context->DiskGraphHandle,
181  NULL,
182  margin.left + innerMargin.left,
183  margin.top + innerMargin.top,
184  graphWidth - innerMargin.left - innerMargin.right,
185  graphHeight - innerMargin.top - innerMargin.bottom,
186  SWP_NOACTIVATE | SWP_NOZORDER
187  );
188 
189  deferHandle = DeferWindowPos(deferHandle, Context->NetworkGroupBox, NULL, margin.left, margin.top + graphHeight + between, graphWidth, graphHeight, SWP_NOACTIVATE | SWP_NOZORDER);
190  deferHandle = DeferWindowPos(
191  deferHandle,
192  Context->NetworkGraphHandle,
193  NULL,
194  margin.left + innerMargin.left,
195  margin.top + graphHeight + between + innerMargin.top,
196  graphWidth - innerMargin.left - innerMargin.right,
197  graphHeight - innerMargin.top - innerMargin.bottom,
198  SWP_NOACTIVATE | SWP_NOZORDER
199  );
200 
201  EndDeferWindowPos(deferHandle);
202 }
203 
204 static VOID EtwDiskNetworkUpdateGraphs(
205  _In_ PET_DISKNET_CONTEXT Context
206  )
207 {
208  Context->DiskGraphState.Valid = FALSE;
209  Context->DiskGraphState.TooltipIndex = -1;
210  Graph_MoveGrid(Context->DiskGraphHandle, 1);
211  Graph_Draw(Context->DiskGraphHandle);
212  Graph_UpdateTooltip(Context->DiskGraphHandle);
213  InvalidateRect(Context->DiskGraphHandle, NULL, FALSE);
214 
215  Context->NetworkGraphState.Valid = FALSE;
216  Context->NetworkGraphState.TooltipIndex = -1;
217  Graph_MoveGrid(Context->NetworkGraphHandle, 1);
218  Graph_Draw(Context->NetworkGraphHandle);
219  Graph_UpdateTooltip(Context->NetworkGraphHandle);
220  InvalidateRect(Context->NetworkGraphHandle, NULL, FALSE);
221 }
222 
223 static VOID EtwDiskNetworkUpdatePanel(
224  _Inout_ PET_DISKNET_CONTEXT Context
225  )
226 {
227  PET_PROCESS_BLOCK block = Context->Block;
228 
229  SetDlgItemText(Context->PanelHandle, IDC_ZREADS_V, PhaFormatUInt64(block->DiskReadCount, TRUE)->Buffer);
230  SetDlgItemText(Context->PanelHandle, IDC_ZREADBYTES_V, PhaFormatSize(block->DiskReadRawDelta.Value, -1)->Buffer);
231  SetDlgItemText(Context->PanelHandle, IDC_ZREADBYTESDELTA_V, PhaFormatSize(block->DiskReadRawDelta.Delta, -1)->Buffer);
232  SetDlgItemText(Context->PanelHandle, IDC_ZWRITES_V, PhaFormatUInt64(block->DiskWriteCount, TRUE)->Buffer);
233  SetDlgItemText(Context->PanelHandle, IDC_ZWRITEBYTES_V, PhaFormatSize(block->DiskWriteRawDelta.Value, -1)->Buffer);
234  SetDlgItemText(Context->PanelHandle, IDC_ZWRITEBYTESDELTA_V, PhaFormatSize(block->DiskWriteRawDelta.Delta, -1)->Buffer);
235 
236  SetDlgItemText(Context->PanelHandle, IDC_ZRECEIVES_V, PhaFormatUInt64(block->NetworkReceiveCount, TRUE)->Buffer);
237  SetDlgItemText(Context->PanelHandle, IDC_ZRECEIVEBYTES_V, PhaFormatSize(block->NetworkReceiveRawDelta.Value, -1)->Buffer);
238  SetDlgItemText(Context->PanelHandle, IDC_ZRECEIVEBYTESDELTA_V, PhaFormatSize(block->NetworkReceiveRawDelta.Delta, -1)->Buffer);
239  SetDlgItemText(Context->PanelHandle, IDC_ZSENDS_V, PhaFormatUInt64(block->NetworkSendCount, TRUE)->Buffer);
240  SetDlgItemText(Context->PanelHandle, IDC_ZSENDBYTES_V, PhaFormatSize(block->NetworkSendRawDelta.Value, -1)->Buffer);
241  SetDlgItemText(Context->PanelHandle, IDC_ZSENDBYTESDELTA_V, PhaFormatSize(block->NetworkSendRawDelta.Delta, -1)->Buffer);
242 }
243 
244 static VOID EtwDiskNetworkUpdateInfo(
245  _In_ PET_DISKNET_CONTEXT Context
246  )
247 {
248  PET_PROCESS_BLOCK block = Context->Block;
249 
250  Context->CurrentDiskRead = block->DiskReadRawDelta.Delta;
251  Context->CurrentDiskWrite = block->DiskWriteRawDelta.Delta;
252  Context->CurrentNetworkSend = block->NetworkSendRawDelta.Delta;
253  Context->CurrentNetworkReceive = block->NetworkReceiveRawDelta.Delta;
254 
255  PhAddItemCircularBuffer_ULONG64(&Context->DiskReadHistory, Context->CurrentDiskRead);
256  PhAddItemCircularBuffer_ULONG64(&Context->DiskWriteHistory, Context->CurrentDiskWrite);
257  PhAddItemCircularBuffer_ULONG64(&Context->NetworkSendHistory, Context->CurrentNetworkSend);
258  PhAddItemCircularBuffer_ULONG64(&Context->NetworkReceiveHistory, Context->CurrentNetworkReceive);
259 }
260 
261 static VOID NTAPI EtwDiskNetworkUpdateHandler(
262  _In_opt_ PVOID Parameter,
263  _In_opt_ PVOID Context
264  )
265 {
266  PET_DISKNET_CONTEXT context = Context;
267 
268  if (!context->Enabled)
269  return;
270 
271  if (context->WindowHandle)
272  {
273  PostMessage(context->WindowHandle, MSG_UPDATE, 0, 0);
274  }
275 }
276 
277 static INT_PTR CALLBACK EtwDiskNetworkPageDlgProc(
278  _In_ HWND hwndDlg,
279  _In_ UINT uMsg,
280  _In_ WPARAM wParam,
281  _In_ LPARAM lParam
282  )
283 {
284  LPPROPSHEETPAGE propSheetPage;
285  PPH_PROCESS_PROPPAGECONTEXT propPageContext;
286  PPH_PROCESS_ITEM processItem;
287  PET_DISKNET_CONTEXT context;
288 
289  if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem))
290  {
291  context = propPageContext->Context;
292  }
293  else
294  {
295  return FALSE;
296  }
297 
298  switch (uMsg)
299  {
300  case WM_INITDIALOG:
301  {
302  ULONG sampleCount;
303 
304  sampleCount = PhGetIntegerSetting(L"SampleCount");
305 
306  context = PhAllocate(sizeof(ET_DISKNET_CONTEXT));
307  memset(context, 0, sizeof(ET_DISKNET_CONTEXT));
308 
309  context->WindowHandle = hwndDlg;
310  context->Block = EtGetProcessBlock(processItem);
311  context->Enabled = TRUE;
312  context->DiskGroupBox = GetDlgItem(hwndDlg, IDC_GROUPDISK);
313  context->NetworkGroupBox = GetDlgItem(hwndDlg, IDC_GROUPNETWORK);
314  propPageContext->Context = context;
315 
316  PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
317 
318  PhInitializeGraphState(&context->DiskGraphState);
319  PhInitializeGraphState(&context->NetworkGraphState);
320 
321  PhInitializeCircularBuffer_ULONG64(&context->DiskReadHistory, sampleCount);
322  PhInitializeCircularBuffer_ULONG64(&context->DiskWriteHistory, sampleCount);
323  PhInitializeCircularBuffer_ULONG64(&context->NetworkSendHistory, sampleCount);
324  PhInitializeCircularBuffer_ULONG64(&context->NetworkReceiveHistory, sampleCount);
325 
326  EtwDiskNetworkCreateGraphs(context);
327  EtwDiskNetworkCreatePanel(context);
328  EtwDiskNetworkUpdateInfo(context);
329  EtwDiskNetworkUpdatePanel(context);
330 
332  &PhProcessesUpdatedEvent,
333  EtwDiskNetworkUpdateHandler,
334  context,
335  &context->ProcessesUpdatedRegistration
336  );
337  }
338  break;
339  case WM_DESTROY:
340  {
341  PhDeleteLayoutManager(&context->LayoutManager);
342 
343  PhDeleteGraphState(&context->DiskGraphState);
344  PhDeleteGraphState(&context->NetworkGraphState);
345 
346  PhDeleteCircularBuffer_ULONG64(&context->DiskReadHistory);
347  PhDeleteCircularBuffer_ULONG64(&context->DiskWriteHistory);
348  PhDeleteCircularBuffer_ULONG64(&context->NetworkSendHistory);
349  PhDeleteCircularBuffer_ULONG64(&context->NetworkReceiveHistory);
350 
351  if (context->DiskGraphHandle)
352  DestroyWindow(context->DiskGraphHandle);
353  if (context->NetworkGraphHandle)
354  DestroyWindow(context->NetworkGraphHandle);
355  if (context->PanelHandle)
356  DestroyWindow(context->PanelHandle);
357 
358  PhUnregisterCallback(&PhProcessesUpdatedEvent, &context->ProcessesUpdatedRegistration);
359  PhFree(context);
360 
361  PhPropPageDlgProcDestroy(hwndDlg);
362  }
363  break;
364  case WM_SHOWWINDOW:
365  {
366  if (PhBeginPropPageLayout(hwndDlg, propPageContext))
367  PhEndPropPageLayout(hwndDlg, propPageContext);
368  }
369  break;
370  case WM_NOTIFY:
371  {
372  LPNMHDR header = (LPNMHDR)lParam;
373 
374  switch (header->code)
375  {
376  case PSN_SETACTIVE:
377  context->Enabled = TRUE;
378  break;
379  case PSN_KILLACTIVE:
380  context->Enabled = FALSE;
381  break;
382  case GCN_GETDRAWINFO:
383  {
384  PPH_GRAPH_GETDRAWINFO getDrawInfo = (PPH_GRAPH_GETDRAWINFO)header;
385  PPH_GRAPH_DRAW_INFO drawInfo = getDrawInfo->DrawInfo;
386 
387  if (header->hwndFrom == context->DiskGraphHandle)
388  {
389  if (PhGetIntegerSetting(L"GraphShowText"))
390  {
391  HDC hdc;
392 
393  PhMoveReference(&context->DiskGraphState.Text, PhFormatString(
394  L"R: %s, W: %s",
395  PhaFormatSize(context->CurrentDiskRead, -1)->Buffer,
396  PhaFormatSize(context->CurrentDiskWrite, -1)->Buffer
397  ));
398 
399  hdc = Graph_GetBufferedContext(context->DiskGraphHandle);
400  SelectObject(hdc, PhApplicationFont);
401  PhSetGraphText(hdc, drawInfo, &context->DiskGraphState.Text->sr,
402  &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT);
403  }
404  else
405  {
406  drawInfo->Text.Buffer = NULL;
407  }
408 
410  PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite"));
411  PhGraphStateGetDrawInfo(&context->DiskGraphState, getDrawInfo, context->DiskReadHistory.Count);
412 
413  if (!context->DiskGraphState.Valid)
414  {
415  FLOAT max = 0;
416 
417  for (ULONG i = 0; i < drawInfo->LineDataCount; i++)
418  {
419  FLOAT data1;
420  FLOAT data2;
421 
422  context->DiskGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->DiskReadHistory, i);
423  context->DiskGraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->DiskWriteHistory, i);
424 
425  if (max < data1 + data2)
426  max = data1 + data2;
427  }
428 
429  // Minimum scaling of 1 MB.
430  //if (max < 1024 * 1024)
431  // max = 1024 * 1024;
432 
433  // Scale the data.
435  context->DiskGraphState.Data1,
436  max,
437  drawInfo->LineDataCount
438  );
439 
440  // Scale the data.
442  context->DiskGraphState.Data2,
443  max,
444  drawInfo->LineDataCount
445  );
446 
447  context->DiskGraphState.Valid = TRUE;
448  }
449  }
450  else if (header->hwndFrom == context->NetworkGraphHandle)
451  {
452  if (PhGetIntegerSetting(L"GraphShowText"))
453  {
454  HDC hdc;
455 
456  PhMoveReference(&context->NetworkGraphState.Text, PhFormatString(
457  L"R: %s, S: %s",
458  PhaFormatSize(context->CurrentNetworkReceive, -1)->Buffer,
459  PhaFormatSize(context->CurrentNetworkSend, -1)->Buffer
460  ));
461 
462  hdc = Graph_GetBufferedContext(context->NetworkGraphHandle);
463  SelectObject(hdc, PhApplicationFont);
464  PhSetGraphText(hdc, drawInfo, &context->NetworkGraphState.Text->sr,
465  &NormalGraphTextMargin, &NormalGraphTextPadding, PH_ALIGN_TOP | PH_ALIGN_LEFT);
466  }
467  else
468  {
469  drawInfo->Text.Buffer = NULL;
470  }
471 
473  PhSiSetColorsGraphDrawInfo(drawInfo, PhGetIntegerSetting(L"ColorIoReadOther"), PhGetIntegerSetting(L"ColorIoWrite"));
474  PhGraphStateGetDrawInfo(&context->NetworkGraphState, getDrawInfo, context->NetworkSendHistory.Count);
475 
476  if (!context->NetworkGraphState.Valid)
477  {
478  FLOAT max = 0;
479 
480  for (ULONG i = 0; i < drawInfo->LineDataCount; i++)
481  {
482  FLOAT data1;
483  FLOAT data2;
484 
485  context->NetworkGraphState.Data1[i] = data1 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->NetworkReceiveHistory, i);
486  context->NetworkGraphState.Data2[i] = data2 = (FLOAT)PhGetItemCircularBuffer_ULONG64(&context->NetworkSendHistory, i);
487 
488  if (max < data1 + data2)
489  max = data1 + data2;
490  }
491 
492  // Minimum scaling of 1 MB.
493  //if (max < 1024 * 1024)
494  // max = 1024 * 1024;
495 
496  // Scale the data.
498  context->NetworkGraphState.Data1,
499  max,
500  drawInfo->LineDataCount
501  );
502 
503  // Scale the data.
505  context->NetworkGraphState.Data2,
506  max,
507  drawInfo->LineDataCount
508  );
509 
510  context->NetworkGraphState.Valid = TRUE;
511  }
512  }
513  }
514  break;
515  case GCN_GETTOOLTIPTEXT:
516  {
517  PPH_GRAPH_GETTOOLTIPTEXT getTooltipText = (PPH_GRAPH_GETTOOLTIPTEXT)lParam;
518 
519  if (getTooltipText->Index < getTooltipText->TotalCount)
520  {
521  if (header->hwndFrom == context->DiskGraphHandle)
522  {
523  if (context->DiskGraphState.TooltipIndex != getTooltipText->Index)
524  {
525  ULONG64 diskRead = PhGetItemCircularBuffer_ULONG64(
526  &context->DiskReadHistory,
527  getTooltipText->Index
528  );
529 
530  ULONG64 diskWrite = PhGetItemCircularBuffer_ULONG64(
531  &context->DiskWriteHistory,
532  getTooltipText->Index
533  );
534 
535  PhMoveReference(&context->DiskGraphState.TooltipText, PhFormatString(
536  L"R: %s\nW: %s\n%s",
537  PhaFormatSize(diskRead, -1)->Buffer,
538  PhaFormatSize(diskWrite, -1)->Buffer,
539  ((PPH_STRING)PhAutoDereferenceObject(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer
540  ));
541  }
542 
543  getTooltipText->Text = context->DiskGraphState.TooltipText->sr;
544  }
545  else if (header->hwndFrom == context->NetworkGraphHandle)
546  {
547  if (context->NetworkGraphState.TooltipIndex != getTooltipText->Index)
548  {
549  ULONG64 networkSend = PhGetItemCircularBuffer_ULONG64(
550  &context->NetworkSendHistory,
551  getTooltipText->Index
552  );
553 
554  ULONG64 networkReceive = PhGetItemCircularBuffer_ULONG64(
555  &context->NetworkReceiveHistory,
556  getTooltipText->Index
557  );
558 
559  PhMoveReference(&context->NetworkGraphState.TooltipText, PhFormatString(
560  L"S: %s\nR: %s\n%s",
561  PhaFormatSize(networkSend, -1)->Buffer,
562  PhaFormatSize(networkReceive, -1)->Buffer,
563  ((PPH_STRING)PhAutoDereferenceObject(PhGetStatisticsTimeString(NULL, getTooltipText->Index)))->Buffer
564  ));
565  }
566 
567  getTooltipText->Text = context->NetworkGraphState.TooltipText->sr;
568  }
569  }
570  }
571  break;
572  }
573  }
574  break;
575  case MSG_UPDATE:
576  {
577  EtwDiskNetworkUpdateInfo(context);
578  EtwDiskNetworkUpdateGraphs(context);
579  EtwDiskNetworkUpdatePanel(context);
580  }
581  break;
582  case WM_SIZE:
583  {
584  EtwDiskNetworkLayoutGraphs(context);
585  }
586  break;
587  }
588 
589  return FALSE;
590 }
591 
593  _In_ PVOID Parameter
594  )
595 {
596  PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter;
597 
598  if (EtEtwEnabled)
599  {
601  propContext->PropContext,
602  PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCDISKNET), EtwDiskNetworkPageDlgProc, NULL)
603  );
604  }
605 }