You are on page 1of 18


Josh Williams
Microsoft Corporation

Legal Information

This is a preliminary document and may be changed substantially prior to final c

ommercial release of the software described herein.

The information contained in this document represents the current view of Micros
oft Corporation on the issues discussed as of the date of publication. Because
Microsoft must respond to changing market conditions, it should not be interpret
ed to be a commitment on the part of Microsoft, and Microsoft cannot guarantee t
he accuracy of any information presented after the date of publication.

This White Paper is for informational purposes only. MICROSOFT MAKES NO WARRANT

Complying with all applicable copyright laws is the responsibility of the user.
Without limiting the rights under copyright, no part of this document may be re
produced, stored in or introduced into a retrieval system, or transmitted in any
form or by any means (electronic, mechanical, photocopying, recording, or other
wise), or for any purpose, without the express written permission of Microsoft C

Microsoft may have patents, patent applications, trademarks, copyrights, or othe

r intellectual property rights covering subject matter in this document. Except
as expressly provided in any written license agreement from Microsoft, the furn
ishing of this document does not give you any license to these patents, trademar
ks, copyrights, or other intellectual property.

Ó 2003, 2006 Microsoft Corporation. All rights reserved.

Microsoft is a registered trademark of Microsoft Corporation in the United State

s and/or other countries.
The names of actual companies and products mentioned herein may be the trademark
s of their respective owners.
Table of contents
Legal Information
Table of contents
What it is
Misson Statement
About PerfConsole
PerfConsole Data Model
Extension Dlls
Current Extension Dlls
Configuration File
What it is
PerfConsole is a simple performance investigation tool which tries to adopt a de
bugger like experience to drilling into Visual Studio Performance Profiler gener
ated data. Generally if you run the Visual Studio Performance Profiler it will o
utput a .VSP file; additionally the Visual Studio Performance Profiler provides
a utility VSPerfReport.exe which will convert that .VSP file to a set of .CSV fi
les which contain processed data (much like you would see in the VS UI). PerfCon
sole understands how to read that .CSV output and give the developer a nice way
to easily traverse the data.

Mission Statement
PerfConsole was designed to be
- Lightweight (requiring nothing more than a base .Net 2.0 install) ana
- Simple to learn and use
- Easily extendable
- Useful for investigation
- Useful for automation

About PerfConsole
PerfConsole can be driven in two modes:
1) In console mode (pass /console on the command line) all output will be lim
ited to the console window. In this mode it is recommended that you set your con
sole to some large width in order for things to show up correctly.
2) In console/winforms mode (the default) you can drive things from the con
sole window, but whenever interesting data is generated it is displayed in a new
window which then has GUI navigation to continue drilling into that data set.

I expect that most people will use PerfConsole in the default mode; however for
the hardcore the console can be surprisingly efficient -- it is kind of the equi
valent to debugging using Visual Studio vs. debugging using WinDbg .

This document will first cover the basics of PerfConsole:

- syntax
- data model
- common commands

We will then get into more interesting pieces like the Windows Forms-based outpu
t and investigation mode. Since this mode is fundamentally based on the same com
mand syntax and data model as the console mode, it is highly recommended that yo
u read straight through.
To understand PerfConsole it helps to have a basic grasp of the command line syn
Access a temporary data object named <name>; this includes loaded profiles or sa
ved results of command execution
{x, y}
Create a tuple with elements x and y. These elements can be profiles, temporarie
s or command sequences
Command separator
Command sequence separator

At the core of PerfConsole are Profile objects, which can contain some set of da
ta including FunctionSummaryData, ModuleSummaryData, CallerCalleeData, CallTreeD
ata and ProfileInfo. Depending on the type of profile imported some of these may
not be useful. Generally however given the default usage of the tool they will
all be available.

To make use of the data there is the concept of commands that know how to manipu
late the shape of data (e.g. find a specific function, sort the modules, get n i
tems from a list). These commands can be applied to a profile s data set in order
to gain useful information about the performance characteristics of the profile

As input a command accepts some data object and optionally some set of other inp
uts (e.g. strings, integers, enum values, etc). The command s output is typically
some other data object which can be consumed by other commands, making commands

> functions @name | sort Exclusive | top 30

For instance, the command above takes all functions out of profile @name, sorts
them by exclusive time and then returns the top 30. This is the PerfConsole logi
cal equivalent to the statement the top 30 exclusive functions in profile @name .

