使用MDB查看变量的值(2)
使用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<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
stl的set內部也是使用紅黑樹實現,只不過set節點的結構有所不同:
? {
??????? color_field,
??????? parent_link,
??????? left_link,
??????? right_link,
??????? value_field //set中的key
? }
set中只有一個key,其他的地方都和map相同。
使用方法同list
?
五、參考資料:
《Solaris 模塊調試器指南(819–7055–10)》
《STL源碼剖析》
總結
以上是生活随笔為你收集整理的使用MDB查看变量的值(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 优化资源配置读后感
- 下一篇: opencv_python阈值处理