Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Measuring DPC time

45 views
Skip to first unread message

Florian

unread,
Feb 20, 2010, 2:23:46 PM2/20/10
to
Hello,

I like to monitor the CPU utilization of a process like the Windows
Task Manager. My first approach was to query the system CPU times with
GetSystemTimes() and the process CPU times with GetProcessTimes() to
compute the CPU utilization of the process. This works fine if the
kernel load of the system is fairly low. If the kernel load is higher
it stops reporting accurate values: the utilization computed with
GetProcessTimes / GetSystemTimes is consistently lower then the
percentage reported by the Windows Task Manager.

Process Explorer reports the same values as GetProcessTimes /
GetSystemTimes (also lower than the Windows Task Manager) however it
was helpful to understand where the system spent the missing time:
Sysinternals Process Explorer shows the difference between the process
utilization reported by the Task Manager and GetProcessTimes /
GetSystemTimes in DPCs (deferred procedure calls).

So the Windows Task Manager knows which portion of the utilization
caused by DPCs belongs to a particular process. Process Explorer only
reports the overall time spent on DPCs and I don't know how to get
this information at all :-)

Could anybody please help me and point me in the right direction?

Thanks a lot,
Florian

Wilson, Phil

unread,
Feb 22, 2010, 6:25:43 PM2/22/10
to
My guess is that neither Task Manager nor Process Explorer use those APIs. I
bet they (and most other analysis tools) use the perfmon calls.

http://msdn.microsoft.com/en-us/library/aa645516(VS.71).aspx

--
Phil Wilson
The Definitive Guide to Windows Installer
http://www.apress.com/book/view/1590592972


"Florian" <myste...@gmail.com> wrote in message
news:d0db2dba-e0d0-4d19...@x1g2000prb.googlegroups.com...

m

unread,
Feb 22, 2010, 8:18:12 PM2/22/10
to
In addition to Phil's advice, you can look at the quasi-documented
NtQuerySystemInformation.

"Wilson, Phil" <ph...@wonderware.nospam.com> wrote in message
news:4445C003-EE6A-408C...@microsoft.com...

Hector Santos

unread,
Feb 23, 2010, 5:15:45 AM2/23/10
to
I am not sure of the question and it seems others as well are not sure.

If you want the CPU utilization and DPC counters, why don't you use
the Performance Counter (PDH) API? Is this the help you are seeking?

I don't see anything in the performance counters that offers a % DPC
for the process itself. I see DPC related counters per CPU and total.

For example (dual core XP):

\\HDEV21\Processor(0)\% DPC Time
\\HDEV21\Processor(1)\% DPC Time
\\HDEV21\Processor(_total)\% DPC Time

To use the PDH API, you basically construct the strings for the
counters and use PdhAddCounter() to add them, and then begin taken
snapshots for the counter values.

Here is a C/C++ class object and console app I quickly snap together
to monitor counters.

-------------------- CUT HERE ----------------------
// File: MonitorPDH.cpp
// Compile: CL MonitorPDH.cpp /W3 /GX /MD /D "_AFXDLL"

#include <stdio.h>
#include <afx.h>
#include <conio.h>
#include <pdh.h>
#pragma comment(lib,"pdh.lib")

//--------------------------------------------------------
// CMonitorPdh
//
// Simple Class to display performance counters

class CMonitorPdh {
public:
CMonitorPdh();
~CMonitorPdh();

public:

//------------------------------------------------------
// Add a counter, add the machine automatically if
// not provided.
//

int AddCounter(const char *szCounter);

//------------------------------------------------------
// Start sampling of counters using a frequency of
// dwSampleMS (default 1000 ms). The sampling stops
// when the CheckExit() virtual function returns TRUE
//

BOOL Start(const DWORD dwSampleMs = 1000);

//------------------------------------------------------
// Add counter strings from a text file

BOOL LoadFromFile(const char *pszFileName);

//------------------------------------------------------
// Clear the counter list

void Clear() { m_saCounters.RemoveAll(); }

//------------------------------------------------------
// Return total counters added

int Count() { return m_saCounters.GetSize(); }

//------------------------------------------------------
// Dump the added counters
//
void DumpCounters();

//------------------------------------------------------
// Get the last pdh status recorded
//

PDH_STATUS GetLastStatus() { return m_pdhStatus; }

public:

//------------------------------------------------------
// Virtual function to exit sampling. For console apps,
// use the keyboard. For a GUI, you can use a PeekMessage()
// with a message detect.
//

virtual BOOL CheckExit();

protected:

BOOL OpenQuery();
BOOL CloseQuery();
BOOL FreeCounterHandles();
BOOL AllocateCounterHandles();

private:
HQUERY m_hQuery;
HCOUNTER *m_hCounters;
PDH_STATUS m_pdhStatus;
CStringArray m_saCounters;
char m_szMach[MAX_PATH];
};

CMonitorPdh::CMonitorPdh()
: m_hQuery(NULL), m_hCounters(NULL), m_pdhStatus(ERROR_SUCCESS)
{
m_szMach[0] = 0;
DWORD dw = sizeof(m_szMach)-1;
GetComputerName(m_szMach,&dw);
}

CMonitorPdh::~CMonitorPdh()
{
FreeCounterHandles();
CloseQuery();
}

BOOL CMonitorPdh::FreeCounterHandles()
{
if (m_hCounters) {
GlobalFree(m_hCounters);
m_hCounters = NULL;
}
return TRUE;
}
BOOL CMonitorPdh::AllocateCounterHandles()
{
FreeCounterHandles();
DWORD nTotal = m_saCounters.GetSize();
if (nTotal == 0) return FALSE;
m_hCounters = (HCOUNTER *)GlobalAlloc(GPTR, nTotal*sizeof(HCOUNTER));
return m_hCounters != NULL;
}

BOOL CMonitorPdh::OpenQuery()
{
if (!CloseQuery()) return FALSE;
m_pdhStatus = PdhOpenQuery (0, 0, &m_hQuery);
return m_pdhStatus == ERROR_SUCCESS;
}

BOOL CMonitorPdh::CloseQuery()
{
if (m_hQuery != NULL) {
m_pdhStatus = PdhCloseQuery(m_hQuery);
return m_pdhStatus == ERROR_SUCCESS;
}
return TRUE;
}

int CMonitorPdh::AddCounter(const char *szCounter)
{
CString s = szCounter;
if (s.Left(2) != "\\\\") {
s.Format("\\\\%s\\%s",m_szMach,szCounter);
}
int i = m_saCounters.Add(s);
return i;
}

void CMonitorPdh::DumpCounters()
{
DWORD nTotal = m_saCounters.GetSize();
for (DWORD i=0; i < nTotal; i++) {
printf("%04d: | %s\n",i,m_saCounters[i]);
}
}

// virtual
BOOL CMonitorPdh::CheckExit()
{
return _kbhit() && (getch() == 27);
}

BOOL CMonitorPdh::Start(const DWORD dwSampleMs /* = 1000 */)
{

if (!OpenQuery()) {
return FALSE;
}

if (!AllocateCounterHandles()) {
return FALSE;
}

DWORD nTotal = Count();

for (DWORD i=0; i < nTotal; i++) {
m_pdhStatus = PdhAddCounter (m_hQuery,
m_saCounters[i],
0,
m_hCounters + i*sizeof(HCOUNTER));
if (m_pdhStatus != ERROR_SUCCESS) {
return FALSE;
}
}

PDH_FMT_COUNTERVALUE fmtValue;
DWORD ctrType;
SYSTEMTIME stSampleTime;
CString sout;

//
// "Prime" counters
//

m_pdhStatus = PdhCollectQueryData (m_hQuery);

// Print counter values until a ESCAPE is pressed.

while (!CheckExit()) {

// Wait one interval.
Sleep(dwSampleMs);

//
// Get the sample time.
//

GetLocalTime (&stSampleTime);

//
// Get the current data values.
//
m_pdhStatus = PdhCollectQueryData (m_hQuery);

//
// Print the time stamp for the sampling.
//
sout.Format("\n\"%2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d.%3.3d\"",
stSampleTime.wMonth,
stSampleTime.wDay,
stSampleTime.wYear,
stSampleTime.wHour,
stSampleTime.wMinute,
stSampleTime.wSecond,
stSampleTime.wMilliseconds);

_tprintf(sout);

//
// Get the current counter values.
//

for (DWORD i = 0; i < nTotal; i++) {
m_pdhStatus = PdhGetFormattedCounterValue(
*(m_hCounters + i*sizeof(HCOUNTER)),
PDH_FMT_DOUBLE,
&ctrType,
&fmtValue);

if (m_pdhStatus == ERROR_SUCCESS) {
sout.Format("%.20g", fmtValue.doubleValue);
} else {
sout = "-1";
}
_tprintf(TEXT(",\"%s\""),sout);
}
}
_tprintf(TEXT("\n"));

FreeCounterHandles();
CloseQuery();

return TRUE;
}

BOOL CMonitorPdh::LoadFromFile(const char *pszFileName)
{


if (!pszFileName || !pszFileName[0]) {
SetLastError(ERROR_BAD_PATHNAME);
return FALSE;
}
CStdioFile f;
if (!f.Open(pszFileName, CFile::modeRead | CFile::typeText)) {
return FALSE;
}

Clear();

CString s;
while (f.ReadString(s)) {
s.TrimLeft(); s.TrimRight();
// skip blank and comment lines
if (s == "") continue;
if (s.FindOneOf(";#") == 0) continue;

// special command to end/stop loading
if (!stricmp(s,"stop")) break;
AddCounter(s);
}
f.Close();
return TRUE;
}

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

CMonitorPdh pdh;

// load string counters from text file
pdh.LoadFromFile("monitorpdh.txt");

// manually add counters
pdh.AddCounter("Process(wcserver)\\% Processor Time");
pdh.AddCounter("Process(wconline)\\% Processor Time");
pdh.AddCounter("Processor(1)\\% DPC Time");

// display counters
pdh.DumpCounters();

// begin sampling with a 250ms sampling rate, press
// ESCAPE to exit

pdh.Start(250);


}
-------------------- CUT HERE ----------------------

Hope this helps

--
HLS

Florian wrote:

--
HLS

Florian

unread,
Feb 23, 2010, 11:59:01 AM2/23/10
to
Thanks a lot for the answers. The problem with the performance counter
APIs is that they are limited to administrators and users in the
performance logs user group:

http://msdn.microsoft.com/en-us/library/aa372183%28VS.85%29.aspx

I would like to do the monitoring for users with standard or even
limited access rights. I wonder if anything can be done to address
this problem?

-Florian

Hector Santos

unread,
Feb 23, 2010, 4:23:35 PM2/23/10
to
Ok, one way to resolve this is to create a user under this group
during installation of your software and then open the process under
this group.

Florian wrote:

--
HLS

0 new messages