Monday, May 14, 2007

The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly

This is a case of poor choice of error messages. What it really means is that the Performance Object Category does not exist. The solution is to load the performance counters from the source DLL. In the .Net case, this means running InstallUtil on the file in question. For older perfmon objects, use Lodctr.

Technical details:

If you attempt to load a performance counter with this code (existing Performance Object, non-existant counter):

PerformanceCounter newCounter = new PerformanceCounter("RealCategory","BogusName","Foo",true);

You'll get the very helpful error message: "Counter 'BogusName' does not exist in the specified Category."

If you use this code (non-existant Performance Object):

PerformanceCounter newCounter = new PerformanceCounter("BogusCategory","BogusName","Foo",true);

You'll get a slightly less helpful error message: "Category does not exist." . . . hmm . . would have been nice to know which category.

Now if you use that same call, but flip the readonly flag to false

PerformanceCounter newCounter = new PerformanceCounter("BogusCategory","BogusName","Foo",false);

You get this very unhelpful message: "The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly."

The trick at this point is figuring out who's trying to load a counter, and which one they're trying to load. The following code will locate a PerformanceCounterInstaller within an assembly, given a filename. Easy enough to list all the DLLs in a given space with DIR /S /B *.DLL, then run InstallUtil on the ones that turn up positive.


System.Reflection.Assembly assy = System.Reflection.Assembly.LoadFile(fileName);
Type[] publicTypes = assy.GetExportedTypes();
foreach (Type t in publicTypes)
{
if (t.BaseType.Name == "Installer")
{
Installer o = (Installer)t.Assembly.CreateInstance(t.FullName);
foreach(Installer i in o.Installers)
{
if (i.GetType().Name == "PerformanceCounterInstaller")
{
MessageBox.Show("Performance Counter Installer Found in Assembly " + fileName);
}
}
}
}

4 comments:

  1. As I mentioned in the post, this problem occurs because of an attempt (in code) to create a performance counter object that is not properly registered in Windows.

    The solutions are to: 1) Register the counters using InstallUtil, or 2) Eliminate the code that uses them (if the install source of counters is not available).

    If you didn't write the code, you will have to blindly try to find the counters in question in their DLL. If you can read source code, you can use .Net Reflector to disassemble whatever module is throwing the error (read your stack trace) to find what specific Perfmon object it's attempting to create.

    Odds are high that the counters in question are part of whatever you're trying to run that's having this problem. You can always try running InstallUtil on every DLL you've installed recently. If they were smart, whoever developed the system might have named the DLL containing the install source something with Performance or PerfMon or Counters in the name.

    ReplyDelete
  2. If you are getting this issue on commerce server sites. below is the solution:

    1) Open the Commerce Server 2007 command line prompt
    2) Type csconfig.exe /r Feature.MS.CS.PerfCounters and press Enter
    3) Click next button to reconfigure performance counter on each steps.
    4) Recycle the application pool for all applications which use the commerce server.

    ReplyDelete
  3. Sanoj, you saved my day.

    ReplyDelete
  4. The article is very helpful to me. My Windows service has its own custom counters installed during setup. However, an external library referenced in the Windows service has additional counters and those counters had not been installed on new host, which causes problem. The lesson is copying a dll to application is sometimes not enough. Thanks!

    ReplyDelete