2025-05-13 19:49:49 +03:00

316 lines
9.6 KiB
C

/*
* Process Hacker Extra Plugins -
* Average CPU Plugin
*
* Copyright (C) 2011 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include <phdk.h>
#include "resource.h"
#define COLUMN_ID_AVGCPU10 1
#define COLUMN_ID_AVGCPU60 2
#define HISTORY_SIZE 60
typedef struct _PROCESS_EXTENSION
{
LIST_ENTRY ListEntry;
PPH_PROCESS_ITEM ProcessItem;
FLOAT CpuHistory[HISTORY_SIZE];
ULONG CpuHistoryCount;
ULONG CpuHistoryPosition;
FLOAT Avg10CpuUsage;
FLOAT Avg60CpuUsage;
WCHAR Avg10CpuUsageText[PH_INT32_STR_LEN_1];
WCHAR Avg60CpuUsageText[PH_INT32_STR_LEN_1];
} PROCESS_EXTENSION, *PPROCESS_EXTENSION;
PPH_PLUGIN PluginInstance;
PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration;
PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration;
PH_CALLBACK_REGISTRATION ProcessAddedCallbackRegistration;
PH_CALLBACK_REGISTRATION ProcessRemovedCallbackRegistration;
PH_CALLBACK_REGISTRATION ProcessesUpdatedCallbackRegistration;
LIST_ENTRY ProcessListHead = { &ProcessListHead, &ProcessListHead };
VOID TreeNewMessageCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
PPH_PLUGIN_TREENEW_MESSAGE message = Parameter;
switch (message->Message)
{
case TreeNewGetCellText:
{
PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1;
PPH_PROCESS_NODE node;
PPROCESS_EXTENSION extension;
node = (PPH_PROCESS_NODE)getCellText->Node;
extension = PhPluginGetObjectExtension(PluginInstance, node->ProcessItem, EmProcessItemType);
switch (message->SubId)
{
case COLUMN_ID_AVGCPU10:
case COLUMN_ID_AVGCPU60:
{
FLOAT cpuUsage;
PWCHAR buffer;
if (message->SubId == COLUMN_ID_AVGCPU10)
{
cpuUsage = extension->Avg10CpuUsage * 100;
buffer = extension->Avg10CpuUsageText;
}
else
{
cpuUsage = extension->Avg60CpuUsage * 100;
buffer = extension->Avg60CpuUsageText;
}
if (cpuUsage >= 0.01)
{
PH_FORMAT format;
SIZE_T returnLength;
PhInitFormatF(&format, cpuUsage, 2);
if (PhFormatToBuffer(&format, 1, buffer, PH_INT32_STR_LEN_1 * sizeof(WCHAR), &returnLength))
{
getCellText->Text.Buffer = buffer;
getCellText->Text.Length = (USHORT)(returnLength - sizeof(WCHAR)); // minus null terminator
}
}
else if (cpuUsage != 0 && PhGetIntegerSetting(L"ShowCpuBelow001"))
{
PhInitializeStringRef(&getCellText->Text, L"< 0.01");
}
}
break;
}
}
break;
}
}
LONG NTAPI AvgCpuSortFunction(
_In_ PVOID Node1,
_In_ PVOID Node2,
_In_ ULONG SubId,
_In_ PVOID Context
)
{
PPH_PROCESS_NODE node1 = Node1;
PPH_PROCESS_NODE node2 = Node2;
PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ProcessItem, EmProcessItemType);
PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ProcessItem, EmProcessItemType);
switch (SubId)
{
case COLUMN_ID_AVGCPU10:
return singlecmp(extension1->Avg10CpuUsage, extension2->Avg10CpuUsage);
case COLUMN_ID_AVGCPU60:
return singlecmp(extension1->Avg60CpuUsage, extension2->Avg60CpuUsage);
}
return 0;
}
VOID ProcessTreeNewInitializingCallback(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
PPH_PLUGIN_TREENEW_INFORMATION info = Parameter;
PH_TREENEW_COLUMN column;
memset(&column, 0, sizeof(PH_TREENEW_COLUMN));
column.SortDescending = TRUE;
column.Text = L"CPU Average (10)";
column.Width = 45;
column.Alignment = PH_ALIGN_RIGHT;
column.TextFlags = DT_RIGHT;
PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_AVGCPU10, NULL, AvgCpuSortFunction);
column.Text = L"CPU Average (60)";
PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COLUMN_ID_AVGCPU60, NULL, AvgCpuSortFunction);
}
VOID ProcessItemCreateCallback(
_In_ PVOID Object,
_In_ PH_EM_OBJECT_TYPE ObjectType,
_In_ PVOID Extension
)
{
PPH_PROCESS_ITEM processItem = Object;
PPROCESS_EXTENSION extension = Extension;
memset(extension, 0, sizeof(PROCESS_EXTENSION));
extension->ProcessItem = processItem;
}
VOID ProcessAddedHandler(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
PPH_PROCESS_ITEM processItem = Parameter;
PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, processItem, EmProcessItemType);
InsertTailList(&ProcessListHead, &extension->ListEntry);
}
VOID ProcessRemovedHandler(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
PPH_PROCESS_ITEM processItem = Parameter;
PPROCESS_EXTENSION extension = PhPluginGetObjectExtension(PluginInstance, processItem, EmProcessItemType);
RemoveEntryList(&extension->ListEntry);
}
VOID ProcessesUpdatedHandler(
_In_opt_ PVOID Parameter,
_In_opt_ PVOID Context
)
{
static ULONG runCount = 0;
if (runCount != 0)
{
PLIST_ENTRY listEntry;
listEntry = ProcessListHead.Flink;
while (listEntry != &ProcessListHead)
{
PPROCESS_EXTENSION extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry);
PPH_PROCESS_ITEM processItem = extension->ProcessItem;
FLOAT sum;
ULONG i;
ULONG total;
ULONG count;
if (extension->CpuHistoryPosition != 0)
extension->CpuHistoryPosition--;
else
extension->CpuHistoryPosition = HISTORY_SIZE - 1;
extension->CpuHistory[extension->CpuHistoryPosition] = processItem->CpuUsage;
if (extension->CpuHistoryCount < HISTORY_SIZE)
extension->CpuHistoryCount++;
// Calculate the 10 interval average.
sum = 0;
i = extension->CpuHistoryPosition;
total = 10;
if (total > extension->CpuHistoryCount)
total = extension->CpuHistoryCount;
count = total;
do
{
sum += extension->CpuHistory[i];
i++;
if (i == HISTORY_SIZE)
i = 0;
} while (--count != 0);
extension->Avg10CpuUsage = sum / total;
// Calculate the 60 interval average.
if (extension->CpuHistoryCount > 10)
{
total = 50;
if (total > extension->CpuHistoryCount - 10)
total = extension->CpuHistoryCount - 10;
count = total;
do
{
sum += extension->CpuHistory[i];
i++;
if (i == HISTORY_SIZE)
i = 0;
} while (--count != 0);
extension->Avg60CpuUsage = sum / (total + 10);
}
else
{
// Not enough samples.
extension->Avg60CpuUsage = extension->Avg10CpuUsage;
}
listEntry = listEntry->Flink;
}
}
runCount++;
}
LOGICAL DllMain(
_In_ HINSTANCE Instance,
_In_ ULONG Reason,
_Reserved_ PVOID Reserved
)
{
if (Reason == DLL_PROCESS_ATTACH)
{
PPH_PLUGIN_INFORMATION info;
PluginInstance = PhRegisterPlugin(L"wj32.AvgCpuPlugin", Instance, &info);
if (!PluginInstance)
return FALSE;
info->DisplayName = L"Average CPU";
info->Description = L"Adds a column to display average CPU times.";
info->Author = L"wj32";
PhRegisterCallback(PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage),
TreeNewMessageCallback, NULL, &TreeNewMessageCallbackRegistration);
PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing),
ProcessTreeNewInitializingCallback, NULL, &ProcessTreeNewInitializingCallbackRegistration);
PhRegisterCallback(&PhProcessAddedEvent, ProcessAddedHandler, NULL, &ProcessAddedCallbackRegistration);
PhRegisterCallback(&PhProcessRemovedEvent, ProcessRemovedHandler, NULL, &ProcessRemovedCallbackRegistration);
PhRegisterCallback(&PhProcessesUpdatedEvent, ProcessesUpdatedHandler, NULL, &ProcessesUpdatedCallbackRegistration);
PhPluginSetObjectExtension(PluginInstance, EmProcessItemType, sizeof(PROCESS_EXTENSION),
ProcessItemCreateCallback, NULL);
}
return TRUE;
}