Share via


Debugging STL Containers with WinDbg: Prolog

For those who use Standard Template Library in applications and depend on WinDbg to troubleshoot, debugging STL containers has always been a little back-bending. There are various STL extensions available, but they sometimes just don’t play nice (fail, or generate an exception).

Take the following example:

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

using namespace std;

int main() {
    vector<string> ss;
    ss.push_back("1");
    ss.push_back("2");
    ss.push_back("3");
    for (int i = 0; i < (int)ss.size(); i++)
        cout << ss[i] << endl;
}
C:\Projects\STL>cl /EHsc /nologo /W4 /MTd /Zi main.cpp
main.cpp

C:\Projects\STL>main
1
2
3

Let’s say you’re using WinDbg 6.12.0002.633 from Microsoft Windows SDK for Windows 7 and .NET Framework 4. If you set a breakpoint at the cout line (when the vector has data) and examine the vector with !stl, you’ll get output similar to below if you use Visual C++ 2010 or 2012 to compile. Try a newer debugger package? WinDbg 6.2.9200.16384 from Windows Software Development Kit (SDK) for Windows 8 produces the same result.

 0:000> !stl ss
TryToDumpAsVector(): Failed to g_ExtSymbols->GetFieldOffset( ModuleCollection, TypeIdCollection, FirstName, &ElementOffset ) 0x80070057 [line 983]
    Unrecognized type 'std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >' as an STL structure -- Defaulting to `dt -n (std::vector<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >) 0x22fa78`
main!std::vector<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         : 0x00361460 std::_Container_proxy
   +0x004 _Myfirst         : 0x003614f0 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x008 _Mylast          : 0x00361550 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x00c _Myend           : 0x00361550 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x010 _Alval           : std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >

However, try the debuggers on Visual C++ 2008 compiled binary and you’ll get output similar to below, which is a whole lot nicer:

 0:000> !stl ss
    Element 0
[da 0x823798]
00823798  "1"
    Element 1
[da 0x8237b8]
008237b8  "2"
    Element 2
[da 0x8237d8]
008237d8  "3"

As you can see, the !stl (and !std_map, etc.) extension is tightly coupled with the STL version that the extension was designed to support (offsets anyone?). By looking at the type information from various Visual C++ versions, you can see that the underlying STL implementation changes ever so slightly over time:

Visual C++ 2008:

 0:000> dt ss
Local var @ 0x18ff14 Type std::vector<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 _Myfirstiter     : (null) 
   +0x004 _Alval           : std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x008 _Myfirst         : 0x00823790 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x00c _Mylast          : 0x008237f0 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x010 _Myend           : 0x008237f0 std::basic_string<char,std::char_traits<char>,std::allocator<char> >

Visual C++ 2010:

 0:000> dt ss
Local var @ 0x22fa78 Type std::vector<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         : 0x00361460 std::_Container_proxy
   +0x004 _Myfirst         : 0x003614f0 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x008 _Mylast          : 0x00361550 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x00c _Myend           : 0x00361550 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x010 _Alval           : std::allocator<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >

Visual C++ 2012:

 0:000> dt ss
Local var @ 0xd4f938 Type std::vector<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         : 0x00e56418 std::_Container_proxy
   +0x004 _Myfirst         : 0x00e56858 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x008 _Mylast          : 0x00e568ac std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x00c _Myend           : 0x00e568ac std::basic_string<char,std::char_traits<char>,std::allocator<char> >

There’s little wonder why the extension sometimes work and sometimes doesn’t. But instead of hunting down the right extension to dump the STL containers, how about understanding the underlying structures so that you can look at the data in WinDbg without an extension? These extensions, after all, look at the same piece of memory. For memory corruption cases, these extensions would probably barf anyway.

In the coming multi-part posts, I’ll show you the steps to examine these containers yourself: vector, list, and map. Before that, I hope this post provides you with a little context on why anyone would want to do it by hand instead of using !stl.

For those who’s interested, stay tuned~!!


P.S. If you’ve read this far you’ve probably pulled your hair out more than once trying to debug STL containers with WinDbg. I’d reckon you’ve tried other extensions like SDbgExt but those too may have suffered similar fate. Little known to a few people I encountered, Visual Studio has had the capability to open dump files since way before my hair started falling off.

Granted the things you could do after opening a dump file with older versions are, well, not that much, newer versions do provide incremental yet significant improvements. In terms of looking at values in STL containers, if that’s all you need, maybe you can just open the dump file with Visual Studio and get comfy there:

VS2008STL

P.P.S. Over the years I occasionally ran into people who had to go through the same ordeal. Unfortunately I never seemed to pick myself up to write about it properly after I’ve helped them out. Not sure what happened but I guess today is always a good day to start doing the right thing. At least now I bitbucket it here so that I don’t need to re-remember these things when someone asks “how do I dump an STL map in WinDbg" next time.