[ Team LiB ] Previous Section Next Section

19.6 Performance Counters

Event logs are useful for capturing application status that is not of a time-sensitive nature, yet needs to be recorded for future analysis. However, to gain insight into the current state of an application (or the system as a whole), a more real-time approach is needed.

The Win32 solution to this need is the performance-monitoring infrastructure, which consists of a set of performance counters that the system and applications expose, and the Microsoft Management Console (MMC) snap-ins used to monitor these counters in real time.

Performance counters are grouped into categories such as "System," "Processor," ".NET CLR Memory," and so on. These categories are sometimes also referred to as "performance objects" by the GUI tools. Each category groups a related set of performance counters that monitor one aspect of the system or application. Examples of performance counters in the ".NET CLR Memory" category include "% Time in GC," "# Bytes in All Heaps," and "Allocated bytes/sec."

Each category may optionally have one or more instances that can be monitored independently. For example, this is useful in the "% Processor Time" performance counter in the "Processor" category, which allows one to monitor CPU utilization. On a multiprocessor machine, this counter supports an instance for each CPU, allowing one to monitor the utilization of each CPU independently.

The following sections illustrate how to perform commonly needed tasks, such as determining which counters are exposed, monitoring a counter, and creating your own counters to expose application status information.

19.6.1 Enumerating the Available Counters

In the following example, enumerate all of the available performance counters on the system. To do this, first retrieve a list of the categories, then iterate through the list displaying the counters and instances for each category, as follows:

// DumpCounters.cs
using System;
using System.Diagnostics;
class DumpCounters {
  static void Main( ) {
    PerformanceCounterCategory[ ] cats;
    cats = PerformanceCounterCategory.GetCategories( );
    foreach (PerformanceCounterCategory cat in cats) {
      try {
        Console.WriteLine("Category: {0}", cat.CategoryName);
        string[ ] instances = cat.GetInstanceNames( );
        if (instances.Length =  = 0) {
          // Dump counters without instances
          foreach (PerformanceCounter ctr in cat.GetCounters( ))
            Console.WriteLine("  Counter: {0}", ctr.CounterName);
        } else {
          // Dump counters with instances
          foreach (string instance in instances) {
            Console.WriteLine("  Instance: {0}", instance);
            foreach (PerformanceCounter ctr in cat.GetCounters(instance))
              Console.WriteLine("    Counter: {0}", ctr.CounterName);
          }    
        }
      } catch (Exception e) { // Some perf counters don't provide details
        Console.WriteLine("Exception: {0}", e.Message);
      }   
    }
  }
}

19.6.2 Reading Performance Counter Data

The following sample demonstrates how to retrieve the current value for an arbitrary performance counter. It incorporates error-handling code to illustrate how to verify category, counter, and instance name before attempting to read a performance counter value. Use the preceding DumpCounters sample to enumerate the available categories, counters, and instances on your machine, and pass them to the sample on the command line:

// WatchCounter.cs - use WatchCounter <category> <counter> [<instance>]
using System;
using System.Threading;
using System.Diagnostics;
class WatchCounter {
  static void Main(string[ ] args) {
    // Extract the parameters
    string cat = args[0], ctr = args[1];
    string inst = (args.Length=  =3) ? args[2] : "";
    // Check the category is OK
    if (!PerformanceCounterCategory.Exists(cat)) {
      Console.WriteLine("Unknown category {0}", cat);
      return;
    }
    // Check the counter is OK
    if (!PerformanceCounterCategory.CounterExists(ctr, cat)) {
      Console.WriteLine("Unknown counter {0}", ctr);
      return;
    }
    // Check the instance is OK
    if (inst.Length>0 && 
        !PerformanceCounterCategory.InstanceExists(inst, cat)) {
      Console.WriteLine("Unknown instance {0}", inst);
      return;
    }
    // Spin in a loop, dumping the counter
    Console.WriteLine("Press <ctrl+c> to end");
    PerformanceCounter pc = new PerformanceCounter(cat, ctr, inst);
    while (true) {
      Console.WriteLine("Counter value is {0}", pc.NextValue( ));
      Thread.Sleep(1000); // No! Bad programmer!
    }
  }
}

19.6.3 Adding a New Performance Counter

To expose real-time status information about your applications, instantiate a read/write PerformanceCounter object with the name of an existing category, the name of the new counter, and the name of the new instance, then update the value of the counter as the application status changes. The following sample demonstrates how to create your own counter and expose application status values through it. (To watch the changing counter values, use the preceding WatchCounter sample.)

// WriteCounter.cs - use WriteCounter <category> <counter> [<instance>]
using System;
using System.Threading;
using System.Diagnostics;
class WriteCounter {
  static void Main(string[ ] args) {
    // Extract the parameters
    string cat = args[0], ctr = args[1];
    string inst = (args.Length=  =3) ? args[2] : "";
    // Create the category if it doesn't already exist
    if (!PerformanceCounterCategory.Exists(cat)) {
      PerformanceCounterCategory.Create(cat, "", ctr, "");
    }
    // Check the counter is OK
    if (!PerformanceCounterCategory.CounterExists(ctr, cat)) {
      Console.WriteLine("Unknown counter {0}", ctr);
      return;
    }
    // Create a new read/write counter & instance for an existing category
    PerformanceCounter pc = new PerformanceCounter(cat, ctr, inst, false);
    // Create counter and spin in a loop, incrementing it
    Console.WriteLine("Press <ctrl+c> to end");
    while (true) {
      Console.WriteLine("Incrementing counter...");
      pc.Increment( );
      Thread.Sleep(1000); // No! Bad programmer!
    }
  }
}
    [ Team LiB ] Previous Section Next Section