As mentioned in my “Hello, World” post, performance counters are a great diagnostic tool for starting performance investigations. Logging performance counters during your runs does not add much overhead, unlike profilers. Still, this is not a replacement for profilers which are necessary for in-depth investigations. The following are some tools you can use to collect performance counters.

Performance Monitor

Windows ships with the Performance Monitor tool, or PerfMon for short. This GUI allows you to view live performance counter data on either the local machine or on remote systems. Simply run `perfmon` and navigate to Performance->Monitoring Tools->Performance Monitor. Right click to add or remove counters. Press ‘Ctrl+G’ to switch the graph type.  Report View is handy when monitoring multiple counters.

Perfecto and LogMan

Perfecto is a Data Collector Set that plugs into PerfMon, and was created by Jose Reyes. This is a quick, simple way to collect ASP.NET, IIS, CLR and system counters which give a good overview of ASP.NET performance.

LogMan is a command line tool for managing PerfMon Data Collector Sets.

Try the following:

  1. Download Perfecto, unzip and run the setup script
  2. Run `logman start –n “Service\ASPNET Perfecto”`
  3. Make a few web requests to localhost
  4. Run `logman stop –n “Service\ASPNET Perfecto”`
  5. View the report at "%SystemDrive%\PerfLogs\Admin\ASPNET Perfecto\000001\report.html”

Don’t be afraid to customize Perfecto to your needs: add performance counters, change the report directory, etc.  Right click ‘ASPNET Perfecto’ to edit the properties in PerfMon.  Right click again and Save Template… to create an updated version of Perfecto.DataCollectorSet.xml.

TypePerf and TList

For basic counter collections, you can use the TypePerf command-line tool. It can be combined with the TList command in order to collect counters which are specific to a given process. For example, you can collect the Processor Time for all processes by running:

