`
blogfeifei
  • 浏览: 1194994 次
文章分类
社区版块
存档分类
最新评论

Improving Visual C++ Debugging with Better Data Display

 
阅读更多

The ability to customize the summary information for a type in the Watch and Variable windows in the Visual C++ debugger has existed for a long time, and for this reason, the feature doesn't receive a lot of coverage in new articles and books on the Visual C++ debugger. Even though the display information customization feature is old, it is still incredibly useful for boosting developer productivity, and it is worth reminding more experienced developers that the feature still exists and demonstrating to more recent converts to Visual C++ how it can be used.

The key to customizing type display in the Visual C++ debugger is the autoexp.dat file. This file, which is located in the\Common7\Packages\Debugger folder in a Visual Studio 2008 installation, uses a custom syntax to instruct the debugger how to display the summarized information for a particular type. At its simplest form, the syntax is type=[text]... For a simple type, such as the MFC CPoint class, the autoexp.dat display specification is CPoint =x=y=, which translates to the display shown in Figure 1.

Figure 1: Simple Type Information Display: CPoint

Without the autoexp.dat entry, CPoint will display as shown in Figure 2, which is obviously a lot less useful from a debugging perspective.

Figure 2: CPoint Debug Display without autoexp.dat

The debugger will use the display information for a base type when a derived type has no custom display information of its own. For the case where a type has multiple base types that have custom display information, the first type in the inheritance list that has custom debug information will be used. In this case, defining the debug display for the derived type by combining the autoexp.dat entries for the base types will be the best course of action.

C++ Debug Visualizers

Prior to the release of Visual Studio 2005, autoexp.dat was fairly static from release to release. Visual Studio 2005 saw the release of debug visualizers in the managed world; they allowed for the display of type information in a custom dialog box, and although the increase in debug display in Visual C++ was not as dramatic, the debug display engine in Visual C++ did receive a significant upgrade, and there was a huge increase in autoexp.dat type coverage. The RTM version of Visual C++ 2003 had a 6KB autoexp.dat, whereas the same file for RTM Visual C++ 2005 was 53KB.

One of the areas that received the most focused coverage increase was the STL. Prior to Visual C++ 2005, inspecting the contents of a STL container often required the use of raw pointers and the memory window to trace through the in-memory layout of the collection manually. Less tedious but no less frustrating was the experience of stopping the debugger, adding code to spit out the contents of the collection to the console, and re-running the application.

Figure 3 shows the before- and after-Visual Studio 2005 display of a STL vector containing three elements in the debugger.

Figure 3: STL Vector Display in the Debugger

Looking at the display in Figure 3, it is apparent that there are two display customizations taking place: the summary information for vector is being customized to display the vector length followed by the vector contents; the member variable display is being customized to display each vector element rather than the raw pointers to _Myfirst and _Mylast. The autoexp.dat display for vector is shown below, and the two separate specifications for preview display and children displaay can be seen. std::vector<*>{ children ( #array ( expr : ($e._Myfirst)[$i], size : $e._Mylast-$e._Myfirst ) ) preview ( #( "[", $e._Mylast - $e._Myfirst , "](", #array ( expr : ($e._Myfirst)[$i], size : $e._Mylast-$e._Myfirst ), ")" ) ) }

The #array keyword tells the debugger that it should loop over the object for a certain number of iterations specified using the size variable, with the $i pseudo-variable specifying the element number. The other supported collection iteration constructs are #tree (which allows head, skip, size, left, and right variables to be defined) and #list (head, size, and next are specified).

In addition to the preview and children display settings that can be specified, it is possible to customize how strings are displayed by using the built-in text visualizer shown in Figure 4. The display setting for the ATL CComBSTR is shown below: ATL::CComBSTR{ preview ([$e.m_str,su]) stringview ([$e.m_str,sub]) }

In both cases, the BSTR member variable is shown by using the $e identifier (which is a pseudo-variable for the object being displayed) followed by the m_str member variable. The difference between the preview and stringview settings is that the string format specifier - su instructs the debugger to display a string with double-quotes, and sub is a string with no quotation marks. Figure 4 shows the display differences.

Figure 4: Customizing stringview display

Advanced Debug Display

For types that have complex display requirements, the Visual C++ debugger supports the use of a conditional display using #if, #else, #switch, and #case statements. The COM VARIANT type is clearly a type that requires a conditional debugger display based on the data stored in the VARIANT, and the autoexp.dat definition, which is partially reproduced below, confirms this: tagPROPVARIANT|tagVARIANT|PROPVARIANT|VARIANT{ preview( #switch ($e.vt) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Base Types ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #case 0 ( #("Empty") ) ; VT_EMPTY #case 1 ( #("NULL") ) ; VT_NULL #case 2 ( #("I2 = ", $e.iVal) ) ; VT_I2 #case 3 ( #("I4 = ", $e.lVal) ) ; VT_I4

Given the lack of clear documentation on the syntax supported by auotexp.dat and the poor debugging experience of the autoexp.dat expression, writing more complex expressions in C++ code will generally be more appealing. The use of C/C++ code to generate debug display information is accomplished via the $ADDIN command and the Expression Evaluator (EE) Add-In API. EE Add-In DLLs are C-style DLLs that expose functions of a specific signature, and a $ADDIN entry in autoexp.dat links a type to a DLL and function name used to display it.

The EE Add-In function is passed the memory of the object that needs to be displayed, a string buffer to store the results of the display, and a helper structure to retrieve additional information about the object being displayed. Once the EE Add-In has the memory address of the data structure that it is customized to display, it simply can cast the pointer to the correct type and use any set of string building and manipulation functions that it chooses to extract data from the type and place it in the return string. A sample EE Add-In DLL project ships with Visual C++ 2008.

Conclusion

Whether you're a developer writing code as part of a small team, a framework developer producing code libraries for other members of your organization to consume, or a ISV producing components for the larger developer community, it is very important that the debug-time experience with the types you create is a good one. One of the fastest and most effective ways that the debug experience can be improved is by providing autoexp.dat entries that make inspecting the important member variables within your type easy.

For simple types, autoexp.dat entries are easy to create and can be completed in a couple of minutes. For more complex types, particularly those that are heavily based on templates, autoexp.dat now supports a rich syntax that can be used to provide detailed information. For types that have very specialized or complex display requirements, an add-in DLL can be written to process the data within a type using C/C++ and package it into a string for debugger consumption.

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 debuggiing.

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.

Author:Nick Wienholt


另一篇:autoexp.dat入门

第一步,打开调试时对变量进行Auto Expand功能


VC在调试状态下,会以三种方式显示一个变量的内容,分别是preview,childrenstringview,例如下图所示,定义一个std::string类型的变量,鼠标悬停后显示的内容就是preview,children是点击”+”符号后显示的内容,而对于含有字符串内容的变量,点击上放大镜符号,会弹出单独的窗口内显示其中的字符串内容。

针对这部分功能,微软提供给开发者一个自定义接口,就是通过修改autoexp.dat这个看起来其貌不扬的小文件,该文件放在“$(VSINSTALLDIR)\Common7\Packages\Debugger”目录下,微软并没有提供关于这个文件的说明文档,只是在这个文件开始的地方有一些简单的说明,好在这个文件的结构并不复杂,通过分析现成的例子,大概也能了解个八九不离十。
先以一个例子说明如何使用这个文件,打开这个文件,在”[Visualizer]“这一行下面添加下面的内容

MyClass{
preview
(
#
(
"Hello,World!"
)
)
}

然后启动VC, 随便定义一个名字为MyClass的类,当使用VC的调试器查看这个类的内容时,你会发现显示的调试内容已经变成了”Hello,World!”


下面我们再来看一个稍微复杂的例子,比如一个自定义的数组类

structMyClass
{
intsize[32];
int*buf;
};

调试这个数据结构的时候,我们想显示这个数组的元素个数,每个元素的值,以及每个元素的奇偶性,那么需要这样定义:

MyClass{
preview
(
#
(
"[count is", $c.size,"]",
"(buf",
#array
(
expr: $c.buf[$i],
size: $c.size
): $e,
")",
"(odd",
#array
(
expr: $c.buf[$i],
size: $c.size
): $e&1,
")"
)
)
}

实际显示效果为:

其中,$c表示当前所定义数据结构,#array表示用数组形式显示内容,$i表示数组中的每个元素的索引,$e表示每个元素的值,同样,可以用#tree#list分别用来显示树形数据结构和链表类型的数据结构。
autoexp.dat中已经对VC提供的缺省的STL数据结构做了定义,比如vector,map等,通过分析这些定义,还可以挖出很多有趣的功能,比如用#if/#else/#switch/#case等条件判断功能,在处理一些比较复杂的组合类型的数据结构时,就非常有用。
下面是一份我制作的,针对另一份较为常用的STL库STLPORT中部分数据结构的定义,适应STLPORT 5.2版本,使用方法是将其中的内容拷贝到autoexp.dat的”[Visualizer]“段中,大家有兴趣的话可以拿去使用,如果发现错误或者有补充的话,请通知我。下载:autoexp.dat(1.2K)



分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics