Debugging Large Projects with Visual Studio 2010
Working on large projects and collaborating with a large engineering team comes with an additional set of challenges for developers. The debugging features included in Visual Studio 2010 are optimized to improve productivity for these developers. This includes features for better managing your threads, breakpoints, and data while debugging. There are also features that help reduce the amount of time it takes to start debugging your application, and features that improve collaboration across your team.
In a large project, you may have many breakpoints across many different files. This can become a little hard to manage. The breakpoints window has been enhanced with some additional features in Visual Studio 2010 that can help you deal with a larger number of breakpoints. Better breakpoint management is achieved through labeling and searching. In the breakpoints window, you can add custom labels to your breakpoints.
You can then use the search box to filter down to only the breakpoints that you care about. You can search on the labels you've created or any of the other columns that appear in the window. The actions that you take in the Breakpoints window will only take effect on the breakpoints that meet the search criteria. So for example, you can disable all breakpoints, and then enable only the ones that you need for your current debugging scenario.
The Threads window has been enhanced to support debugging large applications that have lots of threads. New features in the Threads window include:
New columns, including a column for call stack location
Flagging of threads that are running in user code or in a module specified by the user
Filtering of threads using string search
Sorting of threads
Grouping of threads into categories
Often the user has to select each thread and look at the Call Stack window to identify threads that are of importance for debugging.
In Visual Studio 2010, the call stack is provided in the threads window. The call stacks are collapsed by default and can be expanded by clicking on the down arrow in the Location column.
The user can now flag threads that are running either in the user code or are running in a specific module. This feature is provided by the "Flag Just My Code" and "Flag Custom Module Selection" menus under the Flag icon.
The "Flag Just My code" menu option will flag all the threads that are running in user code. User code includes all of the modules for which symbols are loaded. The "Flag Custom Module Selection" menu will flag all the threads that are running in modules specified by the user. Clicking on the menu item will open a modal dialog listing all the modules currently loaded in the application. The user can check one or more modules to flag threads running in those modules.
The user can filter threads using a string search to search in all visible columns of the Threads window including the topmost frame of the call stack. Threads that do not match the keyword will be filtered out. The filter can be changed to include all the frames of the call stack, if the "Search Call Stack" button is enabled.
The user can also sort threads by clicking on the column headers. Sorting is supported for all columns in the Threads window.
Threads can be grouped using Category, Priority, Name, Flagging Status, Process Name, etc. Grouping is supported through the "Group By" dropdown that lists all the categories by which threads can be grouped. The following screen shot shows the grouping of threads by Category. This is particularly useful in scenarios where you want to focus on a particular subset of the threads in a large application.
Figure 1: Grouping Results
When you start debugging an application, the debugger loads the symbols (PDB files) for the modules loaded by the process. In earlier versions of Visual Studio, there were two options for loading symbols, manual and automatic. Automatic loading can be especially painful for large applications. For example, in some scenarios, Visual Studio itself may have over 100 modules loaded. If the user chooses automatic symbol loading, they may be waiting several minutes before they will be able to begin debugging, especially if the symbols are located on a symbol server or remote share. Manual symbol loading, either through the Modules or Call Stack windows, will get you started a bit faster. However, you will still be spending time clicking through the menus for each individual module.
Visual Studio 2010 improves this scenario by providing users a way to specify which modules they would like to have loaded automatically. For example, to only automatically load symbols for a specific set of modules that are important for your debugging scenario, you first select "Only specified modules" in the symbol settings dialog box.
Figure 2: Symbols, Debugging, Options Dialog Box
You can then click on the "Specify modules" link and enter the list of modules that are important to you as seen in the screenshot below. In this case, the user plans on debugging the debugger features of Visual Studio. The user has included the modules for devenv.exe, the main executable for Visual Studio, and vsdebug.dll, which includes the interesting code for this debugger session. This is where there user is likely going to want to set breakpoints and step through code, which will require symbols to be loaded. The user has also included ntdll.dll, which is an important system library. The user will need to load symbols for this library in order to resolve calls into the library that appear on the call stack.
Figure 3: Symbols to load automatically dialog box
You will notice that there is also an option for always loading symbols that are located next to modules. If you are debugging a project that is built locally on your machine through Visual Studio, this will ensure that symbols for those modules are loaded. It will also make sure that modules are loaded for certain binaries, for example the VC runtimes, that are installed into a Windows directory along with their symbols.
Now when you begin debugging your application, it only takes a few seconds to get started. You can verify which symbols are loaded by viewing the Modules window. In the screenshot below, you can see that symbols have only been loaded for the modules that were specified, or the VC modules that are installed in the same directory as their symbols.
Data inspection is a major part of debugging. The classic ways to inspect the values of an arbitrary variable is to either add it to a Watch window or hover the mouse over the variable to see it in a DataTip. The problem with the Watch windows is that they can easily get cluttered with variables that are no longer relevant in the current context. This problem is exacerbated when you have a large project with many components. As your application executes through code in different places, there are different variables that you want to see and there is only a limited amount of visible space in each Watch window. Constantly adding and removing variables from the watch window uses up valuable time. DataTips, while very convenient, only allow you to look at one variable at a time. Additionally, if you are looking at an object, it can require a few extra clicks to dig down to the specific member you want.
Visual Studio 2010 tackles this problem by allowing you to persist DataTips on the screen. When viewing a DataTip, simply click on the pin icon and the data you care about will stay on the screen.
Once the DataTips are persisted, they have two possible states. The default state is that they are pinned to the source file. This means that they will move with the source code as you scroll. If you scroll them off the screen or switch to a different source file, they are invisible. This allows the data to only be visible when it is relevant in debugging. The other possible state is floating, which can be toggled by clicking the pin icon on the DataTip menu. A floating DataTip is always visible and can be moved anywhere, even onto another monitor. This allows you to make the most of the screen real estate you have.
In either state, the user has the ability to add comments to the DataTip. This is accomplished by clicking on the double chevron icon to expand the comments section, and then simply typing in the comment.
Figure 4: Adding DataTips
When creating a DataTip for an object, it’s possible to go back and add children to the visible portion of the DataTip. To do this, simply expand the object and click on the pin icon for any of the children that you wish to add.
When working across a large team, you may experience crashes in the application that occurred in code produced by a different part of your organization. When sending bugs over to other teams, it is common practice to include a crash dump with the bug. This is especially important when the crash is not easy to reproduce by following a set of simple steps.
The dump debugging scenario has been significantly enhanced in Visual Studio 2010. The first major enhancement is the new Minidump File Summary page. This page changes how minidumps are opened and how you start debugging a minidump. You can still double-click the dump file in Windows Explorer to open the file in Visual Studio. You can now also open the file from the Open File dialog within Visual Studio. In previous releases you could open a minidump file from the open project dialog box. This scenario is no longer supported.
Once a dump is open, Visual Studio takes you to the Minidump File Summary page. This page will provide information about the contents of the minidump file without actually debugging it. Information provided on this page includes:
The process name
The process architecture (x86, x64)
The exception that occurred
Whether or not there is heap information in the dump
The Operation System
The versions of the .NET runtime that were running in the process
The list of all modules that were loaded in the process, including version numbers and paths
There are a number of actions that you can take on this page by clicking the Action links on the right side of the page. These Action links allow you to:
Set your symbol paths
Begin debugging with any available debug engines
Copy the contents of the page to the clipboard
Figure 5: Action Links
The modules list is also searchable, so that it is easy to find the information about a particular module that you are interested in. It also makes it easier to find out if any particular module was loaded into the process at all. The module information is particularly helpful when you want to make sure your symbol paths match the version of the files about to be debugged.
The next big change in dump debugging is the fact that you can now debug managed minidumps. When opening a dump of a process that contains version 4 of the .NET runtime, you will have the option to debug with managed. You will also notice that you now have a notification on the dump summary page that will allow you to toggle the IL interpreter on and off. The IL interpreter allows you to make function evaluations while debugging the minidump even though there is not an active process in which to perform the evaluation. Instead, the evaluation is interpreted from within the Visual Studio process.
With large projects, there is always a greater chance of encountering bugs that belong to a different developer or a different team. Visual Studio 2010 allows you to better transfer information learned from your initial investigation to another developer. First, the developer can set up the breakpoints and DataTips that illustrate where the problem lies.
Figure 6: DataTips Example
Next the developer can export their breakpoints and DataTips to XML files. Exporting DataTips is accomplished through the Debug menu while exporting BPs is accomplished via the toolbar item on the Breakpoints window. The developer can now send over the Breakpoints and DataTips to be imported and used by the second developer. Ideally, this transfer can be accomplished through Team Foundation Server by simply attaching the xml files to the relevant bug, as shown in Figure 7.
Figure 7: Attaching DataTips XML Files to Bugs
To get started on this bug, a developer will simply need to download the attached files and import them using the same menus that were used for exporting. The developer now has what they need to continue to debug the issue.