In console mode the output would look like this:

PerfConsole Data Model

Commands are allowed to operate on objects that derive from the base type PerfDa
ta. All interesting data in the system falls into this category; Profile, Functi
onSummaryData, Function (objects which are contained by FunctionSummaryData), et
c are all derived from PerfData. Most commands take an input of a PerfData deriv
ed object and return a PerfData derived object as output, which is the mechanism
used for command composability, with the | (pipe) operator sewing commands toge
ther (the pipe works as normal, passing data from left to right).

Hence when we see a command like:

> functions @name | sort Exclusive | top 30

we can break it down by saying that there is some PerfData object which is input
to the functions command (in this case Profile @name), and the functions comman
d returns some PerfData object (in this case a FunctionSummaryData) which is pas
sed as input[1] to the sort command, and the output of the sort command is passe
d as input to the top command. This is much like the Windows or Unix command she
ll, except instead of text what is being passed along from command to command is
a strongly typed object. Generally commands will narrow the type of object that
they accept by specifying that it must implement some interface or be some exac
t type. This allows the system to provide better error messages when commands ar
e combined with incompatible data types, and it also assists in automatically bu
ilding good help output.

Looking at the help[2] for the functions command we can see that it returns a Fu
nctionSummaryData object, however the help for the sort command indicates that
it only accepts an object of type ISortable. Note that the help output follows t
he standard | conventions: input to the left of the command, output to the right

> ? functions
Get the function data out of a profile.

Profile | functions | FunctionSummaryData

> ? sort
Sort a IPerfDataSortableList (e.g. FunctionSummary, ModuleSummary). Each type
has its own sort types (e.g. 'name', 'address', 'exclusive', etc...).

ISortable | sort <field:SortField> | ISortable
ISortable | sort <field:SortField> <direction:SortDirection> | ISortable

Have enum paramters:

SortDirection { Ascending, Descending }
SortField { Name, ExclusivePercentage, InclusivePercentage, Address }

There are a special set of interfaces (all declared as nested types of PerfData)
which objects are allowed to implement and are allowed to be the input and outp
ut types of commands. Using the help built into PerfConsole and the special comm
and print type it becomes easy to discover what data type is returned by a comma
nd and what interfaces it implements, For example:

> functions @name | sort Exclusive | top 10 | print type

FunctionSummaryData -- implements: IPerfData, ICloneable, IList, ISearchable, IS
ortable, ITrimable, IHtmlPrintable


Here you can see that the data type is preserved through the commands even thoug
h sort returns an ISortable and top returns an IList, as underneath the Function
SummaryData which is being manipulated implements both of those interfaces.

There is an additional form of the help command which takes in a PerfData object
(much like the print type command), but instead returns a list of other strongly
typed commands which will accept the output data object as an input. This can be
a very useful way to experiment with the system and figure out how commands com
pose together.

PerfConsole contains a set of built in commands which are very useful for shapin
g data into useful forms to gather information during a performance investigatio

Base Commands
Help / ?
Display help. This can come in a number of forms:
1) Listing the commands which are currently loaded: help
2) Showing help for a specific command: help <command name>
3) Showing a list of commands that will work with a specific input data obj
ect: <command that produces object> | help
Display this document.
Load a profile or extension command DLL from disk. The default is to load a Prof
ile, using load <profile filename> .
Save an object into a temporary. As seen in the syntax section of this document,
temporaries are denoted by @name.
View and change PerfConsole application settings.
Clear the console screen.
Return the special PerfData derived null object; currently its only use is to cl
ear a specific temporary (using null | save tempname ).
Add lookup paths for command dlls, profiles or source. Source paths are also pic
ked up from the configuration file and through the _NT_SOURCE_PATH environment v
Exit / Q / Quit
Leave the PerfConsole

Profile Commands
Get the FunctionSummaryData object out of a profile.
Get the ModuleSummaryData object out of a profile.
Get the CallerCalleeData object out of a profile.
Get the CallTreeData object out of a profile.
Get the ProfileInfo object out of a profile

Data Manipulation Commands

