Bug: PerformanceCounterCategory.Delete removes entire registry key

G

Guest

I am attempting to instrument several existing Windows service applications
(written in C#) using the PerformanceCounter* classes in .Net FW 1.1 SP1.
Each service must dynamically add its required counters based on
configuration metadata from SQL Server using the service name as the
Category, and to this end I have the following code snippet:

/// <summary>
/// This method exports the statistics for this process to PerfMon. This
must be called
/// at the end or process initialization so that all statistics for the
process are
/// registered.
/// If this is called multiple times the existing data structure is
replaced by an
/// updated version. This allows, if rather clumsily, counters to be
added after
/// AppManager has completed process initialization.
/// </summary>
/// <returns></returns>
public void ExportStatistics()
{
lock (this)
{
// Remove any hanging or previously-created category information
if (PerformanceCounterCategory.Exists( _perfMonCategoryName ))
{
PerformanceCounterCategory.Delete( _perfMonCategoryName );
}

.... boring details of actual counter setup omitted.

The first call to this method works OK - registry keys named "Linkage" and
"Performance" are added to
HKLM\System\CurrentControlSet\Services\<category-name>, which already exists
because that's the service which is going to publish the counters.

Now, when a revised list of counters needs to be published, the entire
category has to be discarded and recreated, per the following text in MSDN's
description of PerformanceCounterCategory.Delete:

"Remarks
You can delete only custom performance counter categories from the system.
You cannot delete a counter from a category. To do so, delete the category
and recreate the category with the counters you want to retain."

At this point the code snippet above finds .Exists=True and calls .Delete.
I walked through this in the debugger and the entire registry key at
HKLM\System\CurrentControlSet\Services\<category-name/service-name> is
deleted by this method call. This seems like a bug, because it's impossible
under this behaviour to instrument a .Net service with revised Performance
Counters without requiring a full reinstall. This is since my service (and
any other I have ever known) stores its configuration bootstrap data under a
"Parameters" subkey of this now-deleted service Registry placeholder.

Is there a fix for this available or planned? I am sure there are hacks I
could use to get around this (e.g. use a name mapping to put the Category
info in a different place in the Registry from the rest of the service
configuration), but the current behaviour seems clearly incorrect.
 
D

David Browne

Steve Townsend said:
I am attempting to instrument several existing Windows service applications
(written in C#) using the PerformanceCounter* classes in .Net FW 1.1 SP1.
Each service must dynamically add its required counters based on
configuration metadata from SQL Server using the service name as the
Category, and to this end I have the following code snippet:

/// <summary>
/// This method exports the statistics for this process to PerfMon. This
must be called
/// at the end or process initialization so that all statistics for the
process are
/// registered.
/// If this is called multiple times the existing data structure is
replaced by an
/// updated version. This allows, if rather clumsily, counters to be
added after
/// AppManager has completed process initialization.
/// </summary>
/// <returns></returns>
public void ExportStatistics()
{
lock (this)
{
// Remove any hanging or previously-created category information
if (PerformanceCounterCategory.Exists( _perfMonCategoryName ))
{
PerformanceCounterCategory.Delete( _perfMonCategoryName );
}

... boring details of actual counter setup omitted.

The first call to this method works OK - registry keys named "Linkage" and
"Performance" are added to
HKLM\System\CurrentControlSet\Services\<category-name>, which already
exists
because that's the service which is going to publish the counters.

Now, when a revised list of counters needs to be published, the entire
category has to be discarded and recreated, per the following text in
MSDN's
description of PerformanceCounterCategory.Delete:

"Remarks
You can delete only custom performance counter categories from the system.
You cannot delete a counter from a category. To do so, delete the category
and recreate the category with the counters you want to retain."

At this point the code snippet above finds .Exists=True and calls .Delete.
I walked through this in the debugger and the entire registry key at
HKLM\System\CurrentControlSet\Services\<category-name/service-name> is
deleted by this method call. This seems like a bug, because it's
impossible
under this behaviour to instrument a .Net service with revised Performance
Counters without requiring a full reinstall. This is since my service
(and
any other I have ever known) stores its configuration bootstrap data under
a
"Parameters" subkey of this now-deleted service Registry placeholder.

Is there a fix for this available or planned? I am sure there are hacks I
could use to get around this (e.g. use a name mapping to put the Category
info in a different place in the Registry from the rest of the service
configuration), but the current behaviour seems clearly incorrect.

I believe this behavior is changed in .NET 2.0 to delete only the Linkage
and Performance subkeys when the service key has other information in it
too.

David
 
G

Guest

That's good news, thanks.

I worked around this by using a different name for the category and the
service. This is fine for now.

Now I'm hitting another problem. After I Delete the category and re-Create
it, I then add the new counters using code as follows:

// Remove any hanging or previously-created category information
if (PerformanceCounterCategory.Exists( _perfMonCategoryName ))
{
PerformanceCounterCategory.Delete( _perfMonCategoryName );
}

if (_perfMonCategory != null)
{
ApplicationLog.Log( LogType.INFO, 0,
"PerfMon Statistics are being replaced for " +
_perfMonCategoryName);

foreach (PerformanceCounter pc in _perfMonData)
{
pc.Close();
}
PerformanceCounter.CloseSharedResources();

_perfMonData.Clear();
}

// Register the current process with PerfMon as a new category of
counters, with
// one entry in a list of counters per Statistic for the process
CounterCreationDataCollection newCounters = new
CounterCreationDataCollection();

foreach (Statistic st in _statistics)
{
CounterCreationData newCounter = new CounterCreationData(st.PerfMonName,
st.PerfMonName,
PerformanceCounterType.NumberOfItems32 );
newCounters.Add(newCounter);
}

_perfMonCategory = PerformanceCounterCategory.Create(
_perfMonCategoryName,
ColorPaletteHelpString,
newCounters);

// Export the initial values for each counter
foreach (Statistic st in _statistics)
{
PerformanceCounter newData = new PerformanceCounter(
_perfMonCategoryName,
st.Name,
false ); // writable
ApplicationLog.Log( LogType.INFO, 0,
"PerfMon counter:" +
newData.CounterType + " " + newData.CounterHelp);

newData.RawValue = st.iValue;
st.Counter = newData;

_perfMonData.Add( newData );
}
}


In the debugger, the categoryHelp on _perfMonCategory shows up as null
although I am passing in a string ColorPaletteHelpString. In addition, the
Log call to dump newData.CounterType and newData.CounterHelp fails with
exception "Category does not exist", although I can see it in the debugger
after the .Create call just above.

I am beginning to suspect that this area of .Net 1.1 is what is sometimes
referred to as a "bug farm". Is this one also expected to be fixed in .Net
2.0? Are there any other known bugs I need to look out for?

Another comment - it looks to me like the PerformanceCounterCategory class
may use a single named mutex "netfxperf.1.0" to control access to all .Net
custom counters. The SWI team would probably be interested in this, since
these are trivial to squat on and deny access to legitimate users, without
proper security controls.
 
G

Guest

OK, I have a red face. There's a bug in the code I posted here - once I
fixed this the code is working better and publishing data OK. The exception
text is misleading (it's the counter name on PerformanceCounter.Create that's
at fault, not the category) but I still could have checked further. When I
ran under .Net 2.0, the Fwk provides a better exception, and the fix was then
trivial.

Thanks for your help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top