从 C++ 公开的 Windows 性能计数器总是在 perfmon.exe 中产生“无法加载计数器”

Posted

技术标签:

【中文标题】从 C++ 公开的 Windows 性能计数器总是在 perfmon.exe 中产生“无法加载计数器”【英文标题】:Windows performance counters exposed from C++ always yields "Can't load counters" in perfmon.exe 【发布时间】:2015-09-15 17:22:11 【问题描述】:

我正在尝试使用v2.0 of Windows Performance Counters 公开性能数据。我相信我已正确按照说明进行操作,但 perfmon.exe 总是为我的计数器集显示“无法加载计数器”。

这是我存储在名为 PerformanceCounters.xml 的文件中的清单

<?xml version="1.0" encoding="utf-8"?>
<instrumentationManifest xmlns="http://schemas.microsoft.com/win/2004/08/events" xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <instrumentation>
    <counters xmlns="http://schemas.microsoft.com/win/2005/12/counters" schemaVersion="1.1">
      <provider applicationIdentity="D:\temp\ConsoleApplication7\Debug\ConsoleApplication7.exe" providerGuid="84C1D6C9-31BD-4B0F-BED2-F7AF3F24BEB9" symbol="MyPerformanceCounterProvider" providerType="userMode" providerName="MyPerformanceCounterProvider">
        <counterSet uri="MyPerformanceCounterSet" symbol="MyPerformanceCounterSet" guid="50ADA4E1-AD6B-48F0-A1A4-D87D03B8A281" name="MyPerformanceCounterSet" description="MyPerformanceCounterSet" instances="multiple">
          <counter id="1" uri="MyPerformanceCounter1" description="MyPerformanceCounter1" type="perf_counter_large_rawcount" detailLevel="standard" />
        </counterSet>
      </provider>
    </counters>
  </instrumentation>
</instrumentationManifest>

我通过运行创建了一个 .h 和 .rc 文件:

ctrpp PerformanceCounters.xml -o PerformanceCounters.h -rc PerformanceCounters.rc

这是我的测试代码:

#include "PerformanceCounters.h"

int _tmain(int argc, _TCHAR* argv[])

    auto counterInitializeResult = CounterInitialize();
    if (counterInitializeResult == ERROR_SUCCESS)
    
        auto counterSet = PerfCreateInstance(MyPerformanceCounterProvider, &MyPerformanceCounterSetGuid, L"FOO", 1ul);
        if (counterSet != nullptr)
        
            auto setCounterResult = PerfSetULongLongCounterValue(MyPerformanceCounterProvider, counterSet, 1ul, 23);
            if (setCounterResult == ERROR_SUCCESS)
            
                MSG msg;
                while (GetMessage(&msg, NULL, 0, 0)) // message pump is unnecessary but it keeps the process alive
                
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                
            
            PerfDeleteInstance(MyPerformanceCounterProvider, counterSet);
            counterSet = nullptr;
        
        CounterCleanup();
    
    return 0;

我从管理员命令提示符运行以下命令将字符串加载到注册表中:

D:\temp\ConsoleApplication7>lodctr /m:PerformanceCounters.xml

Info: Successfully installed performance counters in D:\temp\ConsoleApplication7\PerformanceCounters.xml

这会将以下内容添加到注册表中:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\84c1d6c9-31bd-4b0f-bed2-f7af3f24beb9]
"ProviderType"=dword:00000000
"ProviderName"="MyPerformanceCounterProvider"
"ApplicationIdentity"=hex(2):44,00,3a,00,5c,00,74,00,65,00,6d,00,70,00,5c,00,\
  43,00,6f,00,6e,00,73,00,6f,00,6c,00,65,00,41,00,70,00,70,00,6c,00,69,00,63,\
  00,61,00,74,00,69,00,6f,00,6e,00,37,00,5c,00,44,00,65,00,62,00,75,00,67,00,\
  5c,00,43,00,6f,00,6e,00,73,00,6f,00,6c,00,65,00,41,00,70,00,70,00,6c,00,69,\
  00,63,00,61,00,74,00,69,00,6f,00,6e,00,37,00,2e,00,65,00,78,00,65,00,00,00
  ^^^^^^^^ This is actually a REG_EXPAND_SZ value that equals "D:\temp\ConsoleApplication7\Debug\ConsoleApplication7.exe"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\84c1d6c9-31bd-4b0f-bed2-f7af3f24beb9\50ada4e1-ad6b-48f0-a1a4-d87d03b8a281]
