In Visual Studio 2013 Update 2 and also in the earlier CTP releases of Visual Studio 2015, we released a memory diagnostic tool that allowed developers to take heap snapshots of their application and then examine the heap contents upon terminating their application. The initial release supported viewing managed and native objects in the heap, and an update in the first Visual Studio 2015 CTP added support for native type derivation and value inspection.
While this tool was a good start in providing Visual Studio developers with an in-box memory profiler, it lacked the ability to easily examine heap contents at a specific program state since the entire program had to be shut down in order to drill deeper into the data.
Improved Memory Profiler for Preview
Now there is a new and improved memory profiler available in Preview that allows developers to leverage the debugger’s powerful control of program flow and examine their app’s heap contents at any break state. Here is a great overview of the new memory profiling experience with an in-depth feature summary complete instructions on activating the feature (Where do I find it? section). Upon following these instructions to activate the tool for the first time, simply pressing F5 will launch the new profiler during the debug session. There is now no longer a need to terminate the app to view the heap snapshots!
The rest of this post will focus primarily on using the new tool with native applications and details the specifics of the tool’s workflow.
Walkthrough: Profiling a Native MFC App
To show off the new memory profiler, an MFC open-source chip-tune sequencer called FamiTracker has been loaded into Visual Studio and modified slightly to build with the new compiler. After enabling the memory profiler via the reg key and starting a debug session on the app with F5, the tool loads and soon the live memory usage is shown and the heap snapshot reel appears below it:
Snapshots can be taken at different points in time to capture the heap state. Instance values are only viewable for the most recent shot and when in a break state.
In this walkthrough, the initial program state for FamiTracker is the initialized sequencer UI:
FamiTracker Initial Sequencer UI
Another dialog called the instrument editor can be launched to edit the properties of each instrument:
FamiTracker Instrument Editor Dialog
Using the new memory profiler, we will take heap snapshots across these two program states to better understand the runtime memory consumption of this app.
First we take a baseline snapshot to store the initial heap contents.
The instrument editor dialog is opened, which triggers a breakpoint in the code and begins a change in program state. This function initializes the instrument editor dialog and calls a few other helper functions that will help create the instrument editor UI.
By taking a snapshot at the breakpoint above at the beginning of OnInitDialog(), we can see the heap contents of the app just before the instrument editor dialog starts allocating objects. A snapshot will list the object types, counts, and memory footprint.
Since we are at in a break state, the instance of each type can be viewed by double-clicking a row, or the icon:
Selecting a type will present a list of all of the allocations of that type, complete with values and allocation call stacks for each instance. Below are all of the instances of CCHannelHandlerN163[]:
After continuing through some breakpoints, the instrument editor dialog finally pops up and a second snapshot is taken. Upon taking a second snapshot, we can see the total amount of memory consumed during the entire invocation of the instrument dialog, in this case a little over 50 KB.
The +51,227 bytes and +405 allocations reveal the total additional memory consumed by launching the instrument editor dialog relative to the baseline snapshot #1, and the top two numbers reveal the total heap contents. Clicking either of these will launch the diff snapshot and list count and type of the additional objects that have exist since the previous snapshot. To see all object in the heap snapshot, simply click one of the two top details in the snapshot that tells the total consumption. Shown below is the second snapshot diffed to the first:
We can even examine the memory overhead more precisely by putting breakpoints at the beginning and the end of a particular function, and taking two snapshots for comparison. The InsertPane function is called twice during the instrument dialog invocation, so it would be nice to see the specific impact it has on the overall execution. Two breakpoints have been set to encapsulate the InsertPane function, and a snapshot is taken at each break. In the snapshot details of the second snapshot below, we are able to clearly see that this function is consuming about 12 KB within its 20 lines of code.
The snapshot diff technique allows any region of your code to be analyzed for memory footprint and provides a powerful tool for exposing memory leaks that may occur between states of execution. Once two snapshots are taken, the latest snapshot will automatically show the diff to the previous one. To diff non-sequential snapshots, simply click the top right corner of a snapshot and select a snapshot to diff against.
This can also be done by clicking the Compare to combo-box at the top right of an opened snapshot select the desired snapshot.
“Hide Undetermined Types” View Setting
Due to the nature of the tool’s type derivation from PDBs, some types cannot be determined due to lack of symbols, or due to the use of custom allocators. We plan to expose the extensibility model for custom allocators in a future blog post. Since it is important to not hinder the workflow for examining user defined types with available symbols, we have chosen to hide these object by default.
These undetermined objects can easily be seen by selecting the View Settings pull-down at the top of the heap view and unchecking the option.
This will list the “Undetermined” type entry in the heap table and in turn will reveal all the instances in memory, complete with the allocation call stack. Below, the object instance at address <0x1E1148> is shown:
In the case that there are not any determined types and the default hide setting is enabled, the heap table will show a watermark with the following string:
Known Issues
As mentioned in Charles’s blog post, the currently supported native application types are Win32, MFC and Windows Store apps. All C++ projects must be built with the new Visual Studio 2015 (v140) compiler to work properly with this tool. The following scenarios are not supported:
- 64-bit targets
- Remote debugging of all project types
- Attach to process
Closing Remarks
This is an early version of this exciting new feature, please share your thoughts and help us make this a valuable and powerful tool for your memory diagnostic needs!
Thanks,
Adam Welch
Visual C++ Team
A good number of our company's products are only ever compiled as 64-bit, and more soon will be. Will 64-bit support be available in the RTM? Seems kinda important these days.
This looks very promising and I'd love to try it out. Alas, I don't seem to be able to with VS2015 Preview. Perhaps I'm missing something.
I made the registry changes to enable the performance debugger. Then I created a new Win32 Project with the wizard and accepted all the defaults. Then I started debugging, clicked Debug -> Show Diagnostic Tools, and was greeted with a message saying "The diagnostic tools failed unexpectedly. The Diagnostics Hub output in the Output window may contain additional information". The output window however simply says "Failed to initialize the Performance Debugger".
If on the other hand ALT+F2 to Start Diagnostic Tools Without Debugging, there Memory Usage tool is not available. I have to click Show All Tools for it to show up (greyed out) and the tooltip says "Memory Usage tool does not support the selected target". The selected target is the Startup Project, which is the C++ ConsoleApplication1 project created by the wizard which is using the default v140 platform toolset.
@Sebastian, It is on our backlog, thanks for the feedback to help us prioritize it.
@ Javier, Just want to make sure that you restarted the VS after registry change?
Please try the following steps to bring VS to the consistent state.
1. Restart the VS
2. Try your scenario
3. If you still have issues, run "devenv /setup" from VS SDK command tool / command line from the VS installed
location.
Ex: > C:Program Files (x86)Microsoft Visual Studio 14.0Common7IDEdevenv /setup
Please let know if you still have issues.
@Iyyappa, I tried running devenv /setup as you suggested, then reapplied the registry change and started VS2015 Preview, but I still see the exact same behavior.
Is there any way to turn on debug logging for this tool? The current logging is not particulary useful. I tried running devenv /log but the activity log didn't have any interesting entries. I then tried attaching another instance of VS as a debugger and I saw dozens of exceptions of all kinds while using the IDE and trying to activate the tool; some of them had a callstack within the DiagnosticsHub VSPackage, but it's hard to tell what the root cause is from that.
@Javier,
Thanks for trying it.
Which OS are you running it on? If you are running on windows 7, can you make sure KB 2882822 is installed.
Also can you confirm whether non-debugger version of native memory profiling scenario is working?
For perf debugger version, does other tools such as CPU profiling works fine?
You can turn on the logging of Diagnostics hub by adding this registry key.
reg add HKEY_CURRENT_USERSoftwareMicrosoftVisualStudio14.0_configDiagnosticsHub /v LogLevel /t REG_SZ /d All
You could also try these steps again.
1) Run devenv /setup
2) Run devenv /updateconfiguration
3) Start VS
4) Stop VS
5) Set the registry key
6) Start VS
Our local testing could not able to reproduce this issue, so we really wanted to understand your scenario better.
You can also send a mail to iyyappam@microsoft.com & adwelch@microsoft.com to help us resolve the issue.
Is it supported or do you have any plans to support plugging in third party memory managers into this?
I'd like to reiterate the importance of 64-bit support. Really, these days, I'd expect 64-bit support would come first, with 32-bit somewhat of an afterthought.
@Sebastian & Ben:
We are working on 64-bit support, but I currently am unable to say when this will be available inside Visual Studio. I will post an update as soon as this has been determined.
@Erik:
We currently do not support any third-party tools. Which third-party tools that you want to use with Visual Studio? Also, do they provide specific capabilities that are not available in the Visual Studio memory tool?
If you would like to follow up in more detail, feel free to shoot me an email: adwelch@microsoft.com
Thanks for the feedback!
Adam
It appears these may be adequately used in education, which is probably good news for C++.
I wanted to inform that this tool is broken on Win 7 (even with patch KB2882822). We will fix this in future iterations.
Thanks @Javier for your feedback.
kraids lair in famitracker xddd
This is all some very nice over-engineered rocket science but how does it help me find a corrupt pointer in my heap? I see boatloads of information but none of it appears useful. What is the procedure for diagnosing a corrupted heap?