Getting CPU Usage for each core?

mwin

2[H]4U
Joined
Jun 24, 2004
Messages
3,386
I've got a class that gets the CPU usage, so I can display it on my Matrix Orbital LCD. I originally wrote it on a single core machine, so it just gets the overall CPU usage. It still works on my new rig, but I'd like to modify it so it could get the usage for each of the cores. Here's the code, as it exists now:

Code:
BOOL CCpuUsage::InitMonitor(void)
{
	BOOL res;
	DWORD idlLow, idlHigh, kerLow, kerHigh, usrLow, usrHigh;
	FILETIME idleTime, kernelTime, userTime;
	
	res = GetSystemTimes(&idleTime, &kernelTime, &userTime);

	if (res)
	{
		idlLow = idleTime.dwLowDateTime;
		idlHigh = idleTime.dwHighDateTime;
		idlLast = idlHigh;
		idlLast = idlLast << 32;
		idlLast += idlLow;

		kerLow = kernelTime.dwLowDateTime;
		kerHigh = kernelTime.dwHighDateTime;
		kerLast = kerHigh;
		kerLast = kerLast << 32;
		kerLast += kerLow;

		usrLow = userTime.dwLowDateTime;
		usrHigh = userTime.dwHighDateTime;
		usrLast = usrHigh;
		usrLast = usrLast << 32;
		usrLast += usrLow;
	}

	return(res);
}

int CCpuUsage::GetUsage(void)
{
	CString msg;

	DWORD idlLow, idlHigh, kerLow, kerHigh, usrLow, usrHigh;
	__int64 idlTotal, usrTotal, kerTotal, sys, cpu = 0,
		idlDif, usrDif, kerDif;
	FILETIME idleTime, kernelTime, userTime;
	BOOL res;

	res = GetSystemTimes(&idleTime, &kernelTime, &userTime);

	if (res)
	{
		idlLow = idleTime.dwLowDateTime;
		idlHigh = idleTime.dwHighDateTime;
		idlTotal = idlHigh;
		idlTotal = idlTotal << 32;
		idlTotal += idlLow;
		idlDif = idlTotal - idlLast;

		kerLow = kernelTime.dwLowDateTime;
		kerHigh = kernelTime.dwHighDateTime;
		kerTotal = kerHigh;
		kerTotal = kerTotal << 32;
		kerTotal += kerLow;
		kerDif = kerTotal - kerLast;

		usrLow = userTime.dwLowDateTime;
		usrHigh = userTime.dwHighDateTime;
		usrTotal = usrHigh;
		usrTotal = usrTotal << 32;
		usrTotal += usrLow;
		usrDif = usrTotal - usrLast;

		sys = kerDif + usrDif;
		if (sys != 0)
		{
			cpu = ((sys - idlDif) * 100 / sys);
		}

		idlLast = idlTotal;
		usrLast = usrTotal;
		kerLast = kerTotal;
	}
	else
	{
		cpu = -1;
	}

	return((int)cpu);
}

Is there a way to easily modify this to report on multiple cores? Or can somebody point me towards a class that could?
 
Which OS do you have? Are you using Windows? I'm guessing that you are, because the GetSystemTime() API is a Windows API. But maybe it also exists on other OSes.

I don't think there's a single API that will give you all the info from all the cores. You can, however, get these values for each of the physical processors on your machine from the Windows performance counters interface.
 
I put the code below together very quickly, so there's bad error handling and it might leak memory or handles. But it should get you started. On my rig, it prints out like below:

Code:
C:\projects\PerfCounters\debug>perfcounters
Enumerating Processor counters ... 8 counters found
Core    Idle  Kernel    Proc
---- ------- ------- -------
0     99.998   0.000   0.002
1     86.820  12.403  13.180
2     87.595   6.977  12.405
3     99.998   0.000   0.002
4     76.743  23.255  23.257
5     84.494  14.728  15.506
6     82.944  11.628  17.056
7     99.223   0.775   0.777

Core    Idle  Kernel    Proc
---- ------- ------- -------
0     99.998   0.000   0.002
1     82.944  15.504  17.056
2     83.719  10.853  16.281
3     99.998   0.000   0.002
4     77.518  20.930  22.482
5     87.595  10.853  12.405
6     82.944  12.403  17.056
7     97.673   2.326   2.327

