Click here to Skip to main content
Click here to Skip to main content

Profiling of C++-Applications in Visual Studio for Free

By , 11 Jul 2012
 

Introduction

While the Team- or Premium-editions of Visual Studio contain a profiler, users of e.g. the professional edition are lacking such a feature. In this article, I present a method for profiling C++-applications with freely available tools and a simple application for presenting the results in an informative view.

profileWithVsPerfCmd/Screenshot2.png

Of course, the resulting solution will not be as comfortable as an integrated profiler, but if you use profiling only when you find bottlenecks, this method is certainly sufficient - and €5.000 cheaper.

The following method was tested with Visual Studio 2008 Professional and Visual C++ 2010 Express.

The Method

The method consists of four steps:

  • Generate a special "/PROFILE" build using Visual Studio (all editions can do this).
  • Profile the freshly compiled application with the freely available Microsoft tool "VSPerfCmd".
  • Convert the resulting .vsp-file to .csv-files using "VSPerfReport".
  • Analyze the resulting - huge - csv-files with the attached Profile Result Viewer.

Generate a /PROFILE-build

Note: The following steps should of course be done with a release build and with debugging symbols enabled.

In Visual Studio 2008, go to the solution explorer, select the project and open the properties page. In the section, "Configuration Properties", choose "Linker" - "Advanced".
Set "Profile" to "Enable Profiling information (/PROFILE)" (the exact steps for other versions of Visual Studio might differ).

profileWithVsPerfCmd/screenshot1.png

Compile the project.

Generate a Profile Report (vsp)

First of all, install the Visual Studio Performance Tools. The Visual Studio 2008 Service Pack 1 Stand-Alone Profiler can be found at this link.

The generation of the report requires to enter a handful of commands in the command line.

Preparations in the Commandline

Open the command prompt. Define a shortcut to the performance tools and switch to the folder, your application is located in:

set pt="C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Performance Tools"
cd [my_app_folder]

Profiling a Full Application Run

The simplest method is to profile the full uptime of the application.

:: Start the profiler
%pt%\vsperfcmd /start:sample /output:my_sampled_data.vsp

:: Launch the application via the profiler
%pt%\vsperfcmd /launch:my_app.exe

:: Shut down the profiler (this command waits, until the application is terminated)
%pt%\vsperfcmd /shutdown

Profiling only part of the application Uptime

It is also possible, to profile only part of the application uptime.

