共用方式為


Debugging STL Containers with WinDbg Part 4: Pair

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

Debugging STL Containers with WinDbg: Prolog
Debugging STL Containers with WinDbg: Prolog 2
Debugging STL Containers with WinDbg Part 1: Vector
Debugging STL Containers with WinDbg Part 2: List
Debugging STL Containers with WinDbg Part 3: Map

I should’ve blogged this post before part 3 as maps use pair to store each tree node’s value, but then I originally intended to fire and forget, so bare with me for now.

A pair is a template class that wraps a pair of values. Simply, pair::first stores the first value, and pair::second stores the second value.

Take the following example:

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

using namespace std;

int main() {
    pair<string, string> mypair("pair1", "pair2");
    cout << "mypair: " << mypair.first << " " << mypair.second << endl;
}
C:\Projects\STL>cl /EHsc /nologo /W4 /MTd /Zi pair.cpp
pair.cpp

C:\Projects\STL>pair
mypair: pair1 pair2

First you need to look at the type of the values. Do a dt on the pair variable and the debugger should tell you:

 0:000> dt mypair
Local var @ 0x32fae0 Type std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first            : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x01c second           : std::basic_string<char,std::char_traits<char>,std::allocator<char> >

In this case since both of my pair values are strings, if you just do a dt -r on the pair variable, you can already see the values:

 0:000> dt -r mypair
Local var @ 0xe7fe6c Type std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first            : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
      +0x000 _Myproxy         : 0x00ff2e68 std::_Container_proxy
         +0x000 _Mycont          : 0x00e7fe6c std::_Container_base12
         +0x004 _Myfirstiter     : (null) 
      +0x004 _Bx              : std::_String_val<std::_Simple_types<char> >::_Bxty
         +0x000 _Buf             : [16]  "pair1"
         +0x000 _Ptr             : 0x72696170  "--- memory read error at address 0x72696170 ---"
         +0x000 _Alias           : [16]  "pair1"
      +0x014 _Mysize          : 5
      +0x018 _Myres           : 0xf
      =00ce1d0c npos             : 0xffffffff
   +0x01c second           : std::basic_string<char,std::char_traits<char>,std::allocator<char> >
      +0x000 _Myproxy         : 0x00ff2a68 std::_Container_proxy
         +0x000 _Mycont          : 0x00e7fe88 std::_Container_base12
         +0x004 _Myfirstiter     : (null) 
      +0x004 _Bx              : std::_String_val<std::_Simple_types<char> >::_Bxty
         +0x000 _Buf             : [16]  "pair2"
         +0x000 _Ptr             : 0x72696170  "--- memory read error at address 0x72696170 ---"
         +0x000 _Alias           : [16]  "pair2"
      +0x014 _Mysize          : 5
      +0x018 _Myres           : 0xf
      =00ce1d0c npos             : 0xffffffff

You can expand each value individually as well:

 0:000> dt -r mypair first..
Local var @ 0xe7fe6c Type std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x000 first   : 
      +0x000 _Myproxy : 
      +0x004 _Bx     : 
         +0x000 _Buf    : [16]  "pair1"
         +0x000 _Ptr    : 0x72696170  "--- memory read error at address 0x72696170 ---"
         +0x000 _Alias  : [16]  "pair1"
      +0x014 _Mysize : 5
      +0x018 _Myres  : 0xf
      =00ce1d0c npos    : 0xffffffff

0:000> dt -r mypair second..
Local var @ 0xe7fe6c Type std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::basic_string<char,std::char_traits<char>,std::allocator<char> > >
   +0x01c second   : 
      +0x000 _Myproxy : 
      +0x004 _Bx      : 
         +0x000 _Buf     : [16]  "pair2"
         +0x000 _Ptr     : 0x72696170  "--- memory read error at address 0x72696170 ---"
         +0x000 _Alias   : [16]  "pair2"
      +0x014 _Mysize  : 5
      +0x018 _Myres   : 0xf
      =00ce1d0c npos     : 0xffffffff

Or you can use the C++ expression evaluator to access _Buf directly:

 0:000> ?? mypair.first._Bx
union std::_String_val<std::_Simple_types<char> >::_Bxty
   +0x000 _Buf             : [16]  "pair1"
   +0x000 _Ptr             : 0x72696170  "--- memory read error at address 0x72696170 ---"
   +0x000 _Alias           : [16]  "pair1"

0:000> ?? mypair.second._Bx
union std::_String_val<std::_Simple_types<char> >::_Bxty
   +0x000 _Buf             : [16]  "pair2"
   +0x000 _Ptr             : 0x72696170  "--- memory read error at address 0x72696170 ---"
   +0x000 _Alias           : [16]  "pair2"

Let’s look at a pair of containers with the following example:

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

using namespace std;

int main() {
    vector<string> myvector;
    myvector.push_back("1");
    myvector.push_back("2");
    myvector.push_back("3");

    list<string> mylist;
    mylist.insert(mylist.end(), "one");
    mylist.insert(mylist.end(), "two");
    mylist.insert(mylist.end(), "three");

    pair<vector<string>, list<string>> mypair(myvector, mylist);
    cout << mypair.first[0] << " " << mypair.second.front() << endl;
}
C:\Projects\STL>cl /EHsc /nologo /W4 /MTd /Zi pair.cpp
pair.cpp

C:\Projects\STL>pair
1 one

Again, you need to look at the type of the values. Do a dt on the pair variable and the debugger should tell you:

 0:000> dt mypair
Local var @ 0xf3f938 Type std::pair<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> > > >,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 first            : 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> > > >
   +0x010 second           : 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> > > >

In this case my first value is of type std::vector<std::basic_string>, and my second value is of type std::list<std::basic_string>.

From here, I can move forward in two ways:

1. Use the C++ expression evaluator for both first and second:

 0:000> ?? mypair.first
class 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         : 0x01586760 std::_Container_proxy
   +0x004 _Myfirst         : 0x01586aa8 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x008 _Mylast          : 0x01586afc std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x00c _Myend           : 0x01586afc std::basic_string<char,std::char_traits<char>,std::allocator<char> >

0:000> ?? mypair.second
class 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         : 0x01586c70 std::_Container_proxy
   +0x004 _Myhead          : 0x01586c10 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
   +0x008 _Mysize          : 3

2. Use the offsets to get the addresses and use dt to display the type instances:

From the output of dt mypair above,  the pair is at 0xf3f938. The first value is at offset 0x000, and the second value is at offset 0x010. With this info, I can ask the debugger to display the specific types at the specific addresses

 0:000> dt mypair
Local var @ 0xfbf730 Type std::pair<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> > > >,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 first            : 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> > > >
   +0x010 second           : 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> > > >

0:000> dt 0xfbf730 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> > > >
pair!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         : 0x01586760 std::_Container_proxy
   +0x004 _Myfirst         : 0x01586aa8 std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x008 _Mylast          : 0x01586afc std::basic_string<char,std::char_traits<char>,std::allocator<char> >
   +0x00c _Myend           : 0x01586afc std::basic_string<char,std::char_traits<char>,std::allocator<char> >

0:000> dt 0xfbf730+0x10 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> > > >
pair!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         : 0x01586c70 std::_Container_proxy
   +0x004 _Myhead          : 0x01586c10 std::_List_node<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,void *>
   +0x008 _Mysize          : 3

From here I can conveniently reference part 1 and part 2 of this series to traverse the containers.