For previous posts of this series, check out the following links:

Debugging STL Containers with WinDbg: Prolog
Debugging STL Containers with WinDbg Part 1: Vector

Let’s now look at lists. A list container manages a varying-length sequence of elements as a bidirectional linked list of nodes, each storing one element. The source in Visual Studio 2012 can be found in C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\crt\src\list.

Take the following example:

C:\Projects\STL>type main.cpp
#include <iostream>
#include <string>
#include <list>

using namespace std;

int main() {
list<string> mylist;
mylist.insert(mylist.end(), "one");
mylist.insert(mylist.end(), "two");
mylist.insert(mylist.end(), "three");
for (list<string>::iterator li = mylist.begin(); li != mylist.end(); ++li)
cout << (*li) << endl;
}
C:\Projects\STL>cl /EHsc /nologo /W4 /MTd /Zi main.cpp
main.cpp

C:\Projects\STL>main
one
two
three

The first step is to get the pointer to the head node of the linked list. _Myhead gives you this information. _Mysize gives you the number of elements in the list.

0:000> dt mylist
Local var @ 0xebf7c8 Type std::list<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >
+0x000 _Myproxy : 0x010e6440 std::_Container_proxy
+0x004 _Myhead : 0x010e5428 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x008 _Mysize : 3
0:000> dt 0x010e5428 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
main!std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x000 _Next : 0x010e64d0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x004 _Prev : 0x010e6868 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x008 _Myval : std::basic_string<char,std::char_traits<char>,std::allocator<char> >

Note that _Myhead points to the head node. _Myhead->_Next points to the first node, and _Myhead->_Prev points to the last node. The last node’s _Next simply points back to _Myhead.

To go through each node in the linked list, simply grab the first node and dt on _Next, and repeat for each node in the list, until _Next points to _Myhead:

0:000> dt 0x010e5428 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
main!std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x000 _Next : 0x010e64d0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x004 _Prev : 0x010e6868 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x008 _Myval : std::basic_string<char,std::char_traits<char>,std::allocator<char> >

0:000> dt 0x010e64d0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
main!std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x000 _Next : 0x010e67c0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x004 _Prev : 0x010e5428 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x008 _Myval : std::basic_string<char,std::char_traits<char>,std::allocator<char> >

0:000> dt 0x010e67c0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
main!std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x000 _Next : 0x010e6868 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x004 _Prev : 0x010e64d0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x008 _Myval : std::basic_string<char,std::char_traits<char>,std::allocator<char> >

0:000> dt 0x010e6868 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
main!std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x000 _Next : 0x010e5428 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x004 _Prev : 0x010e67c0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x008 _Myval : std::basic_string<char,std::char_traits<char>,std::allocator<char> >

To look at each node’s value (_Myval), simply tell dt to display the _Myval field. Note the two dots (..) which means "expand the field two levels deep". In this case, we want to see inside _Myval (first level), then we want to see inside _Bx (second level).

0:000> dt 0x010e64d0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *> _Myval..
main!std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
+0x008 _Myval :
+0x000 _Myproxy :
+0x004 _Bx :
+0x000 _Buf : [16] "one"
+0x000 _Ptr : 0x00656e6f "--- memory read error at address 0x00656e6f ---"
+0x000 _Alias : [16] "one"
+0x014 _Mysize : 3
+0x018 _Myres : 0xf
=00994878 npos : 0xffffffff

The dt command does support -l option which can help you traverse the linked list using _Next, like so:

0:000> dt 0x010e64d0 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *> -l _Next _Myval..
main!std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
_Next at 0x010e64d0
---------------------------------------------
+0x008 _Myval :
+0x000 _Myproxy :
+0x004 _Bx :
+0x000 _Buf : [16] "one"
+0x000 _Ptr : 0x00656e6f "--- memory read error at address 0x00656e6f ---"
+0x000 _Alias : [16] "one"
+0x014 _Mysize : 3
+0x018 _Myres : 0xf
=00994878 npos : 0xffffffff

_Next at 0x010e67c0
---------------------------------------------
+0x008 _Myval :
+0x000 _Myproxy :
+0x004 _Bx :
+0x000 _Buf : [16] "two"
+0x000 _Ptr : 0x006f7774 "--- memory read error at address 0x006f7774 ---"
+0x000 _Alias : [16] "two"
+0x014 _Mysize : 3
+0x018 _Myres : 0xf
=00994878 npos : 0xffffffff

_Next at 0x010e6868
---------------------------------------------
+0x008 _Myval :
+0x000 _Myproxy :
+0x004 _Bx :
+0x000 _Buf : [16] "three"
+0x000 _Ptr : 0x65726874 "--- memory read error at address 0x65726874 ---"
+0x000 _Alias : [16] "three"
+0x014 _Mysize : 5
+0x018 _Myres : 0xf
=00994878 npos : 0xffffffff

_Next at 0x010e5428
---------------------------------------------
+0x008 _Myval :
+0x000 _Myproxy :
+0x004 _Bx :
+0x000 _Buf : [16] "???"
+0x000 _Ptr : 0xcdcdcdcd "--- memory read error at address 0xcdcdcdcd ---"
+0x000 _Alias : [16] "???"
+0x014 _Mysize : 0xcdcdcdcd
+0x018 _Myres : 0xcdcdcdcd
=00994878 npos : 0xffffffff

Simple as mud. Remember to check out !list extension as well, which allows you to execute debugger commands repeatedly, once for every element in the linked list. For those lists with complex values, it might comes handy.

This is as much as I want to cover for now. Have a great day~!


P.S. Now that I’m at Part 2 in the series, I realized I used "Prolog" in the title of the beginning post instead of "Introduction” or "Preface" or something. Does it mean I need to have an Epilog? For blog posts I guess having an Epilog means there’s a hard end to the series. Or should I go back and change the title from Prolog to something else? I guess except my freshman year’s English teacher, nobody really cares :) Moving on..

P.P.S. That particular English teacher was the very first person I know who pronounced, in disgust, that ALL computer science student’s grammar sucks. There might be some truth to it, and I still try my best to write properly up till now, but I did picked assembly (Computer Science) over English (literature). Woot~!