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

基本概念:

一般程序发生coredump,80%的可能是由于参数的值不对造成的(其他可能是堆栈溢出、多线程等问题造成的)。对于可以复现的问题,一般拿到函数堆栈,通过走读代码基本上就可以定位。对于无法复现的问题,大部分情况需要通过查看变量的值来定位。(注:网上很多文章都是介绍通过反汇编定位哪条语句引起的coredump,个人认为这种方法比较繁琐(需要重新编译),且对优化后的程序用处不大,本文就不讨论了。)

变量可以分为两类,一类是基本类型,例如int等,可以在函数堆栈里直接看到值;另一类是类类型,包括string,STL和用户自定义类型等,这种类型在函数堆栈里会显示它的地址。

使用"地址/格式"这种形式,可以查看地址中保存的值。例如,有代码:

int *p = new int(2010);

假设p=0x8011320,则使用8011320/D,就可以显示2010。常用的格式如下:

X:十六进制int(一般用来显示地址)

D:十进制有符号int

U:十进制无符号int

E:十进制无符号long long

e:十进制有符号long long

s:显示字符串

查看变量的值时,一定要有源代码(至少要有函数声明),否则就无法知道变量的类型,许多地址取值操作就无法实施。

实例:

OS:Solaris10(x86)

编译器:Sun Studio 11

1) 看实参的值,普通函数,参数为内置类型(int类型)。

有如下程序:

view plaincopy to clipboardprint?
void f(int i)
{
throw 0;
}

int main()
{
int i = 1;
f(i);
return 0;
}
void f(int i)
{
throw 0;
}

int main()
{
int i = 1;
f(i);
return 0;
}

编译(CC -o 1 coretest.cpp)、运行,会产生core文件。

使用MDB:

bash-3.00# mdb core

Loading modules: [ libc.so.1 ld.so.1 ]

> $G

C++ symbol demangling enabled

> $C

080471f4 libc.so.1`_lwp_kill+7(1, 6)

0804720c libc.so.1`raise+0x1f(6)

08047254 libc.so.1`abort+0xcd(fee28ef0, a8, 804727c, fee14653, fee28ef0,

fee29a20)

08047264 libCrun.so.1`void __Cimpl::default_terminate+0x18(fee28ef0, fee29a20,

80472ac, fefc0568, 80472ac, fee14ab9)

0804727c libCrun.so.1`void std::terminate+0x1b(0, fee29804, fee28ef0, 28,

fee1537d, 0)

080472ac libCrun.so.1`void __Cimpl::ex_terminate+0x39(8047440, fee29804,

fee28ef0, 0, 805092a, 80472e4)

080472c8 libCrun.so.1`_ex_throw_body+0x79(fee29a20, 0)

080472e4 libCrun.so.1`void __Crun::ex_throw+0x52(fee29a6c, 8050b78, 0)

0804730c void f+0x37(1)

0804732c main+0x1f(1, 8047358, 8047360)

0804734c _start+0x7a(1, 80474a8, 0, 80474ac, 80474e0, 80474f5)

其中1即为参数i的值。

2) 看实参的值,普通函数,参数为类类型。

有如下程序:

view plaincopy to clipboardprint?
class A
{
public:
int m_i;
int m_j;
};

void f(const A& a)
{
throw 0;
}

int main()
{
A a;
a.m_i = 3;
a.m_j = 7;
f(a);
return 0;
}
class A
{
public:
int m_i;
int m_j;
};

void f(const A& a)
{
throw 0;
}

int main()
{
A a;
a.m_i = 3;
a.m_j = 7;
f(a);
return 0;
}

编译(CC -o 1 coretest2.cpp)、运行,会产生core文件。

使用MDB:

bash-3.00# mdb core

Loading modules: [ libc.so.1 ld.so.1 ]

> $G

C++ symbol demangling enabled

> $C

080471f4 libc.so.1`_lwp_kill+7(1, 6)

0804720c libc.so.1`raise+0x1f(6)