Top / Bottom
Get some number of elements off the top or bottom of an IList.
Sort an object that implements ISortable by some field and in some direction. Th
is can mean different things depending on the object it is applied to. Also, som
e objects don t support sorting by some fields; in this case the operation will fa
Trim the data within an object[4] that implements ITrimable, for instance: functi
ons @name | trim Inclusive > 10 will return a FunctionSummaryData object where al
l elements have an inclusive time greater than 10%.
Search an ISearchable object[5] using search criteria passed on the command line
. For instance functions @name | find ModuleName Contains mscorwks returns a Funct
ionSummaryData list containing only functions which reside in the mscorwks modul
Trim branches of a CallTreeData object at a specified depth such that the return
ed CallTreeData will have all branches <= the specified depth. This is very usef
ul because otherwise full call trees can be of unwieldy size.
Find all places within a CallTreeData object where a method matching specific se
arch criteria resides and show the call tree of callers leading to that method.
This can be very useful in tracking down patterns of calling a particularly hot
Frequently when looking through calltrees there are many forwarder methods in whic
h the profile spends no actual time (exclusive samples). These lists can be many
methods deep and make calltrees less usable. CompactTree will compact all nodes
that appear to be forwarders into a single node in the tree.
Get a specified item out of an IList and return it as the item type (which will
be derived from PerfData). This becomes very useful when combined with the save
command and property accessor syntax to be able to use the results of one query
programmatically in constructing another query.
Get a specific property of a PerfData object. Accessible properties are shown by
the print type command.
Get the count of items in an object that implements PerfData.IList.

Output Commands
Prints the result of a command to the console. In /console mode this is the defa
ult command executed at the end of a command sequence that returns a non-null Pe
rfData object. Print can also be used with the type or html arguments which control
the output:
- type shows the type of data object which is passed in, including implem
ented interfaces and public properties
- html takes the resulting PerfData object from a command sequence and co
nverts it to html.

If given an output filename the print command will save the results to a file on
Opens a form with the results of the command displayed as html. When not in /con
sole mode this is the default command executed at the end of a command sequence
that returns a non-null PerfData object.[6]

State Commands
Shows interesting state of PerfConsole (e.g.: settings, loaded profiles, current
Output a label to the console, which can be useful for reporting purposes.
Clear all temporaries currently stored.

Commands in PerfConsole are composable, and you aren t limited to using a single c
ommand only once in a command sequence. For instance, the following is perfectly

> functions @name | sort Exclusive | top 10 | sort Name

which says give me the top 10 exclusive time functions sorted by name . Or:

> functions @name | trim Inclusive > 10 | trim Inclusive < 40 | sort Inclusive

which says give me the list of functions with inclusive times between 10 and 40%,
sorted by inclusive time . It is notable that you can use shorthand versions of e
num values as long as you include enough information to fully disambiguate which
value you mean, so:

> functions @name | sort ex | top 10

Is the same as:

> functions @name | sort ExclusivePercentage | top 10

Frequently it is useful to search for specific function or module names; this ca

n easily be achieved using the find command. Here we see the butterfly view of a
specific method in a profile:
Often it proves useful to just take a profile and start playing around with it,
seeing what you find that interests you. Frequently getting to a call tree is ve
ry useful, the command:

> calltree @name | trim in > 5

Results in a useful overview of where time is spend. Here is what this looks lik
e in console mode:

If you run PerfConsole in its default mode you will get the console/winforms mod
e. This mode acts like a console pretty much until it decides to act like a Wind
ows Forms app, which is any time there is interesting data to visualize.

When you start with something as simple as the command functions @name in form mod
e you will be presented with the following:

Here we can see that the FunctionSummaryData object rendered itself as HTML and
was pushed into a form which knows how to display HTML. Notice the context menu
when I right-clicked over one of the links. This shows you the actions which are
hard coded into the UI of things you can do with that data element. In this cas
e you can:

- See a butterfly view centered on the function

- See a call tree showing all the functions that are called by this fun
- See a call tree showing all the functions that called this function
- Open the source file for the function using the configured source edi
tor (set in the configuration file).

If you just click the link then the first command in the context menu is execute
d, in this case showing you the butterfly view centered on that function. Additi
onally, you can enter new commands or variations on current commands in the comm
and execution text box at the top of the window. If you pull down the combo box
you will see a history of the commands that you executed to get to this point an
d you can choose to go back and see the results again (the back/forward buttons
also work for this purpose).
Here are some other interesting views
1) A call tree showing all the functions called by __main__.Proc0$f5(object)

2) A call tree showing all the unique call trees of depth 9 which called CallCom

