Cannot get SuspendProfile / ResumeProfile to work
I've been tearing my hair out about this for days, and I give up. Can someone out there help me?
I'm working on an extremely large project using VS2008 Team Edition, and I'm trying to profile just the pieces that I am responsible for. Simply profiling the entire thing and then filtering isn't an option because the 'interesting' parts aren't until 5 - 10 minutes into the run (at least), and if I try to run it that long, I get 100GB+ vsp files which Visual Studio just crashes when it tries to analyze (if my disks can even hold them) .
So I am attempting to use the SuspendProfile / ResumeProfile functions to just get the information I am interested in. However, I can't get them to work.
Here is what I am doing. Note that this is about the 50th different arrangement I've tried, so apologies if it seems baroque...
First, in WinMain:
#include <strsafe.h> #include "C:\\Program Files\\Microsoft Visual Studio 9.0\\Team Tools\\Performance Tools\\PerfSDK\\vsperf.h" extern int32_t currentSuspendCount = 0; int32_t APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLineOrig, int32_t nCmdShow ) { #if defined(PROFILE_BUILD) // Switch from Global profiling to thread profiles
// I've also tried it without this -- see below. PROFILE_COMMAND_STATUS profileResult = StopProfile( PROFILE_GLOBALLEVEL , PROFILE_CURRENTID ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("StopProfile returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } profileResult = StartProfile( PROFILE_THREADLEVEL, PROFILE_CURRENTID ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("StartProfile returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } profileResult = SuspendProfile( PROFILE_THREADLEVEL, PROFILE_CURRENTID ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("SuspendProfile (WinMain) returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } ++currentSuspendCount; #endif ... }(SE_CRASH is our special assert macro, BTW)
Then, in several places around the code, I've added the following:
#include <strsafe.h> #include "C:\\Program Files\\Microsoft Visual Studio 9.0\\Team Tools\\Performance Tools\\PerfSDK\\vsperf.h" extern int32_t currentSuspendCount; extern bool s_alreadyFinished = false; void MainSystemLoop() { ... #if defined(PROFILE_BUILD) PROFILE_COMMAND_STATUS profileResult; if ( !s_alreadyFinished && isTimePeriodIWantToProfile() ) { profileResult = ResumeProfile( PROFILE_THREADLEVEL, PROFILE_CURRENTID ); DebugMessage(L"ResumeProfile returned %d", profileResult ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("ResumeProfile (Player) returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } --currentSuspendCount; SE_CRASH( currentSuspendCount == 0, "Incorrect suspend count " << currentSuspendCount ); } #endif
// This is what I want to profile mySubsystem->update(); #if defined(PROFILE_BUILD) if ( !s_alreadyFinished && isTimePeriodIWantToProfile() ) { profileResult = SuspendProfile( PROFILE_THREADLEVEL, PROFILE_CURRENTID ); DebugMessage(L"SuspendProfile returned %d", profileResult ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("SuspendProfile (Player) returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } ++currentSuspendCount; } else if ( isPastTimePeriodIWantToProfile() && !s_alreadyFinished ) { s_alreadyFinished = true; profileResult = StopProfile( PROFILE_THREADLEVEL , PROFILE_CURRENTID ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("StopProfile (Player) returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } DebugMessage(L"Done. Shutdown now. Final suspend count %d", currentSuspendCount ); } #endif ... }
I then create a performance session using Analyze->Launch Performance Wizard, select "Instrumentation", right-click on the session in Performance Explorer and choose Launch With Profiling.
And the result is... nothing, basically. When I exit, I get a profile of the all of the pre-WinMain activity (lots of initialization of globals) and nothing else. mySubsystem->update() is nowhere in the list. Also, the program does not run particularly slowly during the time I am trying to profile.
The DebugMessages confirm the SuspendProfile / ResumeProfile functions are being called and are returning 0. The currentSuspendCount is 1 on the end message (which is what I would expect).
I have also tried #if 0'ing out all the code in WinMain, to make sure the StopProfile & StartProfile are not the problem. When I launch with profiling suspended, I get the same results, except that since it doesn't profile the code before WinMain either, I get a "No profiling data. Either none was colleced or it was all filtered out" message and "File contains no data buffers" messge in output.
Does anyone have any ideas what I am doing wrong?
Also, is there ANY way to get the actual suspend / stop status from the VSPerf DLL? I'm checking that I'm calling Resume and Suspend the correct number of times, but in a code base the size of ours, I can't 100% promise that someone else isn't messing things up. Having some status functions available would make this far easier to debug on my side.
This is with Microsoft Visual Studio 2008 Version 9.0.30729.1 SP under Windows XP Professional 5.1.2600 SP 3 Build 2600, in C++.
Thank you in advance for any help!
-Ian
Answers
- I believe the main reason you don't have profiing data is because you can't start profiling on a thread level when you have it turned off on a global level.
There is a blog entry that you might find useful: http://blogs.msdn.com/angryrichard/archive/2005/01/16/354194.aspx - it has an example on how to limit the amount of data in instrumentation (trace) mode.
If you don't want to use command line tools, tired of modifing your code on API level, and you know when in application lifetime the code you're interested in runs, you may try to Launch the app with Profiling Paused and then click Resume and Pause Collection when you need to limit amount of data.
Unfortunately, I can't attach the pictures here, but you can find Launch With Profiling Paused when you right click on Performance Session or by clicking on down arrow on a Launch With Profiling icon on Performance Explorer toolbar. You should see the Resume Collection and Stop Collection buttons at the Performance Explorer toolbar as well after you Launch with Profiling Paused (you may want to make Performance Explorer window large a bit to see all menu options available).- Proposed As Answer byAnna Galaeva Tuesday, September 22, 2009 8:24 PM
- Marked As Answer byRoahn LuoMSFT, ModeratorFriday, September 25, 2009 3:30 AM
All Replies
- I believe the main reason you don't have profiing data is because you can't start profiling on a thread level when you have it turned off on a global level.
There is a blog entry that you might find useful: http://blogs.msdn.com/angryrichard/archive/2005/01/16/354194.aspx - it has an example on how to limit the amount of data in instrumentation (trace) mode.
If you don't want to use command line tools, tired of modifing your code on API level, and you know when in application lifetime the code you're interested in runs, you may try to Launch the app with Profiling Paused and then click Resume and Pause Collection when you need to limit amount of data.
Unfortunately, I can't attach the pictures here, but you can find Launch With Profiling Paused when you right click on Performance Session or by clicking on down arrow on a Launch With Profiling icon on Performance Explorer toolbar. You should see the Resume Collection and Stop Collection buttons at the Performance Explorer toolbar as well after you Launch with Profiling Paused (you may want to make Performance Explorer window large a bit to see all menu options available).- Proposed As Answer byAnna Galaeva Tuesday, September 22, 2009 8:24 PM
- Marked As Answer byRoahn LuoMSFT, ModeratorFriday, September 25, 2009 3:30 AM
So I've now tried it, and it is true that if use PROFILE_GLOBALLEVEL, the profiler works (hurrah!). So that fixes about 75% of my problem. Thank you.Having said that, this is an application with multiple threads, and I'd still like to know if there is a way to get PROFILE_THREADLEVEL to work -- that is, to only profile the one thread I'm interested in.Here's what my code looks like now: I removed all the code in WinMain and just have:#include <strsafe.h> #include "C:\\Program Files\\Microsoft Visual Studio 9.0\\Team Tools\\Performance Tools\\PerfSDK\\vsperf.h" extern int32_t currentSuspendCount; extern bool s_alreadyFinished = false; void MainSystemLoop() { ... #if defined(PROFILE_BUILD) PROFILE_COMMAND_STATUS profileResult; if ( !s_alreadyFinished && isTimePeriodIWantToProfile()) { profileResult = StartProfile( PROFILE_GLOBALLEVEL, PROFILE_CURRENTID ); DebugMessage(L"StartProfile returned %d", profileResult ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("StartProfile (Player) returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } --currentSuspendCount; SE_CRASH( currentSuspendCount == 0, "Incorrect suspend count " << currentSuspendCount ); } #endif // This is what I want to profile mySubsystem->update(); #if defined(PROFILE_BUILD) if ( !s_alreadyFinished && isTimePeriodIWantToProfile() ) { profileResult = StopProfile( PROFILE_GLOBALLEVEL, PROFILE_CURRENTID ); DebugMessage(L"StopProfile returned %d", profileResult ); if ( profileResult != PROFILE_OK ) { LPCTSTR pszFormat = TEXT("%s %d.\0"); TCHAR* pszTxt = TEXT("StopProfile (Player) returned"); TCHAR tchBuffer[256]; StringCchPrintf(tchBuffer, 256, pszFormat, pszTxt, profileResult); SE_CRASH( false, tchBuffer ); } ++currentSuspendCount; } else if ( isPastTimePeriodIWantToProfile() && !s_alreadyFinished ) { s_alreadyFinished = true; DebugMessage(L"Done. Shutdown now. Final suspend count %d", currentSuspendCount ); } #endif ... }e.g. I changed the Suspend/Resume to Start/Stop and changed to PROFILE_GLOBALLEVEL.So as I said, this works, but if I change it to PROFILE_THREADLEVEL, it doesn't. Any ideas on how to get THREADLEVEL to work?Thanks again,-Ian- To isolate the performance data on a thread level you may try to turn off the profiling on threads you're not interested in, thus you'll get data on your thread only.
- Unfortunately, many of the threads I want to exclude are not ones I can easily add code to. Some (such as DirectX and DirectSound) are completely opaque; others are ones I technically have source code for but which are made by other groups in the company and which we keep as prebuild libraries -- recompiling those can require considerable time and difficulty (as they involve switching development environments and using different compilers).Unfortunate. But if there's no way to get performance data for just a single thread, then there isn't. I'll try excluding what threads I can...-Ian
- Hi Ian,
You're trying trying the most advanced user scenarios, which can be painful. :)
You can also try to control the amount of data from the UI - by launching with Profiling Paused and controlling Suspend Collection / Resume Collection.
If your app is CPU bound, you may also try the sampling mode - it generates less data, and you can try the attach / detach scenarios as well. - Ugh, I hate it when moderators mark a question as "accepted as an answer" when you're still talking. It feels rude to me, like you've been interrupted in the middle of a conversation. Is it just me?Anyway, back on topic...After looking at this a bit more, I decided that, rather than trying to disable profiling on the other threads, I would just play tricks with my code to stop them from running during this time, such as holding mutexes I knew the other thread held when running. So I never tried to get PROFILE_THREADLEVEL to work after my last post. I'd still wish for an easier way to profile a single thread.But I did get the information I needed out of the system, so a big thank you to Anna for your help. You really helped me out when I needed it.Thank you,-Ian