Code:
// PerfCounters.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
	PDH_STATUS  pdhStatus               = ERROR_SUCCESS;
    LPTSTR      szCounterListBuffer     = NULL;
    DWORD       dwCounterListSize       = 0;
    LPTSTR      szInstanceListBuffer    = NULL;
    DWORD       dwInstanceListSize      = 0;
    LPTSTR      szThisInstance          = NULL;

	HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, TEXT( "MyEvent" ) );

    // Determine the required buffer size for the data. 
    pdhStatus = PdhEnumObjectItems (
        NULL,                   // real time source
        NULL,                   // local machine
        TEXT("Processor"),        // object to enumerate
        szCounterListBuffer,    // pass NULL and 0
        &dwCounterListSize,     // to get length required
        szInstanceListBuffer,   // buffer size 
        &dwInstanceListSize,    // 
        PERF_DETAIL_WIZARD,     // counter detail level
        0); 

	if ( pdhStatus ==  PDH_MORE_DATA )
	{
        // Allocate the buffers and try the call again.
        szCounterListBuffer = (LPTSTR)malloc (
            (dwCounterListSize * sizeof (TCHAR)));
        szInstanceListBuffer = (LPTSTR)malloc (
            (dwInstanceListSize * sizeof (TCHAR)));

		if ((szCounterListBuffer != NULL) &&
            (szInstanceListBuffer != NULL)) 
        {
            pdhStatus = PdhEnumObjectItems (
                NULL,                 // real time source
                NULL,                 // local machine
                TEXT("Processor"),      // object to enumerate
                szCounterListBuffer,  // buffer to receive counter list
                &dwCounterListSize, 
                szInstanceListBuffer, // buffer to receive instance list 
                &dwInstanceListSize,    
                PERF_DETAIL_WIZARD,   // counter detail level
                0);
     
            if (pdhStatus == ERROR_SUCCESS) 
            {
                _tprintf (TEXT("Enumerating Processor counters ... "));
				int nCount = 0;

                // Walk the instance list. The list can contain one
                // or more null-terminated strings. The last string 
                // is followed by a second null-terminator.
                for (szThisInstance = szInstanceListBuffer;
                     *szThisInstance != 0;
                     szThisInstance += lstrlen(szThisInstance) + 1) 
                {
                     // _tprintf (TEXT("\n  %s"), szThisInstance);
					 if ( 0 != _tcscmp( szThisInstance, TEXT( "_Total" ) ) )
					 {
						 // it's not the toalizer, so count it
						 nCount++;
					 }
                }

				_tprintf( TEXT( "%d counters found\n" ), nCount );

				HCOUNTER *hIdleCounters = new HCOUNTER[nCount];
				HCOUNTER *hPrivelegedCounters = new HCOUNTER[nCount];
				HCOUNTER *hProcessorCounters = new HCOUNTER[nCount];
				HQUERY hQuery;
				pdhStatus = PdhOpenQuery( NULL, 1, &hQuery );

				for ( int n = 0; n < nCount; n++ )
				{
					TCHAR szCounterPath[255];
					_stprintf_s( szCounterPath, 255, TEXT("\\Processor(%d)\\%% Processor Time"), n );
					pdhStatus = PdhAddCounter (hQuery,
						  szCounterPath,
						  n,
						  &hProcessorCounters[n] );
					if ( pdhStatus != ERROR_SUCCESS )
					{
						_tprintf( TEXT("Couldn't add counter \"%s\": 0x8.8X\n" ),
							szCounterPath, pdhStatus );
						break;
					}

					_stprintf_s( szCounterPath, 255, TEXT("\\Processor(%d)\\%% Idle Time"), n );
					pdhStatus = PdhAddCounter (hQuery,
						  szCounterPath,
						  n,
						  &hIdleCounters[n] );
					if ( pdhStatus != ERROR_SUCCESS )
					{
						_tprintf( TEXT("Couldn't add counter \"%s\": 0x8.8X\n" ),
							szCounterPath, pdhStatus );
						break;
					}

					_stprintf_s( szCounterPath, 255, TEXT("\\Processor(%d)\\%% Privileged Time"), n );
					pdhStatus = PdhAddCounter (hQuery,
						  szCounterPath,
						  n,
						  &hPrivelegedCounters[n] );
					if ( pdhStatus != ERROR_SUCCESS )
					{
						_tprintf( TEXT("Couldn't add counter \"%s\": 0x8.8X\n" ),
							szCounterPath, pdhStatus );
						break;
					}
				}

				pdhStatus = PdhCollectQueryDataEx(hQuery, 2, hEvent);

				for (DWORD i = 0; i < 10; i++)
				{
					DWORD dwWaitResult = WaitForSingleObject(hEvent, INFINITE);
					if (WAIT_OBJECT_0 == dwWaitResult)
					{
						// first read always fails, since no previous sample.
						if ( i == 0 )
							continue;

						DWORD dwCounterType = 0;

						_tprintf( TEXT("Core    Idle  Kernel    Proc\n" ) );
						_tprintf( TEXT("---- ------- ------- -------\n" ) );

						for ( int n = 0; n < nCount; n++ )
						{
							PDH_FMT_COUNTERVALUE cvIdle, cvPriveleged, cvProcessor;
							pdhStatus = PdhGetFormattedCounterValue(hIdleCounters[n], PDH_FMT_DOUBLE, &dwCounterType, &cvIdle );
							if ( pdhStatus != ERROR_SUCCESS )
							{
								_tprintf( TEXT( "0: Error 0x%8.8X\n" ), pdhStatus );
								break;
							}

							pdhStatus = PdhGetFormattedCounterValue(hPrivelegedCounters[n], PDH_FMT_DOUBLE, &dwCounterType, &cvPriveleged );
							if ( pdhStatus != ERROR_SUCCESS )
							{
								_tprintf( TEXT( "0: Error 0x%8.8X\n" ), pdhStatus );
								break;
							}

							pdhStatus = PdhGetFormattedCounterValue(hProcessorCounters[n], PDH_FMT_DOUBLE, &dwCounterType, &cvProcessor );
							if ( pdhStatus != ERROR_SUCCESS )
							{
								_tprintf( TEXT( "0: Error 0x%8.8X\n" ), pdhStatus );
								break;
							}

							_tprintf( TEXT( "%-4d %#7.3f %#7.3f %#7.3f\n" ) , n, cvIdle.doubleValue, cvPriveleged.doubleValue, cvProcessor.doubleValue );
						}

						_tprintf( TEXT( "\n" ) );
					}
				}

				delete [] hIdleCounters;
				delete [] hPrivelegedCounters;
				delete [] hProcessorCounters;

				PdhCloseQuery(hQuery);
            }
            else 
            {
                _tprintf(TEXT("\nPdhEnumObjectItems failed with %ld."), pdhStatus);
            }
		}

		free(szCounterListBuffer);
		free(szInstanceListBuffer);

	}
	else 
    {
        _tprintf(TEXT("\nPdhEnumObjectItems failed with %ld."), pdhStatus);
    }

	CloseHandle( hEvent );
	return 0;
}

