使用MDB查看变量的值(2)

使用MDB查看变量的值(2

LW1A2@163.COM

 

本节描述使用MDB查看core文件中STL变量的知识

 

一、目的

在《使用MDB查看变量的值(1)》中,我们 探讨了查看变量值的一般方法,但是对于复杂的对象,一点一点的查看内存太麻烦,MDB提供一种机制,可以自己实现插件来解析内存中的变量。

 

二、原理

在《Solaris 模块调试器指南(819705510)》的第十章详细的介绍了编写插件的方法。这里只简单介绍下几个重要的函数。

1)  const mdb_modinfo_t *_mdb_init(void);

mdb插件的初始化函数,返回一个mdb_modinfo_t结构的指针,mdb_modinfo_t的定义如下:

typedef struct mdb_modinfo {

    ushort_t mi_dvers;               /* Debugger API version number */

    const mdb_dcmd_t *mi_dcmds;      /* NULL-terminated list of dcmds */

    const mdb_walker_t *mi_walkers;  /* NULL-terminated list of walks */

} mdb_modinfo_t;

l  mi_dvers表示版本号,应该始终设置为MDB_API_VERSION

l  mi_dcmds指向自定义dcmd命令的数组

l  mi_walkers指向自定义walker命令的数组。

mi_dcmds的例子:

static const mdb_dcmd_t mi_dcmds [] = {

{ "plist", NULL, "print list", plist }, //自定义dcmd命令plist

{ NULL }

};

注:本文只介绍自定义dcmd命令的实现

 

2)  void _mdb_fini(void);

自定义插件卸载时(::unload)执行的操作。

 

3)  int dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv);

自定义dcmd命令的实现:

例如上面的mi_dcmds的例子中,就需要定义应该这样的函数:

int plist (uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv);

l  addr0x804792c::plist,这样调用plist时,addr就等于0x804792c

l  flags:标志位,其中flags & DCMD_ADDRSPEC为真时,表示以addr::plist这样的形式调用自定义dcmd

l  argc:参数个数,如0x804792c::plist int,参数个数就为1

l  argv:参数值,如0x804792c::plist int,参数值就为int

 

4)  ssize_t mdb_vread(void *buf, size_t nbytes, uintptr_t addr);

从给定的目标虚拟地址addr开始,取长度为nbytes的一块内存值,将其赋给buf。例如:

0x804792c/D对应的mdb_vreadmdb_vread(buf, sizeof(int), 0x804792c)

 

5)  ssize_t mdb_readstr(char *s, size_t nbytes, uintptr_t addr);

从给定的目标虚拟地址addr开始,以空字符结尾的C 字符串读入由s 寻址的缓冲区中。主要用来读取内存中的字符串,例如:

0x804792c/s对应的mdb_readstrmdb_readstr(s, 255, 0x804792c),其中255为缓冲区最大值。

 

6)  void mdb_printf(const char *format, ...);

类似于printf,将计算完的值格式化打印到屏幕上。

 

三、实例

OSSolaris10x86

编译器:Sun Studio 11

/usr/demo/mdb下有一个MDB插件的例子,本实例根据这个例子改编而来,实现了查看std::liststd::vectorstd::mapstd::set的值。对于以上四种stl容器,除了可以以地址形式打印成员变量外,还支持以intlong longstring这三种类型来打印成员变量。

完整代码:

http://download.csdn.net/source/2002961

 

 

四、详细说明

1)  list

stllist使用双向链表来实现,通过dbxprint –r,可以显示出list的结构,其伪结构如下(每个变量都是指针):

    {

        __buffer_size,

        __buffer_list,

        __free_list,

        __next_avail,

        __last,

        __node,               //指向listnode的指针

        __length              //list含有的成员个数

    }

list由多个__node组成,__node的伪结构:

    {

        next,         //双向链表中,指向下一个节点的指针

        prev,         //双向链表中,指向上一个节点的指针

        data                      //指向list成员的指针,本程序就是打印这个指针

    }