3) A butterfly showing the callers and callees for function __main__.Proc0$f5:

Generally it is recommended that you play with the interface to get a feel for i
t. And rest assured that it will get prettier over time.

It helps to get used to the syntax by first staring to play with PerfConsole in
/console mode -- there you can explore the composability of commands.

It is notable that the PerfConsole forms output is just HTML and can be cut/past
ed directly into outlook and emailed. Additionally you can save the HTML or prin
t directly from the application.

One of the interesting uses of PerfConsole will be in automation programs. For t
hose purposes it is notable that PerfConsole can execute commands which are pass
ed as command line arguments. E.g.:

c:\>PerfConsole data\prof3.vsp /console /cmd functions @prof3 | sort ex | top 10;


Here PerfConsole will start up in console mode, load the profile data\prof3.vsp,
print out the top 10 exclusive functions to the console and quit. Put together
with macros and extension dlls this can become a very powerful mechanism for aut
omated performance reporting. Use /? from the command line to see this and other
argument options.
Extension Dlls
Extensibility in PerfConsole comes from extension command dlls. These are simple
managed dlls which have some number of types marked with a special attribute ([
PerfConsoleCustomCommand]) indicating that they are commands which derive from P
erfConsole.Commands.Command. Here is the definition for Command:

public abstract class Command

public abstract string Name { get; }
public abstract string Help { get; }
public abstract ColoredText Usage { get; }
public abstract PerfData InvokeCommand(PerfData obj, string input);

By deriving from this type you can create any extended functionality that you wa
nt for PerfConsole. However, you have to do all that pesky string parsing yourse
lf, taking care to follow all the same rules as the rest of the commands for par
sing and validating parameters (there is a helper for splitting input strings, C
ommand.SplitParameters() which promotes consistency between commands. Additional
ly if other commands want to reuse the code that your command implements they ar
e going to have to pass long hard-to-debug strings to your command to control it
s behavior. Given this I have provided a derivative of Command named ReflectionC
ommand which eliminates all of these hassles. It handles the parsing of the comm
and line and strong typing of command invocation method parameters, therefore al
lowing easy reuse in a way that any C# programmer is used to. This allows you to
implement a new command to (for instance) find all jit helpers by easily reusing
a number of the built in commands:

public static PerfData Invoke(Profile prof)

FunctionSummaryData functions = Functions.Invoke(prof);
functions = Find.Invoke(functions, "JIT_") as FunctionSummaryData;
functions = Sort.Invoke(functions, PerfData.SortField.Exclusive) as Func
return functions;

Here we are able to pass an enum to the sort command and a string to the find co
mmand while passing no extraneous data to the functions command. ReflectionComma
nd allows easy creation of strongly typed command invocation methods (additional
ly automatically creating usage output based on the typing of input and output a
rguments). If you derive from ReflectionCommand instead of Command you get this
for free simply by creating static command invocation methods that conform to th
e following set of rules.

ReflectionCommand invocation method rules:

1) Invocation methods declaration must be public static
2) Method name must be Invoke
3) Return type must be one of:
a. PerfData derived type
b. Interface declared as nested type of PerfData
c. Void
4) Any Invoke method may take at most one PerfData derived type as a parame
ter and it must be first if it exists.
5) Method overloading is based on number of arguments, not type of argument
a. Zero arguments is acceptable
b. The exception is that you can overload two methods with the same number
of arguments where one method has a PerfData derived argument (or PerfData inter
face argument) and the other does not
6) All other parameters must be of type:
a. String
b. Int
c. Long
d. Float
e. Double
f. Boolean
g. Enum derived type
7) Extension commands need to have their declaring type marked with the Per

The ReflectionCommand type then takes the responsibility to implement the Usage
getter as well as InvokeCommand method and dispatch to your declared invocation
methods at runtime when the command is invoked from the console. This also allow
s for easy reuse of your commands by other commands and eliminates the hassle of
string parsing in every command instead allowing you to have strongly typed inp
ut parameter types like enums.[7] While they don t fall into rules per se, there a
re also a set of recommendations for implementing external commands derived from

Recommendations for ReflectionCommand command invocation methods:

1) Give meaningful names to method parameters, as they will be utilized to
auto-generate usage text for the command and should help the user understand the
ir function
2) Use enums as parameter types where possible instead of Booleans or arbit
rary strings as they will provide the user with more discoverability for command
syntax and can be strongly matched at runtime to acceptable values by the Refle
ctionCommand infrastructure (thereby providing good failure messaging).
3) Make PerfData argument type and return type as specific as possible whil
e being wide enough to encompass all of the capabilities of your command. It hel
ps with argument validation and help text generation.
4) Use Enum members named GreaterThan and LessThan, as they are synonymous with
< and > in PerfConsole which can be convenient for command syntax that reads well.

Here is an example of a simple command that computes the sum of all the exclusiv
e or inclusive time of the elements in a list using the ReflectionCommand mechan

class Sum : ReflectionCommand
static readonly string name = "sum";
static readonly string help = "Print the sum of either the Inclusive or
Exclusive percentages of all the entries in a list.";

public override string Name { get { return name; }}

public override string Help { get { return help; }}

public static StringList Invoke(PerfData.IList data)

return Invoke(data, PerfData.Field.Exclusive);
public static StringList Invoke(PerfData.IList data, PerfData.Field fiel
StringList sl = new StringList();
float sum = 0.0f;

switch (fieldToSum)
case PerfData.Field.Exclusive:
for (int i=0; i<data.Count; i++)
IBaseEntry entry = (IBaseEntry)data.GetItem(i);
sum += entry.ExclusivePercentage;
sl.Add(String.Format("Exclusive Sum={0:p}.", sum/100));
case PerfData.Field.Inclusive:
for (int i=0; i<data.Count; i++)
IBaseEntry entry = (IBaseEntry)data.GetItem(i);
sum += entry.InclusivePercentage;
sl.Add(String.Format("Incluisve Sum={0:p}.", sum/100));

return sl;
Current Extension Dlls
Here is a list of the extensions that I have implemented so far. As mentioned be
fore these are loaded using the load CommandDll command.

Data Manipulation Commands

Converts a FunctionSummaryData list to a TypeSummaryData list by grouping call e
xclusive time for methods of a type into the overall exclusive time for that typ
Allows you to partition a list into buckets using a partition specification like
[1,2,3,4,5,50] which would create buckets for 0-1, 1-2, 2-3, 4-5, 5-50, 50-100,
etc Default is buckets split on multiples of 10.

Configuration File
PerfConsole has a simple configuration file pc.config that resides in the same d
irectory as the executable. It is not required, but it does allow for a number o
f customizations. These include
- Command dll lookup paths, profile lookup paths and source lookup path
- Extension command dlls to be loaded at startup
- Default public settings (what can be changed at runtime using the set c
- Source file editor (the default is notepad). Note that %line and %fil
e are special and are replaced at runtime with the line number and file name.

An example pc.config file:

<PerfConsole name="VS Profiler Default">
<!--<Providers default="true"/>-->
<!-- This is where we look for command dlls -->
<!-- Example of how to set interesting paths to make PerfConsole
experience better -->
<!-- SourcePaths are also picked up from _NT_SOURCE_PATH -->
<!-- Use this to specify an editor to use to open source files, %line wi
ll be
replaced with the line number and %file with the full path to the s
file (might be on a network share). The <Editor/> element should be

populated with the .exe name of your editor only. Full path to .exe
<EditorArgs>/g %line %file</EditorArgs>

[1] Commands implicitly clone the incoming data such that the original data is n
ot modified by the command. This means that you can t apply the sort command to th
e function list of a profile and assume that it is sorted that way from then on.
This feature prevents you from being destructive to your original data and enab
les multiple concurrent views of the same data without conflict. It is possible
to save the result of a command into a temporary variable using the save command
[2] It is useful to understand the output of the help command in order to discov
er what commands work together. See the section on commands for more information
[3] It is highly recommended that you use the help command to investigate the ex
act semantics of these commands.
[4] When applied to a CallTreeData object the trim command will terminate all br
anches at the first node which passes below the threshold.
[5] When applied to a CallTreeData object the find command will find all nodes w
hich match the search criteria and promote them to the root level, bringing alon
g their callers. Thus if you specify a query that can match multiple items e.g. f
ind Name Contains System.String you will end up with a branch from the root for e
ach method in System.String.
[6] Things which wouldn t make sense to display in a form are still displayed on t
he console, e.g. results from help, print, set, etc
[7] It additionally allows for some neat functionality in the help command where
it can at runtime lookup which commands will accept a specific PerfData derived
object as input and present the user with a list.