08047254 libc.so.1`abort+0xcd(fee28ef0, a8, 804727c, fee14653, fee28ef0,

fee29a20)

08047264 libCrun.so.1`void __Cimpl::default_terminate+0x18(fee28ef0, fee29a20,

80472ac, fefc0568, 80472ac, fee14ab9)

0804727c libCrun.so.1`void std::terminate+0x1b(0, fee29804, fee28ef0, 28,

fee1537d, 0)

080472ac libCrun.so.1`void __Cimpl::ex_terminate+0x39(8047440, fee29804,

fee28ef0, 0, 805092a, 80472e0)

080472c8 libCrun.so.1`_ex_throw_body+0x79(fee29a20, 0)

080472e0 libCrun.so.1`void __Crun::ex_throw+0x52(fee29a6c, 8050b80, 0)

08047308 void f+0x37(8047320)

0804732c main+0x26(1, 8047358, 8047360)

0804734c _start+0x7a(1, 80474a8, 0, 80474ac, 80474e0, 80474f5)

因为函数原型为void f(const A& a),a为引用类型,所以8047320为a的地址(16进制),即m_i的地址,所以m_i的值为:

> 8047320/X

0x8047320: 3

m_j的地址为m_i的地址加4,所以m_j的值为:

> 8047320+4/X

0x8047324: 7

3) 看实参的值,普通函数,参数为类类型(有虚函数表)。

有如下程序:

view plaincopy to clipboardprint?
class A
{
public:
virtual ~A(){}
int m_i;
int m_j;
};

void f(const A& a)
{
throw 0;
}

int main()
{
A a;
a.m_i = 3;
a.m_j = 7;
f(a);
return 0;
}
class A
{
public:
virtual ~A(){}
int m_i;
int m_j;
};

void f(const A& a)
{
throw 0;
}

int main()
{
A a;
a.m_i = 3;
a.m_j = 7;
f(a);
return 0;
}

编译(CC -o 1 coretest3.cpp)、运行,会产生core文件。

使用MDB:

bash-3.00# mdb core

Loading modules: [ libc.so.1 ld.so.1 ]

> $G

C++ symbol demangling enabled

> $C

080471e4 libc.so.1`_lwp_kill+7(1, 6)

080471fc libc.so.1`raise+0x1f(6)

08047244 libc.so.1`abort+0xcd(fee28ef0, a8, 804726c, fee14653, fee28ef0,

fee29a20)

08047254 libCrun.so.1`void __Cimpl::default_terminate+0x18(fee28ef0, fee29a20,

804729c, fefc0568, 804729c, fee14ab9)

0804726c libCrun.so.1`void std::terminate+0x1b(0, fee29804, fee28ef0, 28,

fee1537d, 0)

0804729c libCrun.so.1`void __Cimpl::ex_terminate+0x39(8047440, fee29804,

fee28ef0, 0, 8050a4a, 80472d8)

080472b8 libCrun.so.1`_ex_throw_body+0x79(fee29a20, 0)

080472d8 libCrun.so.1`void __Crun::ex_throw+0x52(fee29a6c, 8050d58, 0)

08047300 void f+0x37(804731c)

0804732c main+0x32(1, 8047358, 8047360)

0804734c _start+0x7a(1, 80474a8, 0, 80474ac, 80474e0, 80474f5)

因为函数原型为void f(const A& a),a为引用类型,所以804731c为a的地址(16进制)。因为类型A含有虚函数,所以804731c指向的内容8060fa4为虚函数表的地址:

> 804731c/X

0x804731c: 8060fa4

> 8060fa4/X

1`__1cBAG__vtbl_:

1`A::__vtbl: 8050d80

> 8060fa4::nm

Value Size Type Bind Other Shndx Name

