.NET Instrumentation Workshop
Home About Workshops Articles Writing Talks Books Contact

7.4 Providing .NET Performance Counters

As I've mentioned, the perfmon API requires that you use an inter-process communication mechanism to allow another process to read your counters. As a developer you have to provide a DLL that will be loaded into the reader process that provides one endpoint of the IPC and the other endpoint will be in your application. Your application passes the counter data over this IPC and the reader endpoint DLL will then format the data in the nested structures that the API mandates. The good news is that the .NET classes does all this work for you, and make providing counters very simple.

The most difficult part of adding performance counters to your application is deciding what information you will provide. Note that applications choose to read performance data, the data is not forced upon the reader. The application will simply fill the counter with data at whatever rate it decides to use, and the reader application reads the counter at whatever rate the reader decides, if at all. This means that you can provide as many counters as you wish and update them as often as you like without adversely affecting other processes: if anther process wants your data it can have it, but another process that does not want your data does not have to have it. Obviously updating counters does have some affect on the performance of the providing application, but this is the only downside. Contrast this with the event log where a chatty application is unrestricted from filling the (system maintained) log. This gives two downsides: first, the disk file can get very large, and second, the chatty application's messages will swamp messages from other applications, with the possibility of masking important data.

However, you do have to constrain yourself a little. There is little point in providing counters for every intermediate result because they are unlikely to be read and such results represent information leakage which is a security issue. Similarly, there is little point in updating your counter every 10ms if a user will read the counter once a second (this is the minimum read rate in the perfmon application). The extra work to update the counters will have a detrimental affect on the performance of the application and you have to balance the benefit of providing counters against the detrimental affect of providing them. It would be rather ironic for performance counters to have a significant effect on the performance of your application.

Furthermore, it usually makes little sense to use performance counters to show the final results of your application because that is what your presentation layer is for. For example, imagine an application that crawls the internet looking for reviews of a specific product; when this application finds a reference it then determines how many 'points' the review gives the product and displays the name of the site and the review result on the application's window. It makes no sense to put the review result in a counter because a user will not gain any more information from this than they can by looking at the presentation layer of the application. More relevant information to give via a counter would be how many links the crawler had to go through to get to the review page. This is more suitable for a counter because this will give information about how good or bad the crawler algorithm performs. Information like this is of little use to the final user, but it is very useful to an administrator trying to tune the system. Also, don't shy from abstract counters. Values like bytes handled per second, or calculations performed per second, are useful when trying to determine bottlenecks in the system and trying to determine if tuning the system has been successful.

The choice of the type of the counter is a very important decision. The provider process generates raw data, the actual calculation on that data is usually done by CounterSample.Calculate in the client according to the type of the counter. If you make the wrong decision then the wrong calculation will be performed.

Once you have decided what you will provide through counters the actual .NET code to provide those counters is quite simple. However, remember that, so that the perfmon API knows about your performance counters, you must register them with the system. Once you have registered the categories and counters you will have to restart any clients (eg perfmon) so that they can pick up the new perfmon library.  In addition, the general principles of .NET are that if you change a system when installing an application you must revert back to the previous state when the application is uninstalled, so you should also provide a mechanism to unregister the counters when your application is uninstalled. I will explain the best way to do this in a later section, but in the following example I will give a simple, but partial, solution.

Create a file (server.cs) with the following:

using System;
using System.Diagnostics;

class Server
{
   const string catName = "Data Generator";
   const string catHelp = "Generates random sample data";
   const string ctrName = "# Connected Clients";
   const string ctrHelp = "The number of clients connected to the server";

   static void Main(string[] args)
   {
      if (args.Length > 0)
      {
         // Delete category
         if (PerformanceCounterCategory.Exists(catName))
         {
            PerformanceCounterCategory.Delete(catName);
         }
         return;
      }

      if (!PerformanceCounterCategory.Exists(catName))
      {
         // Create new counter if it does not exist
         CounterCreationDataCollection ccdc = new CounterCreationDataCollection();
         CounterCreationData ccd =
            new CounterCreationData(ctrName, ctrHelp,
               PerformanceCounterType.NumberOfItems32);
               ccdc.Add(ccd);
         PerformanceCounterCategory.Create(
            catName, catHelp,
            PerformanceCounterCategoryType.SingleInstance,
            ccdc);
         Console.WriteLine("Category created, re-run the application");
         return;
      }
   }
}

This process can be run in two ways. If no parameter is provided then the application checks for the existence of the category and if it does not exist the category and its counters are created and the process ends. If a parameter is passed (regardless of what that parameter is) then the application assumes that you want to remove the category.