Let me know how you do.
 
Mike, I'm about to purchase the upcoming Windows Internals 5th Ed. and was wondering if it covered the material in this thread. If not, where's a good place to get started learning Windows programming? My curiosity seems boundless these days, so I appreciate anything you can offer!
 
Windows Internals will cover how performance counters work, but I don't think it has a sample program like this one. I wrote this from the MSDN references.

By the way, you should check your PMs.
 
I put the code below together very quickly, so there's bad error handling and it might leak memory or handles. But it should get you started. On my rig, it prints out like below:
...
Let me know how you do.

Is there another .h file that I need to #include, or a library I need to make sure that I build in? When I first pasted that into what I've got, it gave me a bunch of undeclared identifier errors. I searched around on the net and found some other sample code that used the performance counters. So I snagged the #includes out of it. That handled the undeclared identifiers, but now I get unresolved external errors.

I appreciate the help.

My application is an MFC dialog app and I'm using Visual Studio '08 on Vista Ultimate, btw.
 
Try checking the documentation for the functions you're calling that you're getting error messages on. That should lead you to advice about which headers and libs are necessary. For example, when you look up PdhEnumObjectItems() in MSDN, it shows which headers and libs you need to use.
 
Don't know if this needs to be a Win32 app or not, but the .NET managed framework has a lot of this stuff encapsulated and its very easy to get performance counter info.
 
Try checking the documentation for the functions you're calling that you're getting error messages on. That should lead you to advice about which headers and libs are necessary. For example, when you look up PdhEnumObjectItems() in MSDN, it shows which headers and libs you need to use.

I got it working. I don't know why I couldn't find that the other night. Owned by the MSDN documentation. Too much football and beer, or that's the excuse I'm going with. Thanks again.
 
Back
Top