假设程序里list变量的地址为0x804792c,则:

l  __length的地址为0x804792c+sizeof(uintptr_t)*6,由此可以计算出list成员的个数;

l  __node的地址为0x804792c+sizeof(uintptr_t)*5,由此可以计算出每个list成员的地址。__node伪结构中的data即为每个成员的值(或指针)。通过next指针,可以遍历整个双向链表。

使用方法:

将上面的压缩包解压开,执行makesolaris会根据系统的CPU编译不同的动态库,笔者的系统是x86系统,所有会在i386目录下生成printstl.so。可以将这个动态库复制到/usr/lib/mdb/proc/目录下(MDB插件默认目录),然后在mdb中使用::load printstl.so加载插件,使用::unload printstl.so卸载插件。如果不将动态库复制到插件的默认目录,则需要使用绝对路径加载插件:::load /XXX/printstl.so

具体使用方法:

l  804792c::plist:以指针形式打印list,例:

> 804792c::plist

The list size is: 3

The list member is: ([0]0x8079bf0, [1]0x8099978, [2]0x8099988)

l  804792c::plist int:知道list里保存的是int类型,打印list,例:

> 804792c::plist int

The list size is: 3

The list member is: ([0]123, [1]456, [2]789)

l  804792c::plist long long:知道list里保存的是long long类型,打印list,例:

> 804792c::plist long long

The list size is: 3

The list member is: ([0] 9223372036854775807, [1]456, [2]789)

l  804792c::plist string:知道list里保存的是string类型,打印list,例:

> 8047804::plist string

The list size is: 3

The list member is: ([0]abc, [1]def, [2]Good)

 

2)  vector

stlvector中的成员保存在一块连续的内存中,如果能得到成员变量的开始地址、结束地址和成员的大小,就能确定每个成员的地址。通过dbxprint –r,可以显示出vector的结构,其伪结构如下(每个变量都是指针):

    {

        __buffer_size,

        __start,                         //成员变量的开始地址

        __finish,                        //成员变量的结束地址

        __end_of_storage

  }

假设程序里vector变量的地址为0x8047900,则:

l  __start的地址为0x8047900+sizeof(uintptr_t)

l  __finish的地址为0x8047900+sizeof(uintptr_t)*2

使用方法同list

注:若无法确定成员的大小,则只能打印出开始地址和结束地址。

 

3)  map

stlmap内部使用红黑树实现。通过dbxprint –r,可以显示出map的结构,其伪结构如下(每个变量都是指针):

  {

        __buffer_size,

        __buffer_list,

        __free_list,

        __next_avail,

        __last,

        __header,           //指向红黑树的head的指针

        __node_count,       //含有的成员个数

        __insert_always,

        __key_compare

  }

map节点,伪结构如下:

  {

        color_field,

        parent_link,

        left_link.

        right_link,

        first,        //map中的key

        second      //map中的value

  }

假设程序里map变量的地址为0x8047830,则:

l  __node_count的地址为0x8047830+sizeof(uintptr_t)*6

l  __header的地址为0x8047830+sizeof(uintptr_t)*5,之后就可以得到firstsecond

l  得到红黑树的head指针后,就可以使用遍历二叉树的方法来遍历。使用中序遍历二叉树的方法,可以将map中的成员按照从小到大的顺序打印出。

使用方法:

l  8047830::pmap int, int:知道map的定义为map<int,int>,打印map,例:

> 8047830::pmap int, int

The map size is: 3

The map member is: ([0](8,4000), [1](9,2000), [2](1999,2000))

其他使用方法可参考list

 

4)  set

stlset内部也是使用红黑树实现,只不过set节点的结构有所不同:

  {

        color_field,

        parent_link,

        left_link,

        right_link,

        value_field //set中的key

  }

set中只有一个key,其他的地方都和map相同。

使用方法同list

 

五、参考资料:

Solaris 模块调试器指南(819705510)》

STL源码剖析》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值