Compile this code (csc server.cs). Note that to install categories and counters your account must be a member of the Administrators group. If this is the case then run the application without a parameter. If perfmon is running, close it. Now run perfmon and open the Add Counters dialog. In the Performance object drop down list you'll see that it now contains the Data Generator object, select this, and you'll see that the # Connected Clients counter will be shown.

Now start the registry editor (regedit). Open the HKEY_LOCAL_MACHINE hive and navigate to the following key:

SOFTWARE\CurrentControlSet\Services

Since perfmon could find the counter it means that somewhere in this key there should be a entry for the application. Since the subkeys have the names of services, the most obvious key name to use would be the name of the process (server), so navigate there and have a look. You'll see that there isn't an entry (if there is such an entry, check its values - in particular ImagePath - to convince yourself that it is not for your application). The next most obvious place to look is a key with the name of the counter's category: Data Generator. You should find a key with this name, and it will have a Performance subkey. Here are some values from my machine:

Library          netfxperf.dll
Open             OpenPerformanceData
Close            ClosePerformanceData
Collect          CollectPerformanceData
Counter Names    # Connected Clients

Since no path is given for the library this indicates that the library must be in the path. The .NET framework folder is not in the path (deliberately and for good reason) so this implies that the library is stored in the %systemroot%\System32 folder. Type the following:

dir %systemroot%\System32\netfxperf.dll

You should find that the file is listed. This is one of the few .NET framework libraries that is installed in the Windows folder (the only other one is mscoree.dll). The reason why .NET files are not installed in the Windows system folder is because it complicates versioning and opens you up to DLL Hell.

I am surprised that this file was not installed in the framework folder and a fully qualified path given in the registry. I am also surprised that there is just one file for all versions of .NET.

The .NET framework does not provide the code to update the perflib key with the names of the counters, categories and help text. Instead it uses a system tool called lodctr (or unlodctr to remove the items). This tool is run with a parameter that is a path to a script file that has information about the changes to be made. During the time that you are adding counter information (and when you remove a category) the framework will obtain a mutex called netfxperf.1.0 so that only one thread in a .NET application on the system can add or remove perfmon registry information at a time. However, it will not protect you from unmanaged applications from changing these values at that time. I think the mutex really should be obtained by lodctr and unlodctr.

Close this dialog and perfmon and then run the application again, but this time provide a parameter (server delete, it really does not matter what you use as the parameter). Now start perfmon and confirm that the object and counter have been removed. Close perfmon.

Now let's add a counter. Remember that PerformanceCounter implements IDisposable, hence once we have finished with the reference we must dispose the object. The class provides the Close method to do this. Add the following static member:

const string ctrRateHelp = "The rate that clients connect/disconnect";

static PerformanceCounter clients;

static void Main(string[] args)

This member will be available in the Main method and in other methods in the class. Now add the following code at the end of the Main method:

Console.CancelKeyPress += CtrlCHandler;

// Provide counters
clients = new PerformanceCounter(catName, ctrName, false);
Random rand = new Random(Environment.TickCount);
int numberOfClients = 5;

Console.WriteLine("Press Ctrl-C to end the server");
while (true)
{
   // Simulate clients attaching and detaching
   int val = rand.Next(100);
   if (val > 66) numberOfClients++;
   if (val < 33) numberOfClients--;
   if (numberOfClients < 0) numberOfClients = 0;
   clients.RawValue = numberOfClients;
   System.Threading.Thread.Sleep(100);
}

Note the constructor that is used for PerformanceCounter. This overload has the category name and the counter name, in addition, there is a third parameter that the documentation calls readOnly. If you use the constructors without this parameter then a default value of true will be used, that is, you will only be able to read the counter through the object. To be able to write to the counter you must provide a value of false for this parameter. (But note that you can only write to custom counters, system provided counters are always read-only.)

The idea behind this code is that there is an everlasting loop that simulates clients attaching and detaching: a third of the time one client will attach, a third of the time one client will detach and a third of the time the number of clients will remain the same. The time period, as you can see, is 100ms. In this example I write the counter value by assigning the RawValue property (which is a long, so that it can be used for both 32- or 64-bit counters). There are three other ways that you can provide counter values: the Increment and Decrement methods will change the current counter value by one, and the IncrementBy method will change the counter by the value that you supply (the parameter is a long so you can provide positive and negative changes.

The code helpfully points out that if the user presses Ctrl-C they will end the loop, and so to catch this we provide a CancelKeyPress handler (note that if you are running .NET 1.1 or 1.0 then you'll have to explicitly create an instance of ConsolCancelEventHandler, the syntax here is for C# 2.0). The handler method looks like this:

static void CtrlCHandler(object o, ConsoleCancelEventArgs a)
{
   if (clients != null) clients.Close();
   Console.WriteLine("Finished");
}

Thus, when the user presses Ctrl-C this handler will be called to dispose the counter.

For this simple example this code is not strictly necessary because without a handler the process will finish and before that happens the objects in the finalizer queue will be disposed. However, it is good practice to ensure that you always dispose IDisposable objects, even in cases like this, because you may decide to extend the application at a later stage, adding more code after the while loop and then you'll allow the counter object to live longer than it should do.

Compile this code and run the process, it will detect that the counters have not been registered and it will register them. Now run the process again and this time it will go into the loop, providing values for the counter. Start perfmon and add the counter. You will find that the counter will rise and fall between zero and approximately 20 (although there will not be any upper limit).

Now close perfmon and stop the application by pressing Ctrl-C. Unregister the counters by running the process with a parameter. Now let's add a counter that gives the rate of change. Add the following strings:

const string ctrHelp = "The number of clients connected to the server";
const string ctrRateName = "# of clients connecting per sec";
const string ctrRateHelp = "The rate that clients connect";

To make the new counter available you have to register it:

ccd = new CounterCreationData(ctrRateName, ctrRateHelp,
   PerformanceCounterType.RateOfCountsPerSecond32);
ccdc.Add(ccd);
PerformanceCounterCategory.Create(
   catName, catHelp,
   PerformanceCounterCategoryType.SingleInstance,
  ccdc);

Now add a static member for the new counter:

static PerformanceCounter clients;
static PerformanceCounter totalClients;

create the counter in the Main method:

clients = new PerformanceCounter(catName, ctrName, false);
totalClients = new PerformanceCounter(catName, ctrRateName, false);

and dispose it in the Ctrl-C handler:

if (clients != null) clients.Close();
if (totalClients != null) totalClients.Close();

The idea is that the counter will give a count of the total number of clients that have ever connected, so the change of this value over time will give the rate of clients connecting. You do not have to calculate the rate because you have indicated that the counter is RateOfCountsPerSecond32.

Add the following lines:

int numberOfClients = 5;
totalClients.RawValue = numberOfClients;

Console.WriteLine("Press Ctrl-C to end the server");
while (true)
{
   // Simulate clients attaching and detaching
   int val = rand.Next(100);
   if (val > 66)
   {
      numberOfClients++;
      totalClients.Increment();
   }

The totalClients counter keeps a running count of the clients that have connected, which is why it is initialized with the first value of numberOfClients (this assumes that before the loop starts in the real application, five clients have already "connected"). In the loop, when a client connects (that is numberOfClients is incremented) the total number of clients is incremented. As you can see the rate is not calculated here. The rate is calculated by the client code, in this case, perfmon. The performance monitor will take consecutive values provided by totalClients and then calculate the difference and divide by the time between the samples. This gives the rate that the total number of clients connecting changes over time.

Compile this code and then run it once to register the counters, then run it again to provide the counters. Start perfmon and add both of the counters for the Data Generator object.

In this picture the current number of clients connected is in blue and the rate that they have connected is in red, and a measurement is taken once every second. Since the horizontal scale is time the gradient of the number of clients connected will give the change and hence the rate of clients connecting and disconnecting (a negative gradient represents clients disconnecting). Performance monitor cannot show a negative value so this is why I chose to give the rate of clients connecting (hence, just the positive values).

If you look at the blue line you'll see that whenever it rises the gradient will increase and hence the rate should increase. This results in the rate (the red line) peaking before the blue line.  However, the more astute of you will know that when the blue line is horizontal, the rate (and hence the red line) should be zero, but thisis not the case here. Further, when clients disconnect, and hence there is a negative gradient in the blue line, the rate of clients connecting (red line) should also be zero, but in this picture this is not the case.

The reason that the rate is apparently not showing the actual rate is because the counters generate values ten times a second, but the fastest that the performance monitor can take a value is once a second. So during the two measurements that perfmon makes there could have been, for example, six clients connected and five disconnected, this would mean that the change in the displayed value for the # connected clients would change by 1 between the two values, but the actual number clients connecting during that second was 6 and hence the # of clients connecting per sec would be 6 (6/1 sec). If it was possible to measure these values at a rate less than one per second and at the same time as the data is generated then you would find that the # of clients connecting per sec would be equivalent to the (positive) gradient of the # connected clients counter.

One way to get a better measure of the number of clients connecting per second is for the application to keep a time stamp when a client connects, and then when the next client connects it can use the difference between the times stamps to calculate the number of connections per second and provide this value though a counter (but note that the type of this counter should be NumberOfItems32 so that the client performs no calculations on the value).

Bear this in mind when you decide to give your users a rate. It is usually better to calculate the rate yourself - because you have all of the values - than to allow the performance counter client to calculate the rate because the client will almost always have just a subset of the values. Further, for this reason, any statistical calculations performed by the client are meaningless.

Close perfmon, stop the application and then run it again with a parameter so that it unregisters its counters.

This example used just a single instance, the category itself is registered as only ever having a single instance with SingleInstance. If you want to have more than one instance then you should create the category as MultiInstance and then when you create the counter use the constructor that takes an instance name. When you have finished with the instance use the RemoveInstance method to remove it. However, since the performance counters use shared memory you have to be careful that a client is not using the instance when you remove it. The safest action is to remove the instance when the application domain that hosts the counter is unloading.

7.5 Performance Counter Installers

The previous example showed how to add and remove categories and counters. However, although these methods worked fine they are inadequate for production applications. The reason is that an application will have several components that need to be registered any one of which can fail and hence the installation mechanism must be transactional. Imagine that you have an application that has performance counters, writes to the event log, and provides enterprise services (that is, integrates with COM+). All of these require registration during installation but if one of these registrations fails then the installation process must rollback all changes. Your application must register its counters to be able to provide them, so there is an incentive to provide registration information. However, there are few incentives (other than professional pride in producing good software) to remove the counters when the application is uninstalled. Before you gasp in disbelief that a developer could behave in anything other than a professional way, take a look in the registry of your machine. Glance through HKEY_CLASSES_ROOT at the progIds and CLSIDs that have been left there after applications have been uninstalled. They are useless, they clutter up the registry and they slow down your machine. So be professional and remove your clutter when you leave.

To help you be a considerate developer the framework provides installers. The key to these is a utility called InstallUtil. This application is simply a wrapper around a class called ManagedInstallerClass in the System.Configuration.Install namespace (and in the assembly with the same name). You call this utility, passing as the command line parameter, the name of the assembly that you want to install. The utility uses reflection to locate and execute installer classes in your assembly. There are a couple of points to make about this. The first is that the assembly that you provide can be a library, or it can be a process. This is the one occasion where you are encouraged to export code from a process.

A general rule of thumb with .NET is that public types are only exported from libraries. Processes are used to host classes but not to export them. The only situation when a process will export a class is when the process provides a remote objects through .NET remoting, web services or WCF.

The other point to make is that a process assembly passed to InstallUtil cannot be written with C++ because the compiler will put unmanaged code in the process. The solution is fairly easy: you simply create a C# assembly that contains the installer class.

InstallUtil will look for a class derived from Installer that has the [RunInstaller(true)] attribute (System.ComponentModel). Here is the public interface of this class.

[DefaultEvent("AfterInstall")]
public class Installer : Component
{
   public event InstallEventHandler AfterInstall;
   public event InstallEventHandler AfterRollback;
   public event InstallEventHandler AfterUninstall;
   public event InstallEventHandler BeforeInstall;
   public event InstallEventHandler BeforeRollback;
   public event InstallEventHandler BeforeUninstall;
   public event InstallEventHandler Committed;
   public event InstallEventHandler Committing;

   public Installer();
   public virtual void Commit(IDictionary savedState);
   public virtual void Install(IDictionary stateSaver);
   public virtual void Rollback(IDictionary savedState);
   public virtual void Uninstall(IDictionary savedState);
   private void WriteEventHandlerError(string severity, string eventName, Exception e);

   [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
   public InstallContext Context { get; set; }
   [ResDescription("Desc_Installer_HelpText")]
   public virtual string HelpText { get; }
   [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
   public InstallerCollection Installers { get; }
   [TypeConverter(typeof(InstallerParentConverter)), Browsable(true),
      ResDescription("Desc_Installer_Parent"),
      DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
   public Installer Parent { get; set; }
}

Notice the properties Parent and Installers. These give a clue about how this class is used. The Installers collection is one or more instances of classes derived from Installer, each of which will have a back reference to the 'parent' installer that holds the collection of installers.

When you create an installer assembly you need to create a top level 'collection' class. To do this you derive a class from Installer and mark it with [RunInstaller(true)] then in the new class's constructor you create instances of the actual installer classes (which are derived, directly, or indirectly, from Installer) and add them to the Installers collection. You can derive your own installer class from Installer, but in general (because it is the easiest thing to do) you should reuse the classes provided by the framework.

ClassDescription
AssemblyInstaller Loads an assembly and runs all the installers that it contains. This is used by the framework.
ComponentInstaller Abstract base class for component installers
DefaultManagementProjectInstaller Default project installer for assemblies that contain management instrumentation and do not use other installers .
EngineInstaller Installs a PowerShell engine
EventLogInstaller Installs event logs
Installer Base class for all installers. Provides the basic infrastructure of maintaining logs and calling installation and uninstallation methods on all installers.
ManagementInstaller Installs instrumented assemblies.
MessageQueueInstaller Installs message queues.
PerformanceCounterInstaller Installs performance counters and categories.
PSInstaller Base class for the PowerShell installers
PSSnapInInstaller Installs PowerShell snap-ins
ServiceInstaller Installs a service
ServiceProcessInstaller Installs a process that contains one or more services
TransactedInstaller Provides extra code to make installation transactional, this is used by the InstallUtil tool which means that you don't have to use it.

As mentioned previously, when the InstallUtil utility is run to install an application it will locate the installer collection class, a class derived from Installer with the [RunInstaller(true)] attribute. However, it does not call your class directly, instead, it creates an instance of TransactedInstaller and adds your installer collection class to this new instance. The difference between Installer and TransactedInstaller is that while both classes keep a record of the installation, including errors, if one of the installers in the Installer class throws an exception the installation will stop, whereas if an installer throws an exception when run by the TransactedInstaller class then the installation will be rolled back with a call to Rollback which will call the Uninstall method of all the installers that have been run.

During installation, the installer collection class will create a Hashtable and pass this to the Install method of each installer in the Installers collection, this parameter is used by the method to indicate the components that have been installed. If an installer fails to do it's work, it will throw an exception. The TransactedInstaller object catches this exception and then calls the Rollback method on each installer passing the Hashtable that was created during the installation so that the installer can undo the work that has done. If the installation is successful on all of the installers, then the TransactedInstaller object calls Commit on all of the installers which can be used to make the installation final. All the methods (Install, Uninstall, Rollback, Commit) have access to an installation context object (an instance of InstallContext) which has information about log files.

Using the InstallUtil tool is simple, just call it passing the name of the assembly with the installer collection class to perform an installation, and add the /u parameter to uninstall the code.

The class for installing performance counters is called PerformanceCounterInstaller, here is the public interface:

public class PerformanceCounterInstaller : ComponentInstaller
{
   public PerformanceCounterInstaller();
   public override void CopyFromComponent(IComponent component);
   public override void Install(IDictionary stateSaver);
   public override void Rollback(IDictionary savedState);
   public override void Uninstall(IDictionary savedState);

   [TypeConverter("System.Diagnostics.Design.StringValueConverter, System.Design, "
                   + "Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"),
    DefaultValue(""), ResDescription("PCCategoryName")]
   public string CategoryName { get; set; }
   [DefaultValue(""), ResDescription("PCI_CategoryHelp")]
   public string CategoryHelp { get; set; }
   [ComVisible(false), DefaultValue(-1), ResDescription("PCI_IsMultiInstance")]
   public PerformanceCounterCategoryType CategoryType { get; set; }
   [DesignerSerializationVisibility(DesignerSerializationVisibility.Content), ResDescription("PCI_Counters")]
   public CounterCreationDataCollection Counters { get; }
   [ResDescription("PCI_UninstallAction"), DefaultValue(0)]
   public UninstallAction UninstallAction { get; set; }
}

You use this class by initializing the CategoryName and CategoryHelp with the appropriate strings and give the CategoryType a value to determine if the category has a single instance or multiple instances. You then create one or more CounterCreationData objects (as you saw earlier) to give information about the counters to install. The UninstallAction property is used to indicate if the counter should be removed when the application is uninstalled and the default vale is that the counters should be removed.

I hope that you enjoy this tutorial and value the knowledge that you will gain from it. I am always pleased to hear from people who use this tutorial (contact me). If you find this tutorial useful then please also email your comments to mvpga@microsoft.com.

Errata

If you see an error on this page, please contact me and I will fix the problem.

Page Eight

This page is (c) 2007 Richard Grimes, all rights reserved