Better Visual C++ Debugging, Page 2
Microsoft Symbol Server
For application-level bugs that are typically the result of incorrectly implemented business logic, working out the operation system calls or .NET Framework interactions that influenced the code's execution isn't generally required. For more interesting defects, such as multi-threading problems, memory corruption, or memory leaks, working out the operating system and framework calls that are occurring is a critical clue in working out the cause of the problem. For MFC and ATL applications, diagnosing framework problems isn't too difficult because the source code ships with Visual C++, and the debug builds of these frameworks make stepping through the source code relatively easy.
For the .NET Framework and Windows operating system, the situation isn't as straightforward; debug symbols for these binaries do not ship with the operating system. Microsoft maintains a publicly accessible symbol server at http://msdl.microsoft.com/download/symbols. This URL is not accessible via a browser, but when it is specified in the Visual Studio debugger options as shown in Figure 4, debug symbols for a range of Microsoft products, including Windows and the .NET Framework, will be downloaded. The settings shown in Figure 4 also will cache the files locally in the c:\symbols folder. To confirm the setting has been applied correctly, the first time a debug session has been started and the Microsoft Symbol Server is contacted, the dialog shown in Figure 5 will appear.
Figure 4: Downloading Microsoft Debug Symbols
Figure 5: License Agreement Dialog
With these symbols loaded, the name of functions that have been called from operating system DLLs are visible. Figure 6 shows the before and after Call Stack view of a native application.
Figure 6: Before and After With Debug Symbols
The difference in information about the method that was called in kernel32.dll is quite apparent; rather than simply showing the address of the function called, the name of the method and the length in bytes of the parameters that the method takes is shown.
With complex Frameworks such as .NET, having debug information is even more valuable. Figure 7 shows the before and after views of the .NET finalizer thread. Without debug information, trying to work out what the thread is doing is quite difficult, but with debug symbols loaded, the meaningful function names instantly give an indication of the purpose of the thread.
Figure 7: .NET Application with Debug Symbols
Effectively managing and loading debug symbols is an important skill for a developer to have. Newer versions of Visual Studio make debug symbol management extremely easy, and once the small amount of time to correctly set up and debug symbol loading has been invested, the power of knowing exactly what is happening in all the DLLs loaded within a process will rapidly become apparent.
About the Author
Nick Wienholt is an independent Windows and .NET consultant based in Sydney. He is the author of Maximizing .NET Performance and co-author of A Programmers Introduction to C# 2.0 from Apress, and specialises in system-level software architecture and development, with a particular focus of performance, security, interoperability, and debugging.
Nick is a keen and active participant in the .NET community. He is the co-founder of the Sydney Deep .NET User group and writes technical articles for Australian Developer Journal, ZDNet, Pinnacle Publishing, CodeGuru, MSDN Magazine (Australia and New Zealand Edition) and the Microsoft Developer Network. An archive of Nick's SDNUG presentations, articles, and .NET blog is available at www.dotnetperformance.com.
In recognition of his work in the .NET area, he was awarded the Microsoft Most Valued Professional Award from 2002 through 2007.