Here the handling becomes a bit tedious, so I suppose it would be best to implement some simple app to automize the following steps. Anyways, I never really had the demand for profiling only part of the uptime, since I could (maybe by modifying the application's startup code) always come up with code, that spent 90% of its time in the relevant part. Nevertheless, if you cannot modify your code adequately, here are the additional steps.

Start vsperfcmd as above and attach it to your application afterwards:

:: Start the profiler
%pt%\vsperfcmd /start:sample /output:my_sampled_data.vsp

:: Send the profiler to a waiting state
%pt%\vsperfcmd /globaloff

:: Attach to your application by specifying the pid
%pt%\vsperfcmd /attach:[pid]

After attaching to your application, perform whatever preparation is necessary. Now initiate the function that you want to profile and activate the profiler.

:: Activate the profiler
%pt%\vsperfcmd /globalon 

When the function is finished (or you think enough time has passed), stop profiling:

:: Detach the profiler from your application
%pt%\vsperfcmd /detach

:: Shut down the profiler
%pt%\vsperfcmd /shutdown 

Convert the Profile Report to a csv-file

The tool vsperfreport loads symbol-files (.pdb) and merges them with the generated report (.vsp) to create .csv-Files.

:: generate the report files (.csv)
%pt%\vsperfreport /summary:all my_sampled_data.vsp 

If you are also interested in calls within system files, you can also specify a path to a symbol server or a local cache.

%pt%\vsperfreport /summary:all my_sampled_data.vsp 
/symbolpath:"srv*C:\MySymbolCache*http://msdl.microsoft.com/download/symbols" 

View the Report

The csv-files above can in principle be opened with any spreadsheet application, nonetheless, they are typically huge, and not immediately helpful.

Therefore, I wrote a small application that parses one of the generated files (the one named "...CallerCalleeSummary.csv") and presents it as a tree view, ordered by total percentage. Starting from this view, it should be easy to find the cause for performance problems. For my part, I could uncover several bottlenecks in C++-legacy code, which I would never have found, just by looking at the code.

Using the Code

Attached is also a small C++-application for which I generated a profile. The generated .vsp and .csv-files are contained in the "Release"-subfolder. Opening "my_sampled_data_CallerCalleeSummary.csv" with ProfileResultViewer should resemble the screenshot I have given above.

Points of Interest

  • The profile report will show statistics only for functions, not for individual lines.
  • Functions that are inlined will not be contained in the profile, since they also never appear in the native call stack.
  • The presented treeview is not strictly hierarchic: If A calls B, then in the presented view B might seem to require more time than A. This appears, because total percentages are shown, i.e., it is not shown how much time B took, when it was called from A, but how much time B took in total. I am not sure if the csv-files contain enough information to recover the more detailed figures.

Of course, some of the steps above could be easily integrated into some GUI-Application, but for the moment the procedure I described above is sufficiently simple for me.

History

  • 11.01.2011: Initial version
  • 11.07.2012: Improved speed of ProfileResultViewer for large CSV-files (thanks to SteelixB for giving me the hint)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

hofingerandi
Software Developer
Austria Austria
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionGreat!memberCOFF3 Apr '13 - 16:32 
I found this tool very useful!
GeneralYou've done a great job!memberecquadrat6 Feb '13 - 0:58 
Searching for an easy way to get an overview where the most time is spent in my application I found your article. Your approach to the problem is a great idea and by the way it helps to save a lot of money.
 
For profiling an application only a few times for a rough overview it's a real alternative against buying an expensive tool.
 
Thank you very much!
QuestionA follow-up articlememberMattias Högström17 Jan '13 - 7:12 
Excellent article. Thanks for showing us.
 
I made my own variant with some additions.
I gave you credits in the article and linked to this article.
 
* Analysis of root nodes. Identifies threads.
* Drilldown and percentage recalculation.
* Supports both vs2010 and vs2012
* Supports also trace summaries
* Parses both comma and dot separated decimals
* Made in F#
 

Analyzing profiling data from vsperfcmd[^]
 
Have you looked at the data summary file from the vs2012 tools?
It contains a lot more interesting info than vs2010.
One can do more advanced analysis than before.
GeneralMy vote of 5memberMattias Högström29 Nov '12 - 23:24 
This is excellent. I was googling for profilers, and found this alternative. Great!! and so much cheaper.
GeneralMy vote of 5memberMichael Haephrati מיכאל האפרתי21 Oct '12 - 8:23 
Great and useful information. Thanks!
GeneralMy vote of 5memberSergey Podobry14 Oct '12 - 22:35 
Thanks for the useful info! Keep going!
QuestionAlternativemembergeoyar16 Jul '12 - 8:14 
Thank you for an interesting tip. My vote is 5.
By a way, there is an alternative, also free: CodeAnalyst from AMD. It automatically integrates with Visual Studio. I am using it on my machine (AMD Athlon), but it seems it can work with other MCU too.
geoyar

GeneralMy vote of 5memberMihai MOGA14 Jul '12 - 20:18 
This is a great inspiring article. I am pretty much pleased with your good work. You put really very helpful information. Keep it up once again.
SuggestionWorking with huge CSV filesmemberSteelixB11 Jun '12 - 1:11 
In a program I was profiling, I generated a huge CSV file. Opening this file basically causes the profile result viewer to process the tree view forever and become unresponsive. This has a knock on effect of causing all other running processes to run sluggish.
 
The solution is to only fill each node before it is expanded - Fill all top level nodes with a dummy node, and handle the BeforeExpand event on the tree view. In the event handler, remove the dummy node and fill with actual nodes.
 
The difference in time to display the tree is from 10 minutes to 5 seconds.
AnswerRe: Working with huge CSV filesmemberhofingerandi11 Jul '12 - 2:09 
Thanks for the hint!
 
I finally found the time to implement your suggestion and updated the article. The viewer really is much faster now!
SuggestionCall tre XSLmemberTrigve10 Jun '12 - 23:08 
I'm pasting here some xsl template for call trace flat->hierarchical processing. Maybe someone find it useful Smile | :)
 
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
                                        xmlns:fn="http://www.w3.org/2005/xpath-functions"
                                        xmlns:xs="http://www.w3.org/2001/XMLSchema">
     <xsl:output method="xml" encoding="utf-8" version = "1.0" indent="yes" />
     <xsl:template match="/PerformanceReport/CallTreeSummary">
          <xsl:element name= "output">
               <xsl:call-template name="process-level">
                    <xsl:with-param name="population" select="CallTree"/>
                    <xsl:with-param name="level" select="1"/>
               </xsl:call-template>
          </xsl:element>
     </xsl:template>
 
     <xsl:template name="process-level">
          <xsl:param name="population" required="yes" as="element()*"/>
          <xsl:param name="level" required="yes" as="xs:integer"/>
          <xsl:for-each-group select="$population" group-starting-with="*[xs:integer(@Level) eq $level]">
               <xsl:choose>
                    <xsl:when test="count(current-group()) = 1">
                         <xsl:copy>
                              <xsl:copy-of select ="@Level" />
                              <xsl:copy-of select ="@FunctionName" />
                              <xsl:copy-of select ="@NumCalls" />
                              <xsl:copy-of select ="@ExclusiveApplicationTimePercent" />
                              <xsl:copy-of select ="@InclusiveApplicationTimePercent" />
                              <xsl:copy-of select ="@ExclusiveElapsedTimePercent" />
                              <xsl:copy-of select ="@InclusiveElapsedTimePercent" />
                              <xsl:copy-of select ="@ModulePath" />
                         </xsl:copy>
                    </xsl:when>
                    <xsl:otherwise>
                         <xsl:copy>
                              <xsl:copy-of select ="@Level" />
                              <xsl:copy-of select ="@FunctionName" />
                              <xsl:copy-of select ="@NumCalls" />
                              <xsl:copy-of select ="@ExclusiveApplicationTimePercent" />
                              <xsl:copy-of select ="@InclusiveApplicationTimePercent" />
                              <xsl:copy-of select ="@ExclusiveElapsedTimePercent" />
                              <xsl:copy-of select ="@InclusiveElapsedTimePercent" />
                              <xsl:copy-of select ="@ModulePath" />
                              <xsl:call-template name="process-level">
                                   <xsl:with-param name="population" select="current-group()[position() != 1]"/>
                                   <xsl:with-param name="level" select="$level + 1"/>
                              </xsl:call-template>
                         </xsl:copy>
                    </xsl:otherwise>
               </xsl:choose>
          </xsl:for-each-group>
     </xsl:template>
</xsl:stylesheet>
 
Edit: Better xslt
---
 
Wait till u canSmile | :)
GeneralMy vote of 5memberMember 766644014 Mar '12 - 6:00 
I just placed everything into simple batch file and I just have to double click one file and open the results with the project provided. Easy, simple and very powerful method. 5 stars.
QuestionFiltering by pattern in file name using wildcardmemberSergey Chepurin8 Feb '12 - 23:28 
In the sample C# application if you want to filter files not only by extension but also by certain part of file name use also FileName property. Lets say you want to open only certain .csv file in directory with CallerCalleeSummary pattern in the name (profiling output files):
dlg.FileName = "*CallerCalleeSummary*";                                                                                                        dlg.Filter = "CSV and TXT (*.csv;*.txt)|*.csv;*.txt";

AnswerRe: Filtering by pattern in file name using wildcardmemberhofingerandi10 Feb '12 - 8:44 
There are certainly parts of the code that would require improvement; unfortunately this article has already been edited, so it is not so easy any more for me, to change something...
 
For me, the part that would most importantly need revision is the population of the treeview. I am sure this can be made faster by using
BeginUpdate/EndUpdate
instead of
SuspendLayout/ResumeLayout
or by reworking the code to perform a bulk update.
 
If there is interest, I can maybe transfer the code to some other platform to allow for participation.
GeneralRe: Filtering by pattern in file name using wildcardmemberSergey Chepurin10 Feb '12 - 22:12 
Don't get me wrong, you wrote a very good article and code. I just added this small improvement for anyone who uses your application analyzing profiling data (so you don't have to think every time which file to open). Thank you, once more, the articles like yours make Codeproject.com even better.
QuestionVisual Studio 2010 Performance Tools [modified]memberSergey Chepurin6 Feb '12 - 22:55 
By the way, Performance Tools for VC ++ 2010 are here Visual Studio 2010 Performance Tools with Service Pack 1[^]

modified 9 Feb '12 - 10:03.

QuestionError processing file: Requested value 'memberDave Calkins4 Nov '11 - 6:33 
I'm trying to use this on a 64-bit app built with VS2010 on Win7. I've tried both the VS2008 and VS2010 standalone performance tools. In either case, I use /launch to run my app under the profiler and then use /shutdown and exit the app. The .vsp file is created. When I use ProfileResultViewer.exe and pick the .vsp file I get this error, "Error processing file: Requested value '"
AnswerRe: Error processing file: Requested value 'memberDave Calkins4 Nov '11 - 6:37 
looks like Reader.ReadLines(dlg.FileName) is throwing an exception, "Requested value '".
AnswerRe: Error processing file: Requested value 'memberhofingerandi4 Nov '11 - 10:11 
If I understood correctly, you are opening the wrong file:
As stated in the article, you must convert the .vsp to a .csv-file and open the correct .csv-file afterwards (the one named "...CallerCalleeSummary.csv").
 
But of course, I should implement a better error message Wink | ;)
GeneralRe: Error processing file: Requested value 'memberDave Calkins5 Nov '11 - 2:52 
oops! my bad Smile | :) I suppose I was too eager to get to the results! Sorry!
GeneralRe: Error processing file: Requested value 'memberDave Calkins7 Nov '11 - 5:54 
I ran vsperfreport which generated the .csv files. When I try and open the callercallee file, the viewer app seems to hang. It runs but never shows any results and is non-responsive.
BugI met the error when doing the step %pt%\vsperfcmd /start:sample /output:my_sampled_data.vsp [modified]memberMember 62414649 Oct '11 - 21:57 
Hi,
 
I met the error when doing the step %pt%\vsperfcmd /start:sample /output:my_sampled_data.vsp
Error VSP1398 : The monitor was unable to start the VS performance driver. This
driver has been blocked from loading Consider using the /Admin:Driver,Start and
/Admin:Security options of VSPerfCmd from an elevated environment. Profiling c
annot continue.
Error VSP1341 : Could not start logging engine. Shutting down.
Error VSP1394 : Could not start profile monitor
(My environment: Windows 7 SP1, VS2008 SP1)
Could you please help me?
 
Thanks,
Ha

modified 10 Oct '11 - 4:07.

AnswerRe: I met the error when doing the step %pt%\vsperfcmd /start:sample /output:my_sampled_data.vspmemberhofingerandi10 Oct '11 - 1:27 
This seems to be the same problem as answered below: Has anyone luck with this on Window7?[^]
 
Either start the profiler in an elevated command prompt or set permissions for profiling[^].
GeneralGood articlememberCIDev14 Feb '11 - 3:10 
Nice little technique.
Just because the code works, it doesn't mean that it is good code.

GeneralDownloading now ...memberJerry Evans13 Jan '11 - 8:23 
Wonderful resource. Thanks. Gets my 5. I'll report back on W7x64.
Generalblue screen on Windows 7 64 with Nehalem processormemberferdo13 Jan '11 - 5:09 
I got a blue screen with a stop error immedialty after the lauch command.
 