`for /t “tokens=2 delims=. “ %a in (‘tlist’) do typeperf –sc 1 “Process(%a)\% Processor Time”

WCat

WCat also supports the collection of performance counters.  This is our primary way of collecting performance counters since most of our scenarios use WCat.  Take a look at our settings.ubr file to see which counters we track:

settings
{
    counters
    {
        interval = 10;

        counter = ".NET CLR Exceptions(w3wp)\\# of Exceps Thrown / sec";
        counter = ".NET CLR Jit(w3wp)\\% Time in Jit";
        counter = ".NET CLR Jit(w3wp)\\IL Bytes Jitted / sec";
        counter = ".NET CLR Loading(w3wp)\\% Time Loading";
        counter = ".NET CLR Loading(w3wp)\\Current appdomains";
        counter = ".NET CLR Loading(w3wp)\\Current Assemblies";
        counter = ".NET CLR LocksAndThreads(w3wp)\\# of current logical Threads";
        counter = ".NET CLR LocksAndThreads(w3wp)\\# of current physical Threads";
        counter = ".NET CLR LocksAndThreads(w3wp)\\# of current recognized threads";
        counter = ".NET CLR LocksAndThreads(w3wp)\\# of total recognized threads";
        counter = ".NET CLR LocksAndThreads(w3wp)\\Queue Length / sec";
        counter = ".NET CLR LocksAndThreads(w3wp)\\Total # of Contentions";
        counter = ".NET CLR LocksAndThreads(w3wp)\\Contention Rate / sec";
        counter = ".NET CLR LocksAndThreads(w3wp)\\Current Queue Length";
        counter = ".NET CLR Memory(w3wp)\\% Time in GC";
        counter = ".NET CLR Memory(w3wp)\\# Bytes in all Heaps";
        counter = ".NET CLR Memory(w3wp)\\# Gen 0 Collections";
        counter = ".NET CLR Memory(w3wp)\\# Gen 1 Collections";
        counter = ".NET CLR Memory(w3wp)\\# Gen 2 Collections";
        counter = ".NET CLR Memory(w3wp)\\# Induced GC";
        counter = ".NET CLR Memory(w3wp)\\Allocated Bytes/sec";
        counter = ".NET CLR Memory(w3wp)\\Finalization Survivors";
        counter = ".NET CLR Memory(w3wp)\\Gen 0 heap size";
        counter = ".NET CLR Memory(w3wp)\\Gen 1 heap size";
        counter = ".NET CLR Memory(w3wp)\\Gen 2 heap size";
        counter = ".NET CLR Memory(w3wp)\\Large Object Heap size";
        counter = ".NET CLR Memory(w3wp)\\Large Object Heap size";
        counter = ".NET CLR Memory(w3wp)\\# of Pinned Objects";
        counter = ".NET CLR Security(w3wp)\\% Time in RT checks";
        counter = ".NET CLR Security(w3wp)\\Stack Walk Depth";
        counter = ".NET CLR Security(w3wp)\\Total Runtime Checks";
        counter = "ASP.NET Applications(__Total__)\\Cache Total Entries";
        counter = "ASP.NET Applications(__Total__)\\Cache Total Hit Ratio";
        counter = "ASP.NET Applications(__Total__)\\Cache Total Turnover Rate";
        counter = "ASP.NET Applications(__Total__)\\Output Cache Entries";
        counter = "ASP.NET Applications(__Total__)\\Output Cache Hits";
        counter = "ASP.NET Applications(__Total__)\\Output Cache Hit Ratio";
        counter = "ASP.NET Applications(__Total__)\\Output Cache Turnover Rate";
        counter = "ASP.NET Applications(__Total__)\\Compilations Total";
        counter = "ASP.NET Applications(__Total__)\\Errors Total/Sec";
        counter = "ASP.NET Applications(__Total__)\\Pipeline Instance Count";
        counter = "ASP.NET Applications(__Total__)\\Requests Executing";
        counter = "ASP.NET Applications(__Total__)\\Requests in Application Queue";
        counter = "ASP.NET Applications(__Total__)\\Requests/Sec";
        counter = "ASP.NET\\Application Restarts";
        counter = "ASP.NET\\Request Wait Time";
        counter = "ASP.NET\\Requests Current";
        counter = "ASP.NET\\Requests Queued";
        counter = "ASP.NET\\Requests Rejected";
        counter = "Memory\\Available MBytes";
        counter = "Memory\\Cache Faults/sec";
        counter = "Memory\\Demand Zero Faults/sec";
        counter = "Memory\\Page Faults/sec";
        counter = "Memory\\Pages/sec";
        counter = "Memory\\Transition Faults/sec";
        counter = "Memory\\Pool Nonpaged Bytes";
        counter = "Memory\\Pool Paged Bytes";
        counter = "PhysicalDisk(_Total)\\% Disk Time";
        counter = "PhysicalDisk(_Total)\\% Disk Read Time";
        counter = "PhysicalDisk(_Total)\\% Disk Write Time";
        counter = "Process(w3wp)\\% Processor Time";
        counter = "Process(w3wp)\\Handle Count";
        counter = "Process(w3wp)\\ID Process";
        counter = "Process(w3wp)\\Private Bytes";
        counter = "Process(w3wp)\\Thread Count";
        counter = "Process(w3wp)\\Virtual Bytes";
        counter = "Process(w3wp)\\Working Set";
        counter = "Processor(_Total)\\% Interrupt Time";
        counter = "Processor(_Total)\\% Privileged Time";
        counter = "Processor(_Total)\\% Processor Time";
        counter = "Processor(_Total)\\% User Time";
        counter = "System\\Context Switches/sec";
        counter = "System\\System Calls/sec";
        counter = "Web Service(_Total)\\Get Requests/sec";
        counter = "Web Service(_Total)\\Post Requests/sec";
        counter = "Web Service(_Total)\\Connection Attempts/sec";
        counter = "Web Service(_Total)\\Current Connections";
        counter = "Web Service(_Total)\\ISAPI Extension Requests/sec";
        counter = "Web Service Cache\\URI Cache Hits %";
        counter = "Web Service Cache\\Kernel: URI Cache Hits %";
        counter = "Web Service Cache\\File Cache Hits %";
    }
}

PAL

Performance Analysis of Logs (PAL) is a tool by Microsoft Premier Field Engineer, Clint Huffman. I haven’t had a chance to preview this tool yet, so I don’t want to speculate. It does seem to have a powerful feature set and following, so it’s definitely worth a look if you need a more comprehensive view.