Profiling WPF Applications from the Command Line

Toward the end of my next-to-last post I suggested using the command-line to control profiling.  I've been experimenting with this functionality and wanted to post what I've learned. 

First, IanWho’s blog is a pretty amazing source of VSTS Profiler info.  Here is his post on command-line profiling.  The instructions he gives in that post should be used for startup-time profiling.  

However, when measuring actions after startup, slightly different steps are needed to factor out startup time.  Below are the steps I’ve been using for measuring post-startup actions:

Step-by-Step: Profiling WPF from the Command-Line

1. Setup before the app has started.  

Doing this step before the app launches is very important!  If you don't, managed code won't be profiled and you'll get an error similiar to this:

Warning VSP2321 : Managed code could not be profiled. Use VSPerfCLREnv.cmd to set the environment for managed code profiling

set PATH=%PATH%;"C:\Program Files\Microsoft Visual Studio 8\Team Tools\Performance Tools"

VSPerfClrEnv.cmd /sampleon

If you don’t do this, the /attach command in Step 2 won’t work.

vsperfcmd /start:SAMPLE /output:MyOutput.vsp

vsperfcmd /globaloff

These commands will initialize your environmental variables, and warm-up the profiler before profiling is started.  The last command will suspend profiling until you are ready to start profiling. 

2. Profiling your application.

There are 2 ways to start profiling the application -- attaching to a running application, or launching the application. 

Launching the application directly (to measure startup costs)

Sometimes, profiling can be very ... challenging (to your sanity).  Learn this next lesson from me and save your juice for other problems.  Always attach to an application instead of launching it directly, -unless- your optimizing for startup time specifically.  If you don't your profile will contain both startup costs and your post-startup action, and you won't have a clue as to which is which .  If your interested in optimizing both startup time and some post-startup action , then use 2 seperate profiles.

To measure startup time, use the follow to commands:

      vsperfcmd /globalon

      vsperfcmd /launch:MyTest.exe

Use /globalon before launching the app so all of the startup costs are captured by the profiler.

Attaching to an application (to measure anything after startup)

First, launch the application FROM THE COMMAND LINE (the same one you used vsperfclrenv on).  Otherwise the environmental variables set in Step 1 won't be picked up, and you'll get that same error:

Warning VSP2321 : Managed code could not be profiled. Use VSPerfCLREnv.cmd to set the environment for managed code profiling

Run these commands to measure post-startup costs:

vsperfcmd /attach:MyTest.exe

vsperfcmd /globalon

This starts profiling & attaches the profiler to your already-running application.  We use /globalon after /attach so that any costs incurred during /attach aren't included in the profile.

3. Perform the action you want to profile

4. Stop profiling

vsperfcmd /globaloff

First, we turn off profiling to ensure only the interesting action was profiled, and not shutdown.

kill MyTest.exe

The /shutdown switch waits for the app to exit, so kill the app before attempting shutdown.  You can also close it using the UI or Alt+F4.

vsperfcmd /shutdown.

End profiling.  After this step, the .VSP log specified to the /output parameter will be generated.

5. Pack symbols

If your viewing the .VSP soley on the machine you used to gather the profile on, this step is optional.  Otherwise, make sure to pack symbols before moving the .VSP to another machine.

   xcopy /E "C:\Documents and Settings\timothyc\My Documents\Visual Studio 2005\Projects\MyTest\MyTest\bin\Release\*.PDB" .

Copy all of the symbols for your project to the local directory.  I've found symbol packing to work -much- more consistently when symbols exist in the local directory.

      set _NT_SYMBOL_PATH='WPF Symbol Server'

This command is optional for external folks who don't have access to private WPF symbol servers.  A number of internal folks use this blog, so I'm mentioning it so they can make sure and maximize symbol information. 

   vsperfreport /summary:all /packsymbols MyOutput.VSP

 This last command performs the actual symbol packing, now that symbols have been setup.

Using Batch Files

Using a batch file to start & stop profiling is really important.  Not only does it minimize the performance impact of interacting with the command console, it helps ensure that results are reproducible by repeating the exact same steps in subsequent profiles.  Here is the batch file I use when profiling from the command line.

REM Initialize the VSTS Profiler

REM

set PATH=%PATH%;"C:\Program Files\Microsoft Visual Studio 8\Team Tools\Performance Tools"

call VSPerfClrEnv.cmd /sampleon

call vsperfcmd /start:SAMPLE /output:MyOutput.vsp

call vsperfcmd /globaloff

REM

REM Initialize my app

REM Use start.exe to avoid blocking the current command console

REM

start MyTest.exe

REM

REM Sleep for 25 seconds while my app starts up and I navigate through it

REM

sleep 25

REM

REM Start Profiling

REM

call vsperfcmd /attach:MyTest.exe

call vsperfcmd /globalon

REM

REM Record 20 seconds of the action

REM

sleep 20

REM

REM Stop Profiling

REM

call vsperfcmd /globaloff

kill MyTest.exe

call vsperfcmd /shutdown