wiki:DebuggerVisualizers

Version 8 (modified by fkonvick, 15 years ago) ( diff )

Initial revision

Improving Boost Docs Project


Boost Debugger Visualizers

http://svn.boost.org/trac/boost/wiki/DebuggerVisualizers


About Debugger Visualizers

Debugger visualizers enable developers to view data during debugging sessions in a human-friendly way.

Though each debugger UI usually tries to display the data as nicely as possible, with complex types such as those of BOOST, the general approach usually does not allow the user to quickly get the information she needs. That's where debugger visualizers come to help: they are user- or library vendor-defined formatting rules that help the debugger UI to deliver data in a comprehensible form.


Visual Studio 2005

Microsoft Visual Studio 2005 is the first of the Visual Studio versions to support user-customizable debugger visualizers. For CLR languages, such as C#, the possibilities are even more powerful, and documented. Native C++ debugger visualizers are not documented, though it was claimed by some people from Microsoft that some docs are coming - so if you know of some, please let us know!

Native C++ Debugger Visualizers for MSVS 2005

The formatting rules are stored in the autoexp.dat text file (the [Visualizer] section) which can be found at

Microsoft Visual Studio 8\Common7\Packages\Debugger\autoexp.dat

New visualizers can be installed by appending visualizer code to the [Visualizer] section. In the future, we would like to have an installer for this.

Existing visualizers

Visualizers for boost::multi_index_container stored at http://svn.boost.org/svn/boost/sandbox/boost_docs/sandbox

Other existing visualizers will be added shortly. If you're lacking a visualizer for your favorite type, let us know or write one yourself - it's easy!

HOWTO

Our main source of knowledge is the blog at virtualdub.org. I'll try to repeat some of the basic info you need to get started.

Visualizers (visualizer rules) have this general format:

; Comments start with semicolon
pattern [| pattern ...] {
  preview (
    preview_expression
  )
  stringview (
    stringview_expression
  )
  children (
    children_expression
  )
}

The stringview section is used for formatting the data to text, HTML or XML (not covered by this guide yet). The preview section is used to format the data for one-line view. This is used in tooltips (when you hover over variables in code editor) and for the watch window. The children section enumerates child nodes (for structured types). All sections are optional.

Rule pattern matching

When visualizing a variable, the IDE looks at its (most derived) type. It then looks at all patterns in autoexp.dat and tries to find match. My guess is that it chooses the first rule that matches, but there should not be more than one matching rule anyway.

Type names are processed as text. You can see the exact string that the visualizer sees in the watch window (the "Type" column). The bad thing about this is that extremely long type names are cut off at a certain string length. Such cropped strings then fail to match the visualizer patterns.

Patterns can contain wildcards. (My guess is that the patterns are internally transformed to regular expressions by the IDE.) This is great for templates. A wildcard can match one or more template arguments:

std::list<*>

and (preferred syntax)

std::list<*,*>

will both match std::list<int> - because it is in fact std::list<int,std::allocator<int> >.

As with marked subexpressions of regular expressions, we can access the actual text that matched the wildcards in the rule body's expressions. This text is referred to as $T1, $T2, $T3 etc. As with shell variable expansion, the $Ts are expanded in-place in the expression in which they appear. In case of std::list<*>, we have only one wildcard, and $T1 will be expanded to int,std::allocator<int>. In case of std::list<*,*>, $T1 expands to int and $T2 expands to std::allocator<int>. This is more usable, because we can access the item type (int) in the expressions.

preview section

The preview expression produces a string that is used for one-line representation of the data in the tooltips and in the watch window. Usually, one wants to pick 2 or 3 important members out of the hundreds members of the class.

The following code formats std::list preview:

std::list<*,*>{
  preview
    (
      #("[list size=", $e._Mysize, "]")
    )
}

Note the use of the contatenation operator #(s1, s2, ..., sn ). $e refers to the value of the variable being visualized (we are accessing the _Mysize member of the std::list implementation).

children section

When the children section is present, a [+] will appear to the left of the visualized value and can be used to expand it (display the children). Multiple children can be added using the #(c1, c2, ..., cn) operator, and child values can have custom names (and will appear sorted by name):

std::list<*,*>{
  children
  (
    #(
      first item: $e._Myhead->_Next->_Myval,
      second item: $e._Myhead->_Next->_Next->_Myval,
      [third item]: $e._Myhead->_Next->_Next->_Next->_Myval
    )
  )
}

This is nice, but we need more to display real structures like lists, arrays and trees. There are special processors for these structures:

#list

#list(head: head_expr size: size_expr next: next_expr) : deref_expr is used to visualize a linked list of values. This can be used to visualize std::list:

std::list<*,*>{
  children
  (
    #list(size: $c._Mysize,
          head: $c._Myhead->_Next,
          next: _Next
    ) : $e._Myval
  )
}

As you can see, we need to know the size of the list, where is the head node of the list, and how to get to a node's successor (this is done by accessing the next member of the current node). $c refers to the current expression, and in deref_expr, $e refers to the node being displayed.

Alternative approach is #list(head: head_expr skip: skip_expr next: next_expr) : deref_expr. Here skip_expr defines node value (probably compared by memory address) which terminates the list traversal. Also, if a node is reached twice (cycle detection), it is not displayed and the traversal is stopped.

#array

#array(expr: array_access_expr, size : size_expr) : deref_expr is similar to #list, but the values are accessed by indexing the array_head_expr by indices 0, 1, ..., (size_expr-1). In array_access_expr, $i denotes the current index.

There are optional parameters rank and base. base offsets the displayed indices of the array items (there must not be any deref_expr for this to work). rank can be used to display multidimensional arrays. In this case, size and base are evaluated repeatedly and $r in rank_expr and base_expr refers to the current rank being processed.

#tree

#tree(head: head_expr size: size_expr left: left_expr right:right_expr skip: skip_expr) : deref_expr is used to walk a binary tree. It is very similar to #list, the only difference being that the traversal is not linear but recursive depth-first. skip_expr is used to tell the shape of the tree (usually skip: 0).

conditionals

There is a #if (expr) ( then_expr ) #else ( else_expr ) construct (with the _else_ part optional) and a #switch(switch_expr) #case case0_value ( case0_expr ) ... #default ( default_expr ) #except ( except_expr ) ) construct that can be used in the expressions. My guess (haven't tried) is that values matching except_expr are not passed to #default.

TODO

FAQ, examples, common pitfalls, ...


gdb, ddd, others

If you are interested, please send an email to the boost-docs list telling us that you want to help.

Help wanted


Active developers



Filip Konvička
filip dot konvicka at logis dot cz


Note: See TracWiki for help on using the wiki.