"NameResource"=dword:00000001
"ExplainResource"=dword:00000003
"InstanceType"=dword:00000002
"First Counter"=dword:0000302e
"NeutralName"="MyPerformanceCounterSet"
"Last Counter"=dword:00003030
"CounterBlock"=hex:01,00,00,00,00,01,01,00,00,00,00,00,00,00,00,00,64,00,00,00,\
  00,00,00,00,ff,ff,ff,ff,05,00,00,00,00,00,00,00,ff,ff,ff,ff,ff,ff,ff,ff,ff,\
  ff,ff,ff,ff,ff,ff,ff,00,00,00,00
"CounterCount"=dword:00000001

我已将上面的 C++ 和生成的 .rc 文件包含在我的 C++ 项目中。当我编译它时,我看到字符串资源确实嵌入在 .exe 中。程序运行良好——没有返回错误,我进入了我的(不必要的)消息循环。

当我运行 perfmon 时,我在列表中看到我的计数器设置为“MyPerformanceCounterSet”。当我的程序运行时,当我选择计数器集时,我会看到我的实例“FOO”。但如果我展开计数器集,我会看到“无法加载计数器”。

我是这台机器的管理员。我也是当地团体Performance Log UsersPerformance Monitor Users 的成员。我已经三重检查了清单中的路径。如果我启用回调(通过清单中的callback="custom"),我将收到对我的回调方法的回调,因为 perfmon 正在收集信息。但它似乎仍然无法枚举计数器。

更新:我在 Windows 7 SDK (C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\winbase\PerfCounters\Basic\CPP) 中找到了一个示例。此示例产生相同的结果——“无法加载计数器”

【问题讨论】:

“无法加载计数器”是一个无用的诊断。编写一个小测试应用程序来读取计数器以获得更好的错误代码。首先确保程序没有运行提升,现在有太多的程序员在运行 VS 提升。 我不应该编写自己的计数器阅读器。 Perfmon 是一个很好的工具。我运行提升的唯一东西是 perfmon.exe 和 lodctr.exe。 为此编写客户端的最简单方法是什么?我刚刚尝试了 PDH 功能,但我卡在了PdhAddCounter 步骤。我无法弄清楚使用什么路径。我更改了清单,为所有名称、描述和 URI 使用更简单的字符串。我使用PdhMakeCounterPath 创建了一个路径,但是无论我为对象名称和计数器名称提供什么字符串,我都会收到来自PdhAddCounter 的错误PDH_CSTATUS_NO_OBJECT 你按照msdn page you linked底部的评论说的做了吗? 是的。这就是我所指的 .rc 文件。 【参考方案1】:

我迟到了,但想回答一下,以防其他人遇到这个问题。当我在为应用程序生成新的性能计数器时遇到同样的问题时,99.9% 的时间是因为我懒惰地从一个清单复制到另一个清单导致冲突时忘记生成新的 guid。要更正此问题,请为您的提供者(providerGuid 属性)和 counterSet(guid 属性)生成新的 guid。这样,当它加载计数器时,它就可以正确地加载。

【讨论】:

在这种情况下,我在构建清单时自己生成了 GUID。请使用 cmets 而不是这些事情的答案。 我尝试了您在更新中使用的相同示例并且遇到了同样的问题。我生成了新的 guid,在 admin 中启动了一个命令提示符,并使用 unlodctr 取消注册,使用 lodctr 对清单重新注册并且没有问题。这对我使用性能计数器的几十个项目来说都没有问题。很抱歉,我试图帮助我和我的同事一直有效的事情。既然这是不久前的事,是什么解决了您的问题?

以上是关于从 C++ 公开的 Windows 性能计数器总是在 perfmon.exe 中产生“无法加载计数器”的主要内容,如果未能解决你的问题,请参考以下文章

我可以从 Windows 中的用户模式程序中读取 CPU 性能计数器吗?

Windows 性能计数器限制

如何检索“平均值”性能计数器?

Windows性能计数器配置应用

windows高性能计数器的分辨率是多少?

C ++从异步线程更新Windows窗口