Writing custom visualizers for visual studio 2005.
Writing custom visualizers for Visual Studio 2005
The native debugger in Visual Studio has long had an underadvertised feature called autoexp.dat, which is a file in the PackagesDebugger folder that allows you to control several aspects of the debugger. Among the features that you can control in autoexp.dat include: the string that is displayed for types in the variable panes, which functions the debugger will skip when stepping, and string names for COM HRESULTs. The first is the most interesting and useful, but unfortunately it doesn't support complex expressions. If you try to access more than one field in an expression:
MyType=size=<m_last - m_first, i>
...the debugger simply displays ??? instead. You can get around these limitations by writing an expression evaluator plugin, which can read any process memory, but in the end you're still limited to outputing only a single short string.
In Visual Studio 2005, a powerful feature has been added to autoexp.dat in the form of the [Visualizer] section. This section too contains mappings from types to display form, but these have a whole language for evaluating the object -- and unlike the regular [AutoExpand] templates, you can also affect the contents when the object is expanded. This means you can actually view the contents of an STL set, not just see raw red/black tree nodes.
Now, the [Visualizer] section is undocumented, and there's a comment at the top of the section that says DO NOT MODIFY. For those of you who are new to Windows programming, that means "edit at will." The problem is then deciphering the visualizer language.
P.S. Back up autoexp.dat before proceeding.
Basic structure
A visualizer's basic structure is as follows:
typename[|typename...] {
preview (
preview-string-expression
)
stringview (
text-visualizer-expression
)
children (
expanded-contents-expression
)
}
All three of these expressions are optional.
Like the AutoExpand templates, it is possible to match templates by using type<*> syntax, and also possible to do partial template matches, i.e. Foo<Bar, *>. The template parameters will show up as $T1, $T2, etc. You can also supply multiple types using Foo|Bar syntax.
autoexp.dat is reloaded every time the debugger starts. It is not necessary to restart Visual Studio to see changes; restarting the debugger, or detaching and reattaching to the target, is sufficient to effect changes.
The preview expression
The preview expression consists of a single expression to display in the one-line preview of a variable in Watch, QuickWatch, or the Command Window. This can either be a literal string or an expression, which is surrounded in brackets if it contains a formatting specifier. Unlike AutoExpand, however, the expression must dereference the current object using the $c variable. Since a single expression isn't usually useful, you will want to use #( and ) to bracket a comma-delimited list of strings and expressions.
Example:
// code
template<class T> struct MyType {
T *start, *end;
};
// autoexp.dat
MyType<*> {
preview (
#(
"ptr=", (void *)$c.start, ", size=", [$c.end-$c.start, i]
)
)
}
The parser isn't very robust and you'll often get weird results if you make mistakes. So don't do that. Another downer with regard to expressions here is that the sizeof() keyword doesn't work, which is unfortunate for template visualizers. Finally, colons, parens, and brackets within string and character literals can confuse the parser and give you inexplicable mismatch failures.
Displaying children
Now, for the real meat of the visualizer: showing children.
The children block also expects either a single item or a #() delimited list of items; when present, the object will have a [+] next to it and the expanded view will show the contents produced by this block. Each expression will show up as ascending indices, i.e. [0], [1], [2], etc. You can also name specific elements using name : expr syntax; the rules for the name are a little looser than C identifiers, with brackets in particular being accepted. The order of named fields is irrelevant; the contents are always sorted.
Example:
MyType<*> {
children (
#(
ptr: (void *)$c.start,
[size]: [$c.end-$c.start, i]
)
)
}
The ability of a visualizer to construct hierarchies in the watch window is limited. For the most part, you can only add items immediately below the item being visualized; the only ways available to add expandable objects is either to reference an aggregate or other object with a visualizer, or to use an array expression, i.e. [$c.start, 4x].
Note that the contents of the children block will replace the struct fields that would normally be displayed. It's a good idea to leave yourself an escape hatch by including a field with the value [$c,!], whose expanded view re-evaluates the current object without any visualizers or AutoExpand expressions.
Arrays
Being able to plop new fields in is neat, but the above doesn't allow you to view the contents of the data structure, which is hidden between those two pointers. Enter the #array statement:
MyType<*> {
children (
#(
[raw members]: [$c,!],
[size]: [$c.end-$c.start, i],
#array (
expr: $c.start[$i],
size: $c.end - $c.start
)
)
)
}
Here's what it looks like in the debugger:
The #array statement looks like it evaluates an array. Actually, it doesn't -- all it does is count the $i variable up from 0 to size-1, which you can then use in the expr field. This is actually better, because you can use it to evaluate chunked or triangular arrays. You don't even have to evaluate fields at all; you could generate a table with it if you were sufficiently bored. Any fields generated from #array are unnamed and will appear as ascending indices.
It is also possible to customize the value display for the array, by supplying an expression after the #array statement. The $e variable is set to the value produced by expr for that element. For instance, to display the address of each element in the data structure:
MyType<*> {
children (
#(
[size]: [$c.end-$c.start, i],
#array (
expr: $c.start[$i],
size: $c.end - $c.start
) : &$e
)
)
}
The bummer about doing this is that any other fields that you include will bump the indices produced by the array. I haven't found a way around this, yet. If you wrap the value expression in a nested #() it is possible to override the name for each element, although the name will be same for all elements. Also, if you supply multiple items per element, all of them will show up. If they're unnamed they'll get successive indices, so the first element will produce [0] and [1], the second [2] and [3], etc. One use for this is to display a packed array of 4-bit fields.
There are two additional options for #array: the base and rank fields. The base field allows you to change the starting array index. It doesn't change $i, but it does offset the array indices shown. (This only works if you don't override the display for $e, in which case single incrementing indices are always used.) The rank field allows you to do regular multi-dimensional arrays; when it is present and evaluates to greater than one, the size and base expressions are evaluated multiple times with $r indicating the zero-based dimension, and expr is then evaluated once for (product of all size) times with $i counting up contiguously. Thus:
// code
struct MyArray2D {
T *start;
int innerSize;
int outerSize;
};// autoexp.dat
MyArray2D {
children (
#array (
expr: $c.start[$i],
rank: 2,
size: ($r==1)*$c.outerSize+($r==0)*$c.innerSize,
base: 1
)
)
}
Sadly, the order of indices is reversed in the names produced, i.e [1,1], [2,1], [3,1], etc. If you don't mind the wonked ordering, you could fix this by doing some division and modulus on the index.
Conditionals
The #if() statement allows you to conditionally include expressions in your visualizer:
MyType {
preview (
#if (($c.end - $c.start) > 0) (
#("size=", $c.end - $c.start)
) #else (
"empty"
)
)
}
There are also #else and #elif() statements to do more complex if statements; these are analogous to the C equivalents.
The #switch() statement lets you... well, switch. #case and #default are present, except that there are no fall-throughs and no break. You can do some nice hacks using a #switch:
MyType {
children (
#switch($c.start[0].flags & 0xf000)
#case 0x0000 ( #( freed: $c.start[0].value ) )
#case 0x2000 ( #( alloc: $c.start[0].value ) )
#case 0x3000 ( #( active: $c.start[0].value ) )
#default ( #( huh: $c.start[0].value ) )
)
}
Two things to note here. One, the #switch statement doesn't have a scope around its #case statements. Two, it doesn't work within an #array. If you try it, devenv will hang at 100% CPU. For those cases, use #if instead.
Lists
Arrays are fine, but sometimes you want to use a linked list. Well, the visualizer supports that:
// cpp file
struct MyNode {
MyNode *prev, *next;
int value;
};
struct MyList {
MyList *head;
};
// autoexp.dat
MyList {
children (
#list (
head: $c.head,
next: next
)
)
}
The visualizer will then proceed to follow the singly-linked list and place each element in the output. Note that the next portion denotes a field name in the child node and not an expression. Like #array, you can use #list() : <expr> syntax to change the way each $e is displayed.
#list is protected against infinite traversals and will cope gracefully with a circular list. Also, you can use a skip: expression to denote a sentinel node that should not be reported. Although the name implies that the node will be skipped, it actually causes traversal to stop, so if your sentinel node is first you should start traversal after it.
You can also supply a size: expression to limit the number of elements displayed.
Trees
Ah, now for the really evil stuff: trees.
struct TreeNode {
TreeNode *left, *right;
int value;
};
struct TreeTest {
TreeNode *head;
int size;
};
TreeTest {
children
(
#(
#tree (
head : $c.head,
left : left,
right : right,
size : $c.size
) : &$e
)
)
}
In this case, Visual Studio trawls the tree using in-order traversal: that is, left, then current, then right. As with #list, size: can be an expression limiting the node count, skip: can avoid sentinels, and a value expression is possible.
There is a nasty bug with the #list and #tree statements you should be aware of: they do not work if the nodes exist in static storage in a module. The traversal will work OK, but the $e variable will point to an invalid offset instead of the actual item, and you won't be able to see any of the elements. I got burned by this when I tried to write a visualizer to dump the list of critical sections in the process, which turns out to reside in a static block in ntdll.dll, and I don't have a workaround yet. This bug does not affect the #array primitive.
The string view expression
stringview is an odd one: it sets the string that is displayed in the Text, XML, or HTML visualizer. This is what shows up when you click the little magnifying glass on the right side of some entries, like strings. Sadly, this is not terribly useful as all of the text visualizers are modal, but otherwise, it has the same syntax as the regular preview block.
StringTest {
stringview ( "<HTML><FONT color=red>HTML output!!</FONT>" )
}
The usefulness of this is unfortunately damped by two limitations: you can't escape HTML symbols in strings you output, and you can't put literal strings in the value expressions evaluated by, say, #array or #list, as they evaluate to NULL. You can't generate an XML or HTML report from a data structure. It's really only useful if you already have HTML or XML somewhere in memory.
Real examples
One common container that you might write yourself, and want to view, is a hash table: a nice hash table, consisting of a prime number of buckets, and a linked list at each one. Good luck finding anything in a hash table that has 500 buckets, though, so it's perfect for a visualizer. Unfortunately, if you look at the visualizer for stdext::hash_set, you'll discover that the VC guys cheated -- the Dinkumware STL has a single linked list binding all nodes in the hash_set, and just trawled it with a #list. We have an array of singly linked lists to elements.
Can we crawl such a structure? You bet!
struct Node {
Node *next;
int value;
};
struct HashSet {
Node **buckets;
int bucketCount;
};
// autoexp.dat
HashSet {
children (
#(
#array (
expr : ($c.buckets)[$i],
size : $c.bucketCount
) : #(
#list (
head : $e,
next : next
) : $e.value
)
)
)
}
Another tricky example: say you have an array of pointers, but want to skip the entries that are NULL. Can we do it? Sure!
struct SparseArray {
void **start, **end;
};
// autoexp.dat
SparseArray {
children(
#array (
expr: $c.start[$i],
size: $c.end - $c.start
) : #if ($e != 0) (
$e
)
)
}
And here it is in the debugger:
Ehh... hmmm. Well, as it turns out, doing an asymmetric #if within an #array causes the debugger to blow up. It is possible to do this, but only with a big hack:
SparseArray {
children(
#(
#array (
expr: &$c.start[$i],
size: $c.end - $c.start
) : #(
#array (
expr: &$e,
size: $e != 0
) : $e
)
)
)
}
Hee-haw!
Conclusion
The new visualizers are much more powerful in their capabilities, and open up a lot of possibilities for interesting debug displays within the Visual Studio Debugger. It sorely needs to be documented, and it can be a bit unstable when you work with it, but in general it makes data structures a lot nicer to work with.
(Update 9/27/2007: See my followup note.)
Comments
Comments posted:
I guess it was so long underadvertised, because it was quite unusuable in the MSVC6 days. You added your nice little STL size and content stuff, but the debugger would just lock up or even crash, because for some reason it couldn't handle the macros in a lot of cases. In some cases I even wasted hours on those hangs or crashes, because I thought it was my application having something like an endles recursion/stack overflow or even something worse. And at the end it turned out, I just had to remove all the helpful stuff from the autoexp.dat to get it working again. Pretty helpful...
Firewave - 14 08 06 - 04:50
I've got a string class that uses non-null-terminated strings. I'd love to see a way to display a range of characters in the debugger -- the normal ",s" watch expression shows the buffer guards past the end of the string. Any idea how to do that?
Michael - 26 09 06 - 21:14
I'm afraid not. I would suggest the preview block, but there's no way to push out a character by itself. Traditionally STL basic_string implementations maintain a null at the end in order to speed up c_str(), so they don't have this problem.
Phaeron - 26 09 06 - 23:39
Thankyou for this article, i've been using autoexp.dat for years but it's never been able to evaluate the kind of expressions I need to access the data types used by Alias's Maya SDK. Now I can finally see the contents of those MStrings without nasty debug code!
Fraser Graham - 04 10 06 - 18:22
That was a really useful article! Do you know if it's possible to display a matrix, ie have a #array nested inside a #array and display the correct number of things. If so, how do you tell it to access element [i][j]? It's almost hinted at in your hack to skip the NULLs but not quite.
Dave - 14 11 06 - 12:51
The only way I know of to do a multidimensional matrix is to use #array with 'rank'; if you try nesting #array statements then the debugger will just flatten the result. The downside to using rank for this is that it displays the indices in reverse order from normal C conventions. According to one of the guys on the VC++ team, this is a bug. It also means your matrix has to be contiguous.
If you have a fixed-size matrix, then you should simply be able to use an #array with something like [$e,10] for each element.
Phaeron - 15 11 06 - 23:46
Great article!! I've been experimenting with autoexp.dat visualizers to display out own types, and have been successful at creating expressions which can display our custom types. However, almost every debug session, I get the crash mentioned in your article and I have to close VS through task manager. The crashes occur in seemingly random places; at the same code location, sometimes it will crash and sometimes it won't. I haven't been able to narrow down which of the 5 or 6 visualizer expressions is the culprit. Do you have any pointers for tracking down / debugging these crashes to determine which visualizer expression is causing the problem?
Ron - 17 01 07 - 14:59
Nope, afraid not. The best I could suggest is to attach another instance Visual Studio to the bombed one with the Microsoft symbol server hooked up and see if you get lucky with the call stack.
Phaeron - 17 01 07 - 23:16
Great article! The best on the subject on the net at this moment. Note that I've found 1 behavior (somewhat bug) and 1 bug with visualizers that have forced us to modify our usage of them. First, pointers are previewed/expanded as their pointed type (Type* like Type). We have then decide to start all previews with the object address (casted as void* to avoid recursive evaluation). Second, Type[x] is previewed/expanded exactly like Type. This is a particularly bad when "children" is overridden and the array elements cannot be accessed as before. We have then decide to only override "children" for types that we don't expect in a C array. Visualizers will be more useful once this is fixed (I haven't tried SP1 yet).
Nicolas Fleury - 12 04 07 - 15:41
I'm having a problem getting STLPORT containers to work. The core of the issue is that the containers point to and iterate with 'node_base' (which contain the link pointers), but the data is in the subclass 'node'. If I try something like:
#list
(
head : $c._M_node._M_data->_M_next,
skip : $c._M_node._M_data,
next : _M_next
) : (_STL::_List_node*) &$e
It gives me "error | 0" when inspecting the children. Anybody have success with using the template parameters when casting?
Ian - 17 04 07 - 22:56
"_STL::_List_node" is actually templatized on $T1 in the previous comment.
Ian - 17 04 07 - 22:58
The parser can be a bit finicky sometimes, and occasionally wrapping expressions in parens, brackets, or #() can coax it into behaving. In your case, though, the space between the cast and the &$e may be your problem. When using bare expressions in visualizers, it's usually best to avoid all unnecessary spaces.
Phaeron - 18 04 07 - 00:44
Does anyone have any tips on getting boost::shared_ptr to display nicely in the debugger?
If the px member of the ptr shows: 0x02539f08 {"G:somepathName"}
And I write:
boost::shared_ptr<MyPathType>{
preview($c.px)
}
I would expect to see the same display for my shared pointer preview that the px member shows, but all I see is the memory address: {0x02539f08}
Matt - 19 04 07 - 17:03
Awesome. AutoExp.dat is more empowered and you explained the enhancements quite nicely. In fact I was looking for details on AutoExp.dat and I got only this link that explains about visualizer section. Thanks for this as it simplified the 'good for nothing' visual represntation of a unicode string container in the debugger, I work with, everyday.
Naren Chauhan - 04 05 07 - 05:13
I've cloned std:vector to get MFC CArray working but I'd like to be able to view a particular element of a vector or CArray. Any ideas on this?
paul tait - 15 06 07 - 00:06
Try this for stlport lists,
stlp_std::list{
children
(
#list
(
head : $c._M_node._M_data._M_next,
skip : &($c._M_node._M_data),
next : _M_next
) : #( *($T1 *)(sizeof(stlp_std::priv::_List_node_base)+((char*)&($e))) )
)
preview
(
#(
"(",
#list
(
head : $c._M_node._M_data._M_next,
skip : &($c._M_node._M_data),
next : _M_next
) : *($T1 *)(sizeof(stlp_std::priv::_List_node_base)+((char*)&($e))),
")"
)
)
}
It's a hack, I know, but I can't seem to cast to templated types. Similar stuff works for sets, but because I can't cast to templated types I can't find a way to show both keys and values in maps (I've gone for just showing the value).
Dave B - 26 06 07 - 23:04
Darn, the angle brackets have been eaten. It's actually
stlp_std::list[*,*] - with the square brackets as angle brackets
And the #( ... ) in the children section shouldn't be there, and will crash VS2005 if it's left in!!
Dave B - 26 06 07 - 23:27
Did anybody managed to get stl port's map visualized ?
I think I will give it up, I tried all day long.
stlp_std::map
Mark S - 26 07 07 - 13:43
As this seems to be the only source of information that I can find on the net for autoexp.dat under vs 8 I thought I'd add my visualiser for stlport maps:
stlp_std::map {
children
(
#(
[map]: [$c,!],
#tree (
head : $c._M_t._M_header._M_data._M_parent,
left : _M_left,
right : _M_right,
skip : $c._M_t._M_header._M_data
) : *($T2*)(sizeof($T1)+sizeof(stlp_std::_Rb_tree_node_base)+((char*)(&$e)))
; *($T1*)(sizeof(stlp_std::_Rb_tree_node_base)+((char*)(&$e)))
; *($T2*)(sizeof($T1)+sizeof(stlp_std::_Rb_tree_node_base)+((char*)(&$e)))
)
)
}
I've commented the two lines at the end because you can either show the key or the value. I've not yet managed to show both on the same line (showing them as consecutive entries doesn't cut it for me). I've tried showing them as a pair but it's not having it so any other suggestions gratefully received.
Alex Whittaker - 27 07 07 - 07:36
Bah, why has no one written a decent text to HTML converter? Try again, a bit more readable this time:
stlp_std::map {
children
(
#(
[map]: [$c,!],
#tree (
head : $c._M_t._M_header._M_data._M_parent,
left : _M_left,
right : _M_right,
skip : $c._M_t._M_header._M_data
) : *($T2*)(sizeof($T1)+sizeof(stlp_std::_Rb_tree_node_base)+((char*)(&$e)))
; *($T1*)(sizeof(stlp_std::_Rb_tree_node_base)+((char*)(&$e)))
; *($T2*)(sizeof($T1)+sizeof(stlp_std::_Rb_tree_node_base)+((char*)(&$e)))
)
)
}
Alex Whittaker - 27 07 07 - 07:55
This is what I did to stlp set. It only works for the last type of set declared. If I declared set< int >, then set< string >, then only set< string > will display correctly. Any insight? Thanks.
children
(
#tree
(
head : $c._M_t._M_header._M_data._M_parent,
skip : $c._M_t._M_header._M_data,
left : _M_left,
right : _M_right,
size : $c._M_t._M_node_count
) : ((_Node*)(&$e))->_M_value_field
)
Liu - 24 08 07 - 19:38
Hey good article, I have a question though. Is there any way to add types to the view menu? You know, when you right click on the watch window and it brings up a sub menu that lets you change how to view variables of the numeric type. I want to add a new view to it, instead of viewing my values in hex and such, I want to view them in a fix point format. Can this be done?
Andre B - 30 08 07 - 15:04
Great stuff here. I will be able to use this a lot.
People interested in visualizers might also be interested in a graphical viewer for C/C++-arrays (even in unmanaged code!) which I wrote.
See http://arraydebugview.sourceforge.net
Joachim - 12 09 07 - 18:28
Here is a visualizer for STLPort strings
stlp_std::basic_string {
preview
(
#(
#if ($c._M_end_of_storage._M_data == $c._M_buffers._M_static_buf + $c._DEFAULT_SIZE)
(
$c._M_buffers._M_static_buf
) #else (
$c._M_buffers._M_dynamic_buf
)
)
)
}
Kevin - 20 09 07 - 15:06
Hi
Thanks to the very useful information I found here I have been able to write a rather complete autoexp.dat for the STLport library. You will find it in SVN trunk of the STLport sourceforge project here:
https://sourceforge.net/projects/stlport..
Bests
Dums - 31 10 07 - 17:23
I’ve got a string class that uses non-null-terminated strings. I’d love to see a way to display a range of characters in the debugger—the normal ”,s” watch expression shows the buffer guards past the end of the string. Any idea how to do that?
Articles - 11 11 07 - 04:42
Great article!! I’ve been experimenting with autoexp.dat visualizers to display out own types, and have been successful at creating expressions which can display our custom types. However, almost every debug session, I get the crash mentioned in your article and I have to close VS through task manager. The crashes occur in seemingly random places; at the same code location, sometimes it will crash and sometimes it won’t. I haven’t been able to narrow down which of the 5 or 6 visualizer expressions is the culprit. Do you have any pointers for tracking down / debugging these crashes to determine which visualizer expression is causing the problem?
Articles - 12 11 07 - 09:34
Reverse engineering at its best! thanks for putting this online.
It might be useful to note that a checkbox needs to be cleared in VS(2005) :Options/Debugging/General/"Show raw structure of objects.." . checking this bypasses loading autoexp.dat altogether.
And a question - can you say what the 'b' format suffix does? (as in [$c.foo, sub])
Ofek - 23 01 08 - 10:53
The b suffix gives you the bare representation of the object. I haven't explored what it does in all cases, but for strings (s, su, and s8), it suppresses the quotes that ordinarily appear around the string.
Phaeron - 23 01 08 - 23:32
Thanks!
also - i have a method to visualize multiple members of an array element *without* bumping the indices up: write a custom preview to the array element, to view it's members (or part of them) in a single line.
You can take this further in some cases, to make fake 2d visualizers. example c++ source (D3D-like matrix):
struct MATRIX{
FLOAT _11, _12, _13, _14;
FLOAT _21, _22, _23, _24;
FLOAT _31, _32, _33, _34;
FLOAT _41, _42, _43, _44;
};
modify to-
struct MatrixLine
{ FLOAT f1, f2, f3, f4; };
struct MATRIX
{
union {
struct {
FLOAT _11, _12, _13, _14;
FLOAT _21, _22, _23, _24;
FLOAT _31, _32, _33, _34;
FLOAT _41, _42, _43, _44;
};
MatrixLine line[ 4 ];
};
and add to autoexp -
MatrixLine{
preview(#($c.f1,", ",$c.f2,", ",$c.f3,", ",$c.f4))
children([$c,!])
}
to get a nice 2d layout of the matrix.
To keep the watch edit-access, it's important to keep the children block (previewed data isn't editable).
Ofek - 25 01 08 - 09:25
Annoying gotcha to the matrix transformation: anonymous structs are a Visual C++ specific language extension. Standard C++ only supports anonymous unions. Kinda sucks, at least for math libraries.
Phaeron - 26 01 08 - 21:41
I'm having a problem getting custom visualizers to work. I placed a new section in autoexp.dat and followed the instructions here, but I don't see any effect in my watch windows. I know that my changes are being read in, because when I made syntax errors I was notified and fixed them. What could be going wrong? The typenames I'm using are identical to those displayed in the "Type" column of the watch window.
Luke - 11 06 08 - 21:07
I figured out the answer to my question. It turns out that the file format is whitespace-sensitive. You have to have the opening curly brace on the same line. D'oh!
Luke - 24 06 08 - 21:21
Hi all,
Thanks for the great article , I found it most useful as i work with CMaps a lot !
However, i was wondering if there was a way to display meaningful values of a
CTypedPtrArray ? I have something declared this way :
CTypedPtrArray *> mymap;
All i can get is a CPtrArray member and a numeric value (address of mymap i guess...)
Thanks
RAM - 25 09 08 - 08:04
Oooops, i didn't notice the small print comment at the end of the page...
Here is the array declaration with '{' and '}' instead of 'less than' and 'greater than':
CTypedPtrArray { CPtrArray ,CMap { CString, LPCSTR, CString, LPCSTR } *} mMapPageScopeID;
Thanks
RAM - 25 09 08 - 08:09
Out of curiosity, how did you figure all this out? We're using quite a few complex visualizers (which we were able to create thanks to your information), but they occasionally cause VS to crash (usually right when I reach the critical line that will reveal the source of the bug, heh). I'm unsure how to go about figuring out which part of the visualizer (or even which visualizer) is causing the problem....
Ron Prestenback - 17 10 08 - 14:51
I'm afraid I just sat down, studied the existing visualizers, and experimented with writing new ones.
What usually causes the crashes is infinite recursion -- just about everything else will either give you a parse error dialog or display (error) in the watch window. Do watch out for the change in SP1 that affects $c vs. $e, which I noted in the followup. That'll give you hard to find errors since the blow ups will only occur when visualizers are nested.
Phaeron - 18 10 08 - 00:43
Here is a visualizer for boost::unordered_map(which I've spent a whole afternoon to work it out):
boost::unordered_map{
preview
(
#(
"[", $e.base.data_.size_, "](",
#array
(
expr : $e.base.data_.buckets_[$i],
size : $e.base.max_load_,
) : #(
#array (
expr: $e,
size: $e.next_ != 0
) : #(
#list(
head : $e.next_,
next : next_
) : ((boost::unordered_detail::hash_table_data_unique_keys::node*)&$e)->value_
)
),
")"
)
)
children
(
#(
[raw members]: [$c,!],
#array
(
expr : $e.base.data_.buckets_[$i],
size : $e.base.max_load_,
) : #(
#array (
expr: $e,
size: $e.next_ != 0
) : #(
#list(
head : $e.next_,
next : next_
) : ((boost::unordered_detail::hash_table_data_unique_keys::node*)&$e)->value_
)
)
)
)
}
And you can find more visualizers for boost ptr_containers at
http://lists.boost.org/boost-users/2007/..
zhaowei - 25 10 08 - 13:24
Sorry, it should be:
boost::unordered_map{
...
}
zhaowei - 25 10 08 - 13:28
Hello,
This is a very interesting article. Do you know if there is a way to make use of global variables in visualizers? Some of my data structures make use of a global variable and I need to access it to fully display the content of an element.
More precisely, my data structure contains 16-bit indexes into a global object array. To fully display the data structure, I need to access the global array. I have tried to refer to the global variable as $V or V, but it doesn't work...
Alexandre - 04 11 08 - 03:42
Hi,
This is a very nice article, the best I can find about autoexp.dat.
Has anybody come up with a visualizer for boost::any?
It is a useful class but difficult to be debugged.
Thanks
Yuan
Yuan Li - 22 12 08 - 02:59
Really helpful article; thanks! I'm posting here in case anyone else is ever looking like I was for a way to show Rogue Wave strings (RWCString) properly in the debugger. The string itself can be exposed using the AutoExpand section, but since they store their length in a funky way it was impossibly to show that. The Visualizer section changes that though:
RWCString {
preview (
#("{",
[$c.data_,st],
" length=", strlen($c.data_
[(((RWCStringRef*)$c.data_)-1)->nchars_,u],
"}"
)
)
}
Owen - 07 01 09 - 18:06
Visual C++ 2008
Boost 1.36.0
boost::unordered_map visualizer described by zhaowei doesn't work for me... I've tested with boost::unordered_map.
dlaugt - 08 01 09 - 05:44
boost::unordered_map<int, int>
boost::unordered_map<int, int> entities;
entities[0] = 10;
entities[1] = 11;
entities[2] = 12;
entities[3] = 13;
dlaugt - 08 01 09 - 06:41
At the end of #list, $e has the correct address but it will not be able to cast into node*
((boost::unordered_detail::hash_table_data_unique_keys::node*)&$e)->value_
dlaugt - 08 01 09 - 08:25
Nice article. I'm running VC9SP1 and find that I need to restart the devenv to see my changes to autoexp.dat. Anybody else seeing this? This is with a native C++ project - if that matters.
Paul - 13 01 09 - 18:05
Yes, I did start seeing that once I'd upgraded to SP1. I think it only happens sometimes, but I might be wrong.
Phaeron - 14 01 09 - 01:00
In case you are interested in a boost::shared_ptr (smart pointer) visualizer, you can find one here:
http://code.msdn.microsoft.com/boostshar..
CppGuy - 29 01 09 - 06:32
I had my own problems with stlport sets and maps, since the autoexp that comes with stlport supports version 5.2 and I'm still using 5.1. They use one visualizer for sets and maps, but maps don't work as expected. Here is how to fix this without using casts to stlport structs.
set needs to be declared with two or three template parameters, if you have only one the visualizer won't work.
#tree
(
head : $c._M_t._M_header._M_data._M_parent,
skip : $c._M_t._M_header._M_data,
left : _M_left,
right : _M_right
) : *($T1*)(&($e) + 1)
this works in both preview and children expression
map needs to be declared with three or four template parameters, it' won't work with one or two
#tree
(
head : $c._M_t._M_header._M_data._M_parent,
skip : $c._M_t._M_header._M_data),
left : _M_left,
right : _M_right
) : *(stlp_std::pair*)(&($e) + 1)
this works in children expression but to make it work in preview you need to change stlp_pair visualizer to have only one template parameter and to dereference first and second with $e variable instead of $c. This is how MS implemented their visualizer for pair and they should know how this works ;)
This was tested with Visual Studio 2008 SP1
Nikola Smiljanic - 10 03 09 - 04:37
damn html
#tree
(
head : $c._M_t._M_header._M_data._M_parent,
skip : $c._M_t._M_header._M_data,
left : _M_left,
right : _M_right
) : *(stlp_std::pair[$T1 const ,$T2]*)(&($e) + 1)
use angle brackets instead of square ones, spaces are very important: one before and one after const, and no space after comma (it won't work otherwise)
Nikola Smiljanic - 11 03 09 - 10:06
I have a multidimensional (2D) template array where I want to preview the values of the array.
Does anybody know a solution for that problem?
zack - 03 05 09 - 09:20
Great article! Helped me a lot!
Maybe some will find his useful: Just had a little problem with a virtual array build of partial array chunks that are in a tree structure. But I wanted to see all entries with contiguous indices. Works like this:
children (#(
#tree(
head: $e._data,
left: before,
right: after
) : #array(
expr: (($T1*)($e._data))[$i + $e._skip],
size: $e._fill
) : $e,
#(
[_raw]: [$e,!],
[blocks]: #tree(
head: $e._data,
left: before,
right: after
)
)
))
The #() trick you metioned gets the named items after the list, so that the list starts at 0 and the ": $e" after the #array has the effect of disabling the automatic index reset for each array. Without it the numbers would start at 0 (or base) again for each branch of the tree.
PS: Maybe you could also allow the <pre> or <code> html tag
Zillatron - 13 05 09 - 09:21
@zack: Details? If you post the data part of the template ...
If your template(s) look anything like this:
template< class Elem, int size >
class TA {
public:
Elem data[ size ];
};
template< class Elem, int size1, int size2 >
class TA2 {
public:
TA< Elem, size1 > data2[ size2 ];
};
then this solution works:
TA<*,*>{
preview (#(
"(",
#array(
expr: $e.data[$i],
size: $T2
),
")"
))
children (
#array(
expr: $e.data[$i],
size: $T2
)
)
}
TA2<*,*,*>{
preview (#(
#array(
expr: $e.data2[$i],
size: $T3
)
))
children (
#array(
expr: $e.data2[$i],
size: $T3
)
)
}
Zillatron - 27 05 09 - 06:19
Hi,
is there any possibility to make the visualizer show the most derived object instead of the base class?
Example
class A {...};
class B : public A {...};
class C {A* _a;};
visualizer
C{
preview:( $e._a )
}
I would prefer to see
- (A*)($e._a) if $e._a is of type A but not of type B
- (B*)($e._a) if $e._a is of type B
For now I always get the base class view
steach - 29 05 09 - 06:54
@steach: That's one of the things, I'm looking for, too. :(
The only (partial) help is to define a preview for class B. The you'll see this previews after the address of the pointer. For this the debugger analyses the most derived class. But if you say
| *($e._a)
you always see the data from the base class, just as if you said
| *(A*)($e._a)
The same problem is worse in "children". For my smart pointers I'ld like to define something like
| data: *($e._data)
but then I only get the base class part of the object they point to. Pretty useless. So I have to use
| data: $e._data,
| direct: *($T1*)($e._data)
If the object is of the templated base class, the the "direct" entry works and I can access the object directly. But if it's of a derived class then I have to use "data" and open there the node for the derived class.
Zillatron - 03 06 09 - 04:41
Looking further at the boost libraries you can see an example where User Customer Functions are called out to.
Basically it allows you to call out from a children-block( ) to a custom user function. This should resolve most peoples problems with displaying partial-strings, etc. However, it does need the user to press refresh button each time they want an update - and the first time the debugger runs it seems to display "CXX0001: Error: attempting to execute suer function".
// Code to be hash-included everywhere you need to debug
inline unsigned long long hpp_vizu(int &pt)
{
unsigned long long val = pt + 5 ;
return val;
}
namespace {
template struct _print_ptime_helper {
_print_ptime_helper() {
int pt=0 ;
hpp_vizu(pt);
}
};
typedef _print_ptime_helper _print_ptime_helper_2;
_print_ptime_helper_2 _print_ptime_helper_2i;
}
; Your script...
; T1=*, T2=*
my_type {
children (
#( call func: [hpp_vizu($T1),u] ), ; passing template data in, can access globals too?
#( test: [hpp_vizu(*&($c.array[1])),u] ) ; passing struct data, could do a void * too?
)
I didnt invent this, its the brilliance of filip konvicka of the Boost project.
1: The visualizer script
https://svn.boost.org/svn/boost/sandbox/..
2: A header file that instantiates an inline function.
https://svn.boost.org/svn/boost/sandbox/..
Lucien Murray-Pitts - 10 06 09 - 06:55
@Lucien: Thanks, for the hint!
I tried it and can confirm, that it is possible to call user functions/methods.
With VS 2008 SP1 I always get the error first, but only sometimes with the "refresh" button. So I can't always coax the debugger to call the method again and show the return value. Haven't really figured out the system behind this behavior yet. My first guess is that the refresh button only shows after a step that didn't modify the concerned variable.
Zillatron - 10 06 09 - 08:56
Problem: Typecasting "head" in #list with templates.
It seems I (almost) always have to typecast the "head" in a #list. It usually works even with template types. But I can't seem to get it working when i have to use a "$T1" as a template parameter:
Array<*> {
... #list(
...... head: (ArrayElement<$T1>*)($e.head),
I tried inserting spaces and braces - nothing seems to work here. Showing it as an ordinary variable value is no problem. Any ideas?
Zillatron - 22 06 09 - 10:20
Re: casting ArrayElement<$T1>
We had the same problem - it seems $T1 can only be used standalone and not as a parameter of another template.
We solved this by casting to a dummy ArrayElement<int> and later casting from int to (*($T1) &($e.val))
Of course you have to make sure the dummy cast does not spoil your traversal of the array/tree/list
iggi - 06 07 09 - 03:10
@iggi: Thanks for the hint!
I had to reorder my structures a bit, but it works now! :)))
And I found, that $T1 even works as a template argument, when i use it for the element type cast or ordinary variable display. Just not for the "head:" inside the #list. This does the job:
Array<*>{
...
#list(
head : (ArrayElement<DummyType>*)$e.head,
...
): (ArrayElement<$T1>*)&$e
Zillatron - 06 07 09 - 23:50
Has anybody figured out how to represent doubly-linked lists with no Head. I have a struct that can either stand alone or be a part of another struct. It looks something like this:
typedef struct _Stream (
struct _Stream *p_next,
struct _Stream *p_prev,
/* the rest of the structure */
) _Stream;
Any ideas on how to make this one work?
rembo666 - 21 07 09 - 06:44
@rembo
Like our friendly host posted in his original article, something like this:
_Stream{
children(
raw: [$e,!],
#list(
head: $e.p_next,
next: p_next,
skip: $e
))
}
should do the trick and show all other elements of the linked list when you open one of them.
Zillatron - 21 07 09 - 23:01
hi, exist any way to create visualizer for typedef type (e.g. unsigned long long which represents timestamp in msec). During debugging, it will be perfect show timestamp in human-readeable format, e.g YY-MM-DD hh:mm:ss.sss.
andrei - 22 09 09 - 21:00
Hello there.
Would it be possible to create a plugin for visual studio to show watch variables in an even more flexible way than what autoexp.dat permits ?
Michael (link) - 02 10 09 - 20:43
@andrei:
Have a look at the EEAddin sample for VS. It already shows how to display timestamps.
You probably have to write your own addin and compile it as a dll. Then you can reference it in the autoexp.dat with the $ADDIN definition.
Zillatron - 08 10 09 - 23:05
Is there any way to correctly display smart pointers?
My problem is, is that I have a smart pointer object, which has member variables that are also smart pointers.
It looks something like this
typedef smartPtr myType1_ptr;
typedef smartPtr myType2_ptr;
class myType1
{
private:
myType2_ptr data;
};
class myType2
{
private:
string data;
}
And I have a secion in autoexp.dat that looks somthing like this
smartPtr{
children
(
#(
object : ($T1*)$c.object,
orig : [$c,!]
)
)
}
myType1{
children
(
#(
orig : [$c,!],
data : $c.data
)
)
}
The problem is, is that in the debugger the variable data shows "error" with a type of int.
Any ideas how I could solve this problem?
Joseph - 14 10 09 - 03:29
@ Joseph:
could you post the template of smartPtr?
... and make sure you substitute the angular brackets with their html-encodings (< and >) or they get lost.
what type does the debugger display for "data" when you open the "orig"?
Zillatron - 14 10 09 - 06:32
Ok, so I figured out what the problem actually is.
It is a problem with virtual inheritance, and I have almost come to a fix, but now I am having another problem.
So the actual object pointer in the smart_ptr is a base class pointer.
All of the actual data is on the derived pointer, but since it is virtually derived I cannot simply downcast it, and I dont think the visualizer support dynamic_cast. I can figure out the actual offset of the pointers, but when I do something like this:
derived : (Derived*)(void*)$e.object - 120
The visualizer multiplies the number by 128 so I end up with an offset of much much greater than I want.
Question1: is there anyway to do a dynamic_cast in the visualizer?
Question2: is there anyway to subtract a pointer address using bytes?
Thanks,
Joseph - 14 10 09 - 09:12
Q2: Try
derived : (Derived*)(((char*)$e.object) - 120)
Zillatron - 14 10 09 - 22:01
First of all, thanks for the great article and the helpful comment section! Unfortunately, I still wasn't able to solve my particular problem.
Maps or sets of "complex" objects (a simple struct will do) or not resolved properly until you provide a visualization rule for that type of object. Which is strange, because such an object on its own is resolved properly by displaying its members. It is also cumbersome, because you would have to provide visualization rules for all those types you put into containers. But what's really bad is that the rules can't handle locally defined types, such as inside a class or function.
Any ideas about this?
Thanks,
Hurzler - 19 10 09 - 02:19
@Hurzler
/But what's really bad is that the rules can't handle locally defined types, such as inside a class or function./
At least classes defined locally inside classes work for me. You just have to use the fully qualified name:
namespace junk {
class outer {
class inner {
};
};
};
In VS2005 and VS 2008 you can see the contents of STL containers. The rules for getting at the data are in autoexp.dat "c:\Program Files\Microsoft Visual Studio 9\Common7\Packages\Debugger\autoexp.dat".
AutoExp.dat is meant to be customized. However, the STL defs are under a section called [Visualizer]. If you can figure out the language used in that section, more power to you, however I'd recommend just leaving that part alone.
Autoexp.dat existed in VS2003, but there was no support for STL containers ([Visualizer] didn't exist). In VS2003 you have to manually navigate the underlying data representation.
By modifying autoexp.dat it is possible to add rules for navigating the data representation of your own types so they are easier to debug. If you do this, you should only add to the stuff under [AutoExp]. Be careful and keep a back up of this file before you modify it.