Seems that this is a problem with an old version of the driver Vsperfdrv90.sys
 
see: http://support.microsoft.com/kb/958842
QuestionHas anyone luck with this on Window7 ?memberJens froslev-nielsen12 Jan '11 - 20:44 
Hi there,
Greate stuff,
I'am running on Windows7 - it doesn't seem to work (at least) out of the box - you need to have admin (elevated user) access rights
MS states that the profiler is only supported on XP ?
AnswerRe: Has anyone luck with this on Window7 ?memberhofingerandi12 Jan '11 - 21:24 
In principle, you need admin rights to run vsperfcmd, so the easiest way is to start it in a cmd-window with administrative rights.
 
If you do not have the possibility to do this (or do not want to enter your password every time you are profiling) you can follow the instructions given in the msdn: How to: Set Profiling Permissions
GeneralRe: Has anyone luck with this on Window7 ?memberJens froslev-nielsen13 Jan '11 - 7:28 
thx
Works like charm... Jens
GeneralRe: Has anyone luck with this on Window7 ?memberprinzhang30 Jan '13 - 15:21 
I have done as the guid in page 'Set Profiling Permissions'
in command prompt window:
 
input: vsperfcmd /admin:driver,start /admin:service,start
return error information as below:
Microsoft (R) VSPerf Command Version 9.0.30729 x86
Copyright (C) Microsoft Corp. All rights reserved.
driver,start
Starting VSPerfDrv90
Error VSP1449 : Unable to start VSPerfDrv90.
Try using the /Admin switch of VSPerfCmd from an elevated environment.
 
so I try again
input: vsperfcmd /admin:aecurity,allow,Fullaccess,administrator
return error information as below:
Microsoft (R) VSPerf Command Version 9.0.30729 x86
Copyright (C) Microsoft Corp. All rights reserved.
security,allow,Fullaccess,administrator
Error VSP1468 : Unable to write new Security Descriptor for profiling.
 
what can i do??
Too simple or too difficult.
You know yourself.

QuestionRe: Has anyone luck with this on Window7 ?memberhofingerandi30 Jan '13 - 20:44 
Did you open the command prompt as an administrator?
AnswerRe: Has anyone luck with this on Window7 ?memberprinzhang8 Feb '13 - 20:19 
Yes..
Too simple or too difficult.
You know yourself.

AnswerRe: Has anyone luck with this on Window7 ?memberprinzhang15 Feb '13 - 15:57 
yes, I opened the command prompt as an administrator.
Too simple or too difficult.
You know yourself.

GeneralRe: Has anyone luck with this on Window7 ?memberhofingerandi18 Feb '13 - 5:33 
I am sorry, I don't have any further ideas for this issue.
Maybe you could post this question in some forum from microsoft?
 
