来源网站:http://www.ibm.com/developerworks/cn/websphere/techjournal/0310_braithwaite/braithwaite.html#sec4-3
开始时,在访问所需功能的自定义应用程序的开头包含若干头文件是必要的:
#include <windows.h>, #include <pdh.h>, #include <PDHMSG.H>, #include <WINPERF.H>
现在,让我们看一看在使用 PDH获取性能数据时需要包括的 自定义程序的四个基本步骤中的每一个。下面将详细讨论每个部分以及将要执行每项任务的功能查询。
1. 创建查询
查询是计数器的集合,它是用自定义代码创建的,用于管理性能数据的集合。查询用在 PDH函数调用中更新它管理的计数器,因而获取性能数据。创建查询会返回一个句柄,可以用它来访问 PDH函数中的查询。
PdhOpenQuery: 创建一个新的查询。需要两个输入参数,返回一个参数和一个返回代码。样本用法: if( (pdhStatus = PdhOpenQuery( pszDataSource, dwUserData, &hQuery)) == ERROR_SUCCESS)
|
如果成功地开始了此查询,那么它将返回值 ERROR_SUCCESS
;否则将返回错误的代码。
2. 使计数器与查询相关联
PdhAddCounter: 将计数器添加到查询。需要三个输入参数,返回一个参数和一个返回代码。样本用法: if( (pdhStatus = PdhAddCounter( hQuery, szFullCounterPath, dwUserData, &phCounter)) != ERROR_SUCCESS)
|
PdhMakeCounterPath: 创建指向使用 PDH_COUNTER_PATH_ELEMENTS结构的成员的计数器的全路径。需要三个输入参数,返回一个参数和一个返回代码。样本用法: pdhStatus = PdhMakeCounterPath( &spdhCPE, szFullPathBuffer, &dwpcchBufferSize, dwFlags);
|
3. 收集和处理数据
收集:
既然设置了查询和添加了容器,现在就可以开始收集性能数据了。要做到这一点,可以收集原始数据并人工处理它,也可以使用内置的 PDH日志记录函数。
用于人工处理:
PdhCollectQueryData: 检索调用时在查询中指定的所有计数器的原始数据、实时数据。需要一个输入参数,返回一个返回代码。样本用法: if( (pdhStatus = PdhCollectQueryData( hQuery)) != ERROR_SUCCESS)
|
在使用函数来收集数据时,如果不首先以某种方式对收集的数据进行处理,那么它们将会是无效的。例如,如果计数器长时间地监视某些事情(例如,每秒的 I/O数据字节),那么从 PdhCollectQueryData返回的原始值将只是数据字节的运行总数。要获得实际的每秒数据字节,您将必须接受两个样本(来获取初始值和结束值),然后根据样本之间的持续时间来进行区分。(使用 PdhGetFormattedCounterValue 函数可以为您完成这一过程。)
用于日志记录方法:
也可以如上获得相同的数据,并将其直接写入日志文件。要做到这一点,只需打开用于写入的日志,然后在每次收集数据时更新日志就行了。完成这项任务所需的两个函数是 PdhOpenLog和 PdhUpdateLog:
PdhOpenLog: 打开用于写入的日志。需要六个输入参数,返回一个参数和一个返回代码。样本用法: pdhStatus = PdhOpenLog (szLogFileName, dwAccessFalgs, lpdwLogType, hQuery, dwMaxSize, szUserText, pdhLog)
|
PdhUpdateLog: 更新日志。需要两个输入参数,返回一个返回代码。样本用法: pdhStatus = PdhUpdateLog (hLog, dwText))
|
一旦不再需要该日志,就应该使用 PdhCloseLog函数关闭它:
PdhCloseLog: 关闭该日志。需要两个输入参数,返回一个返回代码。样本用法: pdhStatus = PdhCloseLog (hLog, dwFlag)
|
处理:
要处理从 PdhCollectQueryData调用检索的原始数据,就有必要调用 PdhGetFormattedDataValue。在长时间监视计数器时,这尤其重要,因为 PdhCollectQueryData函数返回的是运行的整个数据,而不是每秒的数据。
PdhGetFormattedCounterValue: 检索格式化数据(formatted data)。需要两个输入参数,返回两个参数和一个返回代码。样本用法: pdhStatus = PdhGetFormattedCounterValue( hCounter, dwFormat, &dwValue, pdhValue);此函数接受对 PdhCollectQueryData的最后一次调用返回的样本来执行它的计算。如果是长时间监视计数器,该函数就接受来自对 PdhCollectQueryData的前两次调用的数据。
|
当使用日志记录方法时,可以以多种方式处理数据,这取决于指定的格式。例如,如果数据是采用 CSV格式写入的,那么就可以把日志文件导入电子表格。如何处理日志完全由用户决定。
4. 关闭查询
当查询变得多余时(因为已经收集了全部所需的数据),就可以通过调用 PdhCloseQuery函数来关闭查询。(如果是使用日志,PdhCloseLog也可以关闭查询。)
PdhCloseQuery: 关闭查询。需要一个输入参数,返回一个返回代码。样本用法: pdhStatus = PdhCloseQuery (hQuery)
|
上面概述的函数代表了PDH库中可用的函数的样本,我们用它们说明了可以如何构造自定义应用程序。从 MicrosoftWeb站点可以获得可用函数的完整清单。
样本应用程序
下面是基于本文中所讨论的示例的样本代码。此代码示范了一个非常简单的监视应用程序,其中所有的值都是硬编码的,我们的目的只在于为您编写更复杂的应用程序提供一个良好的开端。下面的代码列出了将写入日志的最后输出。
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <pdh.h>
#include <pdhmsg.h>
int __cdecl _tmain (void)
{ HLOG phLog;
PDH_STATUS pdhStatus;
HCOUNTER phCounter;
DWORD count;
char szFileName[24];
WORD dwUserData = 0;
HQUERY hQuery = NULL;
DWORD logType = PDH_LOG_TYPE_CSV;
CHAR szCounterPath[45]= TEXT("\\Process(calc)\\% Processor Time");
Strcpy ( szFileName,"QuickMonitor.log");
// Open a query.
(pdhStatus = PdhOpenQuery( NULL, 0, &hQuery));
// Add a counter.
pdhStatus = PdhAddCounter( hQuery, szCounterPath,dwUserData,&phCounter);
// Open the log file for write access.
pdhStatus = PdhOpenLog (szFileName, PDH_LOG_WRITE_ACCESS |PDH_LOG_CREATE_ALWAYS ,
&logType, hQuery, 0, NULL, &phLog);
// Capture 10 samples and write them to the log.
for (count = 0; count <= 10; count++) {
pdhStatus = PdhUpdateLog (phLog, TEXT("SomeText."));
Sleep(1000); // Sleep for 1 seconds betweensamples
}
// Close the log and the Query
pdhStatus = PdhCloseLog (phLog, PDH_FLAGS_CLOSE_QUERY);
return 0;
}
在把上面的代码构建成 exe文件并且从命令行运行它之后,就创建了称为 QuickMonitor.log 的日志文件,并且将包含类似于下面这样的数据:
"(PDH-CSV 4.0) (GMT Daylight Time)(-60)","\Process(calc)\% Processor Time"
"09/07/2003 11:21:25.367","5.06732874742129e-008"
"09/07/2003 11:21:26.398","0.98039215686274506"
"09/07/2003 11:21:27.430","0"
"09/07/2003 11:21:28.461","0.97087378640776689"
"09/07/2003 11:21:29.503","3.8461538461538463"
"09/07/2003 11:21:30.554","1.9047619047619049"
"09/07/2003 11:21:31.766","0.82644628099173556"
"09/07/2003 11:21:32.808","6.7307692307692308"
"09/07/2003 11:21:33.859","6.666666666666667"
"09/07/2003 11:21:34.891","5.825242718446602"
"09/07/2003 11:21:35.922","3.8834951456310676"
这个示例中的数据展示了机器 CPU 的 0和 6.7%之间所用的计算进程,不过非常基本并且脱离了上下文。
上面的数据是以 CSV格式显示的,因此可导入电子表格,以更方便地进行分析。然而,所收集的数据最实用的格式将取决于实际问题情形的独特组合、选取的选项和执行分析的用户。