Windows性能计数器相关基础(一)

来源网站: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)
输入参数:
pszDataSource 字符串,它引用要从中读取数据的日志文件。对于实时数据捕获,请指定 NULL 值。
dwUserData 与此查询相关的用户定义的值。它可以是识别此查询惟一的值,而如果没有分配值,则 C++ WORD/DWORD数据类型的 0 也将是足够的。
返回的参数:
hQuery 指向已创建的查询的句柄。这个指针在随后的任何 PDH函数调用中都是必需的。

如果成功地开始了此查询,那么它将返回值 ERROR_SUCCESS ;否则将返回错误的代码。

2. 使计数器与查询相关联
 
PdhAddCounter:
将计数器添加到查询。需要三个输入参数,返回一个参数和一个返回代码。样本用法: 
if( (pdhStatus = PdhAddCounter( hQuery, szFullCounterPath, dwUserData, &phCounter)) != ERROR_SUCCESS)
输入参数:
hQuery 指向您想将此计数器添加到的查询的句柄。
szFullCounterPath 指向计数器的路径的指针。在传送此参数之前,必须首先用计数器的详细资料对其进行初始化,为了做到这一点,可以创建 PDH_COUNTER_PATH_ELEMENTS 数据结构,它包含机器、对象、计数器和实例的名称,如下所示: 
PDH_COUNTER_PATH_ELEMENTS spdhCPE; 
spdhCPE.szMachineName = "Some Value"; 
spdhCPE.szObjectName = "Some Value"; 
spdhCPE.szInstanceName = "Some Value"; 
spdhCPE.szCounterName = "Some Value"; 然后,将此数据结构传送到下面的 PdhMakeCounterPath函数调用中。
dwUserData 与此查询相关的用户定义的值。它可以是识别此查询惟一的值,而如果没有分配值,则 C++ WORD/DWORD数据类型的 0 也将是足够的。
Returned Parameters:
phCounter 指向已创建的查询的句柄。
 
PdhMakeCounterPath: 
创建指向使用 PDH_COUNTER_PATH_ELEMENTS结构的成员的计数器的全路径。需要三个输入参数,返回一个参数和一个返回代码。样本用法: 
pdhStatus = PdhMakeCounterPath( &spdhCPE, szFullPathBuffer, &dwpcchBufferSize, dwFlags);
输入参数:
spdhCPE 指向 PDH_COUNTER_PATH_ELEMENTS 的指针。
dwpcchBufferSize 如果函数调用成功,就将此参数的值设置为可用缓冲器的大小。要不然,就将它设置为所需的缓冲器的大小*。因而,此参数可以是输入参数,也可以是输出参数。 

(*分析此参数的值并且相应地扩展缓冲器可能是必要的。)
dwFlags 指定计数器值的格式。它可以是:
  • PDH_PATH_WBEM_RESULT:以 WMI格式返回结果。
  • PDH_PATH_WBEM_INPUT:假定输入值采用 WMI格式。
  • 0:以注册路径项的列表的形式返回结果。
返回参数:
szFullPathBuffer 指向将要在 PdhAddCounter函数中使用的计数器的全路径。
dwpcchBufferSize 参见上面的 dwpcchBufferSize 输入参数。

3. 收集和处理数据

收集:

既然设置了查询和添加了容器,现在就可以开始收集性能数据了。要做到这一点,可以收集原始数据并人工处理它,也可以使用内置的 PDH日志记录函数。

用于人工处理:

 
PdhCollectQueryData:
检索调用时在查询中指定的所有计数器的原始数据、实时数据。需要一个输入参数,返回一个返回代码。样本用法: 
if( (pdhStatus = PdhCollectQueryData( hQuery)) != ERROR_SUCCESS)
输入参数:
hQuery 指向您想要从中收集数据的查询的句柄。

在使用函数来收集数据时,如果不首先以某种方式对收集的数据进行处理,那么它们将会是无效的。例如,如果计数器长时间地监视某些事情(例如,每秒的 I/O数据字节),那么从 PdhCollectQueryData返回的原始值将只是数据字节的运行总数。要获得实际的每秒数据字节,您将必须接受两个样本(来获取初始值和结束值),然后根据样本之间的持续时间来进行区分。(使用 PdhGetFormattedCounterValue 函数可以为您完成这一过程。)

用于日志记录方法:

也可以如上获得相同的数据,并将其直接写入日志文件。要做到这一点,只需打开用于写入的日志,然后在每次收集数据时更新日志就行了。完成这项任务所需的两个函数是 PdhOpenLog和 PdhUpdateLog:

 
PdhOpenLog:
打开用于写入的日志。需要六个输入参数,返回一个参数和一个返回代码。样本用法: 
pdhStatus = PdhOpenLog (szLogFileName, dwAccessFalgs, lpdwLogType, hQuery, dwMaxSize, szUserText, pdhLog)
输入参数:
szLogFileName 字符串,它表示要创建的日志文件的名称/路径。
dwAccessFlags 所需的对日志文件的访问级别。这些值可以是:
  • PDH_LOG_READ_ACCESS(读取)
  • PDH_LOG_WRITE_ACCESS(写入)
  • PDH_LOG_UPDATE_ACCESS(打开的用于写入的现有日志)
需要使用 OR运算符与下面的某个值组合的读取、写入和更新标志:
  • PDH_LOG_CREATE_NEW(已创建的新日志文件)
  • PDH_LOG_CREATE_ALWAYS(清除任何具有相同名称的现有日志)
  • PDH_LOG_OPEN_EXISTING(打开一个现有的日志文件,如果它不存在的话,就创建一个新的日志文件)
  • PDH_LOG_OPEN_ALWAYS(打开一个现有的日志文件或创建一个新的日志文件)
lpdwLogType 要打开的日志的格式。这个值可以是:
  • PDH_LOG_TYPE_UNDEFINED(未定义的)
  • PDH_LOG_TYPE_CSV(日志有列头,后面紧跟着数据值;值是用双引号和逗号隔开的)
  • PDH_LOG_TYPE_SQL(SQL 格式的数据)
  • PDH_LOG_TYPE_TSV(日志有列头,后面紧跟着数据值;值是用双引号和标记隔开的)
  • PDH_LOG_TYPE_BINARY(二进制格式)
  • PDH_LOG_TYPE_PERFMON(性能监视器用来存储数据的只读格式;在本质上与二进制是相同的,不过缺少有效的空格方式)
hQuery 指向在其上打开日志的查询的句柄。
dwMaxSize 最大的日志文件大小。
szUserText 用于描述日志文件的内容的字符串。
返回参数:
pdhLog 指向日志文件的句柄。
 
PdhUpdateLog:
更新日志。需要两个输入参数,返回一个返回代码。样本用法: 
pdhStatus = PdhUpdateLog (hLog, dwText))
输入参数:
hLog 指向要更新的日志文件得句柄。
dwText 指定要添加到日志文件的任何附加文本,比如用户想要在性能数据之外添加的注释。

一旦不再需要该日志,就应该使用 PdhCloseLog函数关闭它:

 
PdhCloseLog: 
关闭该日志。需要两个输入参数,返回一个返回代码。样本用法: 
pdhStatus = PdhCloseLog (hLog, dwFlag)
输入参数:
hLog 指向要关闭的日志文件的句柄。
dwFlag 可以设置为 PDH_FLAGS_CLOSE_QUERY,在这种情况下,查询与日志是同时关闭的。

处理:

要处理从 PdhCollectQueryData调用检索的原始数据,就有必要调用 PdhGetFormattedDataValue。在长时间监视计数器时,这尤其重要,因为 PdhCollectQueryData函数返回的是运行的整个数据,而不是每秒的数据。

 
PdhGetFormattedCounterValue: 
检索格式化数据(formatted data)。需要两个输入参数,返回两个参数和一个返回代码。样本用法: 
pdhStatus = PdhGetFormattedCounterValue( hCounter, dwFormat, &dwValue, pdhValue);此函数接受对 PdhCollectQueryData的最后一次调用返回的样本来执行它的计算。如果是长时间监视计数器,该函数就接受来自对 PdhCollectQueryData的前两次调用的数据。
输入参数:
hCounter 指向将要格式化其值的的计数器的句柄。
dwFormat 指定以哪一种格式返回数据:
  • PDH_FMT_DOUBLE(双精度浮点型)
  • PDH_FMT_LARGE(64位整型)
  • PDH_FMT_LONG(长整型)
返回参数:
&dwValue 返回计数器的类型(比如文本或数值(可选))的指针。
pdhValue 指向包含计数器值的格式化计数器数据结构的指针。

当使用日志记录方法时,可以以多种方式处理数据,这取决于指定的格式。例如,如果数据是采用 CSV格式写入的,那么就可以把日志文件导入电子表格。如何处理日志完全由用户决定。

4. 关闭查询

当查询变得多余时(因为已经收集了全部所需的数据),就可以通过调用 PdhCloseQuery函数来关闭查询。(如果是使用日志,PdhCloseLog也可以关闭查询。)

 
PdhCloseQuery: 
关闭查询。需要一个输入参数,返回一个返回代码。样本用法: 
pdhStatus = PdhCloseQuery (hQuery)
输入参数:
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格式显示的,因此可导入电子表格,以更方便地进行分析。然而,所收集的数据最实用的格式将取决于实际问题情形的独特组合、选取的选项和执行分析的用户。



原文地址:https://www.cnblogs.com/lovelyx/p/4867149.html