If you find a solution, please post it here!
QuestionMultithreaded applications?membershaown04712 Jan '11 - 1:06 
Have you tested this for multithreaded applications?
BTW very nice article, I have been using intel vtune which is excellent,
but at same time very expensive Frown | :( . So if this is good enough I may give
it a try.
Answerdepends on what you needmemberhofingerandi13 Jan '11 - 1:01 
Hi,
 
I have now made some tests with a multithreaded test-application. The short answer is, it depends on your needs whether the presented approach works for you, or not. In simple cases, the described approach will work for you without modifications.
 
Now to the long answer:
vsperfcmd:
As far as I understand it, the stand-alone profiler can do, whatever the profiler of the Team-Edition can do. The purpose of the standalone profiler is just, to enable gathering data on PCs that are not running Visual Studio (e.g. at a customers). Thus if you are evaluating the generated .vsp-file on your developer-PC, having the Team-Edition installed, everything is fine.
 
vsperfreport:
In the article, I am evaluating the .vsp-file using vsperfreport with the option /summary:all. The main file CallerCalleeSummary.csv does not contain thread-ids, but process-ids only.
Anyways, the generated file ThreadSummary.csv contains a list of detected threads and their entry-points (you could as well (temporarily) attach a debugger to get the desired thread-id).
When you have the thread-id, you can use vsperfreport with the option /Thread:[thread-id] to generate the corresponding files for this single thread only (cf. MSDN).
 
ProfileResultViewer:
If you directly use the approach in the article for an application with two threads, you might get the following result:
70% main
69% foo
...
31% bar
30% my_thread_entry_point
...
10% baz
* The percentages do not start with 100%
* In the view, the entry-points of the threads are not specially highlighted and therefore not easily distinguishable from the rest
* Multiple calls from different threads are just summed up (it is not clear, if baz is called from main- or child-thread, or both)
 
So, concluding I would say, the approach in the article works well, when you have only a couple of threads with well-separated responsibilities. Anyways, I would not start to play around with the thread-id used by vsperfreport (at least not manually in the command line).
GeneralI think you can try "very sleepy"memberhhhhhhhhhhhhhhhhhh1111111111 Jan '11 - 15:26 
http://www.codersnotes.com/sleepy/
It needs only a pdb file to get the function's name.Very easy to use.
GeneralRe: I think you can try "very sleepy"memberhofingerandi11 Jan '11 - 20:53 
hi hhhhhhhhhhhhhhhhhh11111111,
 
I have tried "very sleepy" before, but I was not so happy with the presentation of the results (actually, the main window looks very much like the .csv-files mentioned in the article). Personally, I prefer treeviews for displaying the results, since I find navigation much easier.
 
Btw, for profiling of .Net-applications I am using NProf, which inspired me for ProfileResultViewer.
GeneralRe: I think you can try "very sleepy"memberKochise13 Jan '11 - 2:09 
Tried (commercial product) GlowCode, which helped me to fix some issues here :
 
http://www.codeproject.com/KB/tree/ctreelistctrl.aspx?msg=1342351#xx1342351xx
 
Kochise
 
In Code we trust !
GeneralRe: I think you can try "very sleepy"memberSunny127013 Jun '11 - 6:24 
"Very Sleepy" is indeed a great open source tool for visual studio profiling. The best is that is has a comfortable GUI and is very easy to use. way better than the stand alone command line profiler from M$. Amazingly, it profiles even without the /profile linker setting in the c++ program, and can run on a machine without VS installed on it. Runs extremely fast too.
GeneralRe: I think you can try "very sleepy"memberMihai Maerean15 Jul '12 - 22:27 
And it works on any normal build, you don't have to create a "profile" build. All it needs are the PDB files. I strongly recommend it.
GeneralMy vote of 5memberHarold Bamford11 Jan '11 - 14:27 
At last, holders of the professional version of VS can do professional work!
 
Thanks!
GeneralMy vote of 5memberChris Meech11 Jan '11 - 4:43 
Excellent. This should be quite helpful to all of those still developing native code. Thanks for sharing it here. Smile | :)
QuestionGreat ! And what Visual Studio 2010 ?memberjean Davy11 Jan '11 - 2:24 
I made a quick search and found nothing, do you know if this is one is usable with VS2010 ?
I will update my vote to 5 if it is Wink | ;)
jean Davy
The less you do, the more you do

AnswerRe: Great ! And what Visual Studio 2010 ?memberhofingerandi11 Jan '11 - 2:48 
In principle it should work, nevertheless I have not tested it, since I do not have VS2010 installed anywhere.
 
I will let you know, as soon I have checked it.
AnswerRe: Great ! And what Visual Studio 2010 ?memberhofingerandi11 Jan '11 - 8:47 
I just tried the procedure above with Visual C++ 2010 Express and it works fine Smile | :)
 
In particular, I was using (as described) the VS 2008 Standalone Profiler together with VS 2010, so you do not need to look for a VS 2010 Standalone Profiler!
AnswerRe: Great ! And what Visual Studio 2010 ?memberjean Davy11 Jan '11 - 22:40 
I update my vote, your are 5/5, congratulations ! Rose | [Rose]
jean Davy
The less you do, the more you do

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 11 Jul 2012
Article Copyright 2011 by hofingerandi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid