Windbg 脚本 - STL 映射,在命令输出中显示有限的输出

Windbg script - STL map, displaying limited output in command output

我已经使用 Windbg 几个月了,最后决定编写一个脚本。我的意图是绕过 STL 映射的节点并显示键/值对。我已经到了输出我发现的东西的地步,但我的简单问题是我是否可以解析该输出以使其更清晰易读,因为当我转储 _Myval.first._Bx_Myval.second._Bx 通过 ?? - 如果可能的话,我 喜欢 只是把我感兴趣的东西拿出来。

例如,当我这样做时没有看到这个:

.printf "\nTREE MAP NODE: %p\n", @$t0 .printf " KEY = " ?? @$t0->_Myval.first._Bx

TREE MAP NODE: 00000010bc1f2cb0 KEY = union std::_String_val<char,std::allocator<char> >::_Bxty +0x000 _Buf : [16] "Connections" +0x000 _Ptr : 0x00004c5353657355 "--- memory read error at address 0x00004c5353657355 ---" +0x000 _Alias : [16] "Connections"

我想看这个:

TREE MAP NODE: 00000010bc1f2cb0 KEY = "Connections"

所以我最终可以这样显示:

TREE MAP NODE: 00000010bc1f2cb0 KEY = "Connections" VALUE = "10" TREE MAP NODE: 00000010bc1f2cc0 KEY = "StartupTask" VALUE = "TestTask" TREE MAP NODE: 00000010bc1f2cd0 KEY = "Location" VALUE = "UK"

我知道我可以使用 ?? 查看节点的 _Myval.first,其中显示了其中包含的任何内容的大小,例如0xb 我什至可以用 _Myval.first._Mysize 返回 unsigned int64 0xb 之类的东西来解决这个问题,但是我可以对 contents?[=28 做同样的事情吗? =]

这可能是我缺乏经验,但如果我尝试显示 _Myval.first._Bx._Buf 中的内容,那么我只会得到一个地址并返回第一个字符,例如'C'... 所以,我的问题就是这样 - 据我所知 我想要的 thing 被举行了,我知道long 有多长(如果我需要知道这一点)-那么我怎么才能显示那个...而不是当前返回 [=25] 的整个 _Bx =]、_Ptr_Alias?

下面是在windbg中玩转STL模板显示的几个例子

尝试新的 dx 表达式求值器,它深入到最后一个成员并漂亮地打印它

如果您使用的是最新的 windbg 版本而不是 windbgs 本机脚本,也可以使用 javascript

假设一个正在调试的代码有一个私有 pdb

if done dv 在函数初始化地图之后 如果迭代器可用,可以直接使用它

0:000> ?? iter._Ptr->_Myval.second
char * 0x000341a0
 "Alpha"

如果想用粗体打印

.printf /D "contents of iter_.second is <b>%ma</b>\n" , @@c++(iter._Ptr->_Myval.second)
contents of iter_.second is Alpha

如果要枚举映射中的每一对,则必须枚举父左右分支

0:000> ?? mymap._Mypair._Myval2._Myval2._Myhead->_Parent->_Myval
struct std::pair<char const ,char const *>
   +0x000 first            : -120 '' (eax)
   +0x004 second           : 0x000341a8  "Beta"
0:000> ?? mymap._Mypair._Myval2._Myval2._Myhead->_Left->_Myval
struct std::pair<char const ,char const *>
   +0x000 first            : -120 '' (eax)
   +0x004 second           : 0x000341a0  "Alpha"
0:000> ?? mymap._Mypair._Myval2._Myval2._Myhead->_Right->_Myval
struct std::pair<char const ,char const *>
   +0x000 first            : -120 '' (eax)
   +0x004 second           : 0x000341c0  "Epsilon"

如果需要打印右分支(地图中的最后一个成员)

.printf "the last memeber in map is %ma\n" , @@c++(mymap._Mypair._Myval2._Myval2._Myhead->_Right->_Myval.second)
the last memeber in map is Epsilon

使用最新的 dx 表达式计算器来漂亮地打印几乎所有内容

0:000> dx mymap
mymap                 : { size=0x5 } [Type: std::map<char,char const *,std::less<char>,std::allocator<std::pair<char const ,char const *> > >]
    [<Raw View>]     [Type: std::map<char,char const *,std::less<char>,std::allocator<std::pair<char const ,char const *> > >]
    [comparator]     : less [Type: std::_Compressed_pair<std::less<char>,std::_Compressed_pair<std::allocator<std::_Tree_node<std::pair<char const ,char const *>,void *> >,std::_Tree_val<std::_Tree_simple_types<std::pair<char const ,char const *> > >,1>,1>]
    [allocator]      : allocator [Type: std::_Compressed_pair<std::allocator<std::_Tree_node<std::pair<char const ,char const *>,void *> >,std::_Tree_val<std::_Tree_simple_types<std::pair<char const ,char const *> > >,1>]
    [0x0]            : 65 'A', "Alpha" [Type: std::pair<char const ,char const *>]
    [0x1]            : 66 'B', "Beta" [Type: std::pair<char const ,char const *>]
    [0x2]            : 67 'C', "Gamma" [Type: std::pair<char const ,char const *>]
    [0x3]            : 68 'D', "Delta" [Type: std::pair<char const ,char const *>]
    [0x4]            : 69 'E', "Epsilon" [Type: std::pair<char const ,char const *>]

使用 dx 表达式计算器可以像这样将任何地址转换为映射

dx @$myvar = ((stdmap!std::map<char,char const *,std::less<char>,std::allocator<std::pair<char const ,char const *> > > * ) 0x17fb90)

施法后 可以这样使用它

0:000> .for (r $t0 = 0 ; @$t0 < 5 ; r $t0 = @$t0+1) { dx @$myvar[0][@$t0].second }
@$myvar[0][@$t0].second                  : 0x341a0 : "Alpha" [Type: char *]
@$myvar[0][@$t0].second                  : 0x341a8 : "Beta" [Type: char *]
@$myvar[0][@$t0].second                  : 0x341b0 : "Gamma" [Type: char *]
@$myvar[0][@$t0].second                  : 0x341b8 : "Delta" [Type: char *]
@$myvar[0][@$t0].second                  : 0x341c0 : "Epsilon" [Type: char *]

_Bx 等属于 std::string 而不是映射

正在打印 std::string

0:000> .printf "%ma\n" , @@c++(test._Mypair._Myval2._Bx._Ptr)

Hello I am Me Are You Thou

_Bx 是联合使用适当的成员

0:000> ?? test._Mypair._Myval2._Bx._Buf
char [16] 0x0013f888
96 '`'
0:000> ?? test._Mypair._Myval2._Bx._Ptr
char * 0x001a9860
 ".Hello I am Me Are You Thou."
0:000> ?? test._Mypair._Myval2._Bx._Alias
char [16] 0x0013f888
96 '

或在打印前将其转换为正确的类型

0:000> ?? (char *)*(unsigned long *)test._Mypair._Myval2._Bx._Buf
char * 0x001a9860
 ".Hello I am Me Are You Thou."

或者这里是为 _Buf 正确转换的 printf 变体,其中包含一个 _Ptr

0:000> .printf "%ma\n" , @@c++( *(char **)(test._Mypair._Myval2._Bx._Buf) )

Hello I am Me Are You Thou

如果有人想跟进
用于显示这些结果的代码如下

using namespace std;
#define ALLOCSIZ 5
const char *Greek_Alphabets[ALLOCSIZ] = { "Alpha","Beta","Gamma","Delta","Epsilon" };

__declspec(noinline) void  play_with_map() {
    cout << "playing with maps\n\n";
    map<char, const char*> mymap;
    for (int i = 0; i < ALLOCSIZ; i++) {
        mymap.insert(pair<char, const char*>('A' + i, Greek_Alphabets[i]));
    }
    map<char, const char*>::iterator iter = mymap.begin();
    for (iter; iter != mymap.end(); iter++)
        cout << iter->first << " = " << iter->second << "\n";
}
__declspec(noinline) void play_with_vector(){
    cout << "playing with vectors\n\n";
    vector< pair< char, const char* > > myvec;
    for (int i = 0; i < ALLOCSIZ; i++) {
        myvec.push_back(make_pair('A' + i, Greek_Alphabets[i]));
    }
    for (int i = 0; i < ALLOCSIZ; i++) {
        cout << myvec[i].first << " = "<< myvec[i].second <<"\n";
    }
}
int main() {
    play_with_map();
    play_with_vector();
    return 0;
}

std::string函数如下

__declspec(noinline) void play_with_std_string() {
    string test("\nHello I am Me Are You Thou\n");
    cout << test.c_str();
}

JAVASCRIPT


要转储的示例 Javascript std::map map mymap

"use strict";

// make an alias so that it can be used like regular bang command !dumpmap address
function initializeScript() {
    return [new host.functionAlias(dump_std_map, "dumpmap")];
}

// a helper to log strings
function log(instr) {
    host.diagnostics.debugLog(instr + "\n")
}

//use dt /v /t std::map* to get a type description string and pass it 
// or use the default (applicable for this example only 
// std::map< int , int > will not pan for  std::map <bar , foo > 
function dump_std_map(input , typedesc) {
    var typeDescription;
    if(typedesc) {
        typeDescription = typedesc 
        }else {
            typeDescription = "(std::map<char,char const *,std::less<char>,std::allocator<std::pair<char const ,char const *> > > *)"
        }   
    var foo = host.evaluateExpression( typeDescription + input.toString() )
    var mapsize = foo._Mypair._Myval2._Myval2._Mysize
    for (var i=0; i<mapsize;i++) 
    {
        log (foo.dereference().Skip(i).First().toString())
    }
}

并像

一样使用它
0:000> ? mymap
Evaluate expression: 2357904 = 0023fa90
0:000> !dumpmap 0x23fa90
65 'A', "Alpha"
66 'B', "Beta"
67 'C', "Gamma"
68 'D', "Delta"
69 'E', "Epsilon"
@$dumpmap(0x23fa90)