0x08060fa4|0x0000000c|OBJT |WEAK |0x0 |18 |1`__1cBAG__vtbl_

m_i的地址为a的地址加4,所以m_i的值为:

> 804731c+4/X

0x8047320: 3

m_j的地址为m_i的地址加4,所以m_j的值为:

> 804731c+4+4/X

0x8047324: 7

4) 看实参的值,成员函数,参数为类类型(有虚函数表)。

有如下程序:

view plaincopy to clipboardprint?
class A
{
public:
virtual ~A(){}
int m_i;
int m_j;
};

class B
{
public:
void f(const A& a)
{
throw 0;
}
};

int main()
{
A a;
a.m_i = 3;
a.m_j = 7;
B b;
b.f(a);
return 0;
}
class A
{
public:
virtual ~A(){}
int m_i;
int m_j;
};

class B
{
public:
void f(const A& a)
{
throw 0;
}
};

int main()
{
A a;
a.m_i = 3;
a.m_j = 7;
B b;
b.f(a);
return 0;
}

编译(CC -o 1 -g coretest4.cpp)、运行,会产生core文件。

使用MDB:

bash-3.00# mdb core

Loading modules: [ libc.so.1 ld.so.1 ]

> $G

C++ symbol demangling enabled

> $C

080471e4 libc.so.1`_lwp_kill+7(1, 6)

080471fc libc.so.1`raise+0x1f(6)

08047244 libc.so.1`abort+0xcd(fee28ef0, a8, 804726c, fee14653, fee28ef0,

fee29a20)

08047254 libCrun.so.1`void __Cimpl::default_terminate+0x18(fee28ef0, fee29a20,

804729c, fefc0568, 804729c, fee14ab9)

0804726c libCrun.so.1`void std::terminate+0x1b(0, fee29804, fee28ef0, 28,

fee1537d, 0)

0804729c libCrun.so.1`void __Cimpl::ex_terminate+0x39(8047440, fee29804,

fee28ef0, 0, 8050a4a, 80472d0)

080472b8 libCrun.so.1`_ex_throw_body+0x79(fee29a20, 0)

080472d0 libCrun.so.1`void __Crun::ex_throw+0x52(fee29a6c, 8050d94, 0)

080472f8 void B::f+0x37(804731b, 804731c)

0804732c main+0x39(1, 8047358, 8047360)

0804734c _start+0x7a(1, 80474a8, 0, 80474ac, 80474e0, 80474f5)

因为f为非静态成员函数,所以第一个参数(804731b)为this指针,第二个参数(804731c)为a的地址,其他变量查看方法同3)

注:如果f为静态成员函数,则不存在this指针。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lw1a2/archive/2009/10/06/4636469.aspx

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

LW1A2@163.COM

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

一、目的

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

二、原理

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

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 addr:0x804792c::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_vread为mdb_vread(buf, sizeof(int), 0x804792c)

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

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

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

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

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

三、实例

OS:Solaris10(x86)

编译器:Sun Studio 11

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

完整代码:

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

四、详细说明

1) list

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

{

__buffer_size,

__buffer_list,

__free_list,

__next_avail,

__last,

__node, //指向list的node的指针

__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指针,可以遍历整个双向链表。

使用方法:

将上面的压缩包解压开,执行make,solaris会根据系统的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

stl的vector中的成员保存在一块连续的内存中,如果能得到成员变量的开始地址、结束地址和成员的大小,就能确定每个成员的地址。通过dbx的print -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

stl的map内部使用红黑树实现。通过dbx的print -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,之后就可以得到first和second;

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

使用方法:

l 8047830::pmap int, int:知道map的定义为map,打印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

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

{

color_field,

parent_link,

left_link,

right_link,

value_field //set中的key

}

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

使用方法同list

五、参考资料:

《Solaris 模块调试器指南(819-7055-10)》

《STL源码剖析》

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lw1a2/archive/2010/01/17/5203645.aspx

使用MDB查看变量的值相关推荐

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

    使用MDB查看变量的值(2) LW1A2@163.COM 本节描述使用MDB查看core文件中STL变量的知识 一.目的 在<使用MDB查看变量的值(1)>中,我们 探讨了查看变量值的一般 ...

  2. pycharm如何在程序运行后查看变量的值,变量的类型(不通过print和debug的方式)

    文章目录: 1 问题描述 2 ycharm如何在程序运行后查看变量的值,变量的类型 1 问题描述 有时候程序中有很多变量,我们在调试程序的时候需要知道这些变量的的值和变量的类型,如果通过print的方 ...

  3. vs2015自动窗口(查看变量的值)

    问题:在使用vs2015时,调试无法看到变量的值,解决方案如下: 1.启动调试 2.调试-->窗口-->自动窗口(Ctrl+Alt+V,A) 如下图:

  4. S32DS实时查看变量的值

    变量动态监测要求在调试时,点击让CPU全速运行的过程中,能够观察到变量的动态变化.目前只有S32DS for ARM v2.0及更高版本的IDE通过PEMicro提供的Real Time Expres ...

  5. qt5.9.0调试如何查看变量的值_从0开发3D引擎(四):搭建测试环境

    大家好,本文介绍了3D引擎的测试方法,搭建了本地的测试环境. 上一篇博文 wonder-yyc:从0开发3D引擎(三):搭建开发环境​zhuanlan.zhihu.com 下一篇博文 wonder-y ...

  6. qt5.9.0调试如何查看变量的值_深入了解 Java 调试

    Bug(俗称"八阿哥") 是软件开发绕不过的一道坎,因此调试便成了每位程序员一项必备的核心技能.调试不仅有助于理解程序的运行流程,还能改进代码质量,最终提高开发者解决问题的能力以及 ...

  7. 【Keil C51】使用 watch1 来查看变量的值

    在使用Keil C51,进行软件调试时,对变量观察的办法如下: 在变量处单击右键,选择添加至watch1窗口,即可看到R6变量在代码调试运行时,具体的数值变化.

  8. gdb+linux+查看变量,gdb查看变量值

    转贴地址 gdb调试过程中如何查看变量的值?一般说来使用print(p)指令来实现,并有自己很多的输出格式. print和它的显示格式 p /x var #十六进制显示变量. p /d var #十进 ...

  9. keil如何进行软件仿真,以及如何查看变量的实时值

    1.打开工程,点击魔术棒选择芯片和晶振,如下图 2.在Debug里面选择选择Use Simulator-使用软件仿真,勾选上Run to main() 3.点击红色的d符号,即可以进入仿真界面 下面介 ...

最新文章

  1. 数学知识-- 信赖域(Trust Region)算法是怎么一回事
  2. JavaScript基础学习之强制类转换(二)
  3. Oracle PL/SQL 程序设计读书笔记 - 第14章 DML和事务管理
  4. Python简介-01-Python的起源
  5. oracle exchange partition 測试
  6. CentOS 6.x通过yum安装php7.1及相关环境
  7. Word转换pdf技巧之pdf虚拟打印机怎么用教程
  8. 场景应用:100亿的数据你怎么排序
  9. EXCEL数据处理小结(SQL)
  10. 【自然语言处理】论述自然语言处理的技术范畴
  11. UVM实战 卷I学习笔记14——OVM到UVM的迁移
  12. 移动应用的引导模式设计
  13. matlab怎样分别求偏相关系数,如何用Matlab计算相关系数和偏相关系数
  14. 模型预测控制(MPC)解析(七):约束控制问题的表述
  15. 专科咋了?5年时间从三流外包到阿里P6,逆袭成功终于肝出了这份大厂Android研发岗中高级面经!
  16. win10 ubuntu16.04 双系统 时间不一致问题解决办法
  17. Error creating bean with name ‘ribbonLoadBalancingHttpClient‘
  18. 网络空间开源威胁情报分析的人机优化策略研究
  19. 首席新媒体运营黎想教程:活动运营推广入门全攻略及进阶
  20. 我们在文本摘要方面取得了什么成就?

热门文章

  1. 机器学习 刀光剑影 之屠龙刀
  2. java火车站售票源代码_火车站售票管理系统 JavaSe
  3. 计算机网络基础的五个层,计算机网络基础(五) 运输层
  4. 用python做PDF本地化压缩,新增多进程
  5. 30 个相见恨晚的超实用网站
  6. Pytorch单机多卡加速
  7. canvas小鸟飞行游戏
  8. HTML5 Canvas编写五彩连珠(5):寻路
  9. Python神笔马良案例集简介
  10. Keep上市,流血不止