4

I am trying to expose performance data using v2.0 of Windows Performance Counters. I believe I have followed the instructions correctly, but perfmon.exe always says "Can't load counters" for my counter set.

Here's my manifest that I store in a file called 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>

I create a .h and .rc file by running:

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

Here's my test code:

#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;
}

I run the following from an administrator command prompt to load the strings into the registry:

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

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

This adds the following to the registry:

[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

I have included the C++ from above and the generated .rc file in my C++ project. When I compile it, I see the string resources are indeed embedded in the .exe. The program runs fine -- no errors are returned and I enter my (unnecessary) message loop.

When I run perfmon, I see my counter set "MyPerformanceCounterSet" in the list. When my program is running, I see my instance "FOO" when I select the counter set. But if I expand the counter set, I see "Can't load counters".

I am an admin on this machine. I am also a member of the local groups Performance Log Users and Performance Monitor Users. I have triple-checked the path in the manifest. If I enable callbacks (via callback="custom" in the manifest), I am receiving callbacks to my callback method as perfmon is gathering info. But still it cannot seem to enumerate the counters.

UPDATE: I found a sample in the Windows 7 SDK (C:\Program Files\Microsoft SDKs\Windows\v7.0\Samples\winbase\PerfCounters\Basic\CPP). This sample produces the same result -- "Can't load counters"

Michael Gunter
  • 12,528
  • 1
  • 24
  • 58
  • 3
    "Can't load counters" is a useless diagnostic. Write a little test app to reads the counter to get a better error code. First make sure that the program isn't running elevated, too many programmers are running VS elevated these days. – Hans Passant Sep 15 '15 at 17:36
  • 1
    I shouldn't have to write my own counter reader. Perfmon is a fine tool for that. The only things I'm running elevated are perfmon.exe and lodctr.exe. – Michael Gunter Sep 15 '15 at 17:37
  • What's the simplest way to write a client for this? I just tried the PDH functions, but I got stuck at the `PdhAddCounter` step. I can't figure out what path to use. I changed my manifest to use simpler strings for all names, descriptions and URIs. I created a path using `PdhMakeCounterPath`, but no matter what strings I provide for the object name and counter name, I receive error `PDH_CSTATUS_NO_OBJECT` from `PdhAddCounter`. – Michael Gunter Sep 15 '15 at 18:11
  • Did you do what the comment at the bottom of the [msdn page you linked](https://msdn.microsoft.com/en-us/library/windows/desktop/aa965334.aspx) said to do? – Serdalis Sep 15 '15 at 22:08
  • Yes. That's the .rc file I am referring to. – Michael Gunter Sep 15 '15 at 23:02
  • Have you managed to resolve this eventually? – SomeWittyUsername Apr 23 '20 at 12:12

1 Answers1

-1

I'm way late to the party but wanted to answer in case anyone else runs across this. When I've had the same problem when generating new performance counters for applications, 99.9% of the time it's because I forgot to generate a new guid when I lazily copied from one manifest to another leading to a conflict. To correct this, generate new guids for both your provider (providerGuid attribute) and counterSet (guid attribute). That way when it loads the counters it can do so correctly.

stunf
  • 1
  • In this case, I generated the GUIDs myself when building the manifest. Please use comments instead of answers for such things. – Michael Gunter May 22 '19 at 16:20
  • I tried the same sample you have in your update and had the same problem. I generated new guids, started a command prompt in admin and unregistered using unlodctr, reregistered using lodctr against the manifest and didn't have an issue. This has worked for me for dozens of projects using performance counters without an issue. I'm sorry I tried to help with what has always worked for me and my colleagues. Since this was some time ago what solved your problem? – stunf May 23 '19 at 23:52