Process CPU and memory usage monitor

Started by MichaelW, July 14, 2009, 04:14:01 AM

Previous topic - Next topic

MichaelW

This is implemented as a small, simple static library that runs a separate thread to periodically collect the necessary information, calculate the CPU and memory usage, and pass them to a user-supplied callback. Instead of dealing with the complexities of PDH or a lower-level API, this code derives the CPU usage from the GetProcesssTimes function and the memory usage from the GetProcessMemoryInfo function. The test app is intended as a simple method of providing a controllable CPU usage, so the usage returned by the library can be compared to the usage reported by Task Manager and Process Monitor, across a range of values.

Edit:  Added the monitor.inc that I left out of the attachment.

[attachment deleted by admin]
eschew obfuscation

d0d0


ecube

Nice work :) seems pretty accurate, is better than the slow crappy pdh code I got somewhere.

BasilYercin

nice and easy to adapt... i wanted the page fault counter to be available and that costed me few minutes to include.
many thanks :U

evlncrn8

doesn't report cpu usage for each core though.. does it?

MichaelW

I'm not sure what it would report running on a multi-core system because I have no multi-core hardware available (or at least not conveniently available). I think it could be restricted to running on a given core with SetProcessAffinityMask. MonitorInit already gets the process handle, so the modification could be as simple as adding an affinity mask parameter and passing it to SetProcessAffinityMask.
eschew obfuscation

dedndave

you can restrict execution to a single core by using the Task Manager (right-click on the process - Set Affinity)

evlncrn8

thats not what i meant.. i meant its reporting overall cpu usage, not usage per core :)

dedndave

i knew that, of course
i meant for a simple quick test of the function
i don't have to tell Michael how to use SetProcessAffinityMask - lol
but, many do not know you can set it manually

EDIT
he has a single-core machine and may want some help testing

MichaelW

The Windows 2000 Task Manager allows you to control the process priority, but there is no support for the affinity mask.
eschew obfuscation

dedndave

it may be that it just doesn't show up on a single-core, Michael

evlncrn8

correct...

SetProcessAffinityMask Function

Minimum supported client   Windows 2000 Professional
Minimum supported server   Windows 2000 Server
Header   Winbase.h (include Windows.h)
Library   Kernel32.lib
DLL   Kernel32.dll

you won't see affinity stuff unless you have >1 core's, which explains why you won't see it in task manager
taskmanager can definately show cpu usage per core, just i think its undocumented, in my own code i can
see the overall cpu utilization but not a core by core breakdown.. and i've yet to find the api for it, knowing
my luck its probably undocumented...

if i find it i'll post here :)

dedndave

good luck evlncrn8  :U
so far, i haven't seen any software that breaks it down that way - i'll keep my eyes open

evlncrn8

just figured it out :)

NtQuerySystemInformation using SystemProcessorPerformanceInformation class (0x08) is the key...
the struct you pass should be sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*sysinf.dwNumberOfProcessors)

where sysinf.dwNumberOfProcessors is obtained from a GetSystemInfo call

this returns the timing for all the processors on the system (using the size of the struct *1 will just return processor #0.. and none of the others)...
easiest way would also be to make a buffer of sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) *32
(cos there can only be 32 max processors afaik.. course, this may change in the future)...

then you do the processing as normal

call once, fill an 'old' struct.. call again, filling the 'new' struct'

do the math on the differential between each one...
advance the struct pointers to read the next data for the processor(s)...
loop.. repeat with a little sleep inbetween...

job done :) now im off to update my code in protection id so it reports each core usage

oh and taskmanager breaks it down that way... just required a little bit of reversing and figuring out how it did its stuff

here's the proof of concept code.. its in c.. prototype.. but works.. .needs some tweaking but it shows how to do it



//---------

#include "stdio.h"
#include "windows.h"

//---------

typedef DWORD (__stdcall *LPFN_NtQuerySystemInformation)(DWORD, PVOID, DWORD, PDWORD);

#define SystemProcessorPerformanceInformation 0x8

//---------

#pragma pack(push,8)

typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
    LARGE_INTEGER IdleTime;
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER DpcTime;
    LARGE_INTEGER InterruptTime;
    ULONG InterruptCount;
} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION,
*PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;

#pragma pack(pop)

//---------

int main(int argc, char* argv[]) {

SYSTEM_INFO   systeminfo;
unsigned long bytesreturned;

SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION spi_old[32];
memset(spi_old,0,sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*32);

SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION spi[32];
memset(spi,0,sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*32);

LPFN_NtQuerySystemInformation ntquerysysteminformation =(LPFN_NtQuerySystemInformation) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");

if(!ntquerysysteminformation) {

  printf("\n*** no ntquerysysteminformation api?.. bugger");
  return -1;
}

GetSystemInfo(&systeminfo);

printf("\n[i] Processor Count: %d\n",systeminfo.dwNumberOfProcessors);

ntquerysysteminformation(SystemProcessorPerformanceInformation,spi_old, (sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*systeminfo.dwNumberOfProcessors),&bytesreturned);

Sleep(500);

ntquerysysteminformation(SystemProcessorPerformanceInformation,spi, (sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)*systeminfo.dwNumberOfProcessors),&bytesreturned);

for(int cpuloopcount = 0; cpuloopcount < systeminfo.dwNumberOfProcessors; cpuloopcount++) {

BYTE cpuusage = (BYTE) (100 - (((spi[cpuloopcount].IdleTime.QuadPart - spi_old[cpuloopcount].IdleTime.QuadPart) * 100) /  \
((spi[cpuloopcount].KernelTime.QuadPart +  spi[cpuloopcount].UserTime.QuadPart) - (spi_old[cpuloopcount].KernelTime.QuadPart + spi_old[cpuloopcount].UserTime.QuadPart))));

printf("\n[i] CPU %02d: = %0d%%",cpuloopcount, cpuusage);

}

  return 0;
}



rename .zip to .rar then unrar it.. dunno why rar's arent supported here but i really couldn't be bothered making a zip...


[attachment deleted by admin]

BlackVortex

Cool stuff, good work evil incarnate   :toothy