2013年7月20日 星期六

gdb 顯示 STL container 的方法

問題描述

如下列的程式碼:

std::vector<int> numbers;
for (int i = 0; i < 4; i++) {
  numbers.push_back(i * 3);
}

std::map<std::string, std::string> contacts;
contacts["john"] = "0987-654321";
contacts["mary"] = "";

在 gdb 中不易閱讀 numbers 和 contacts 的顯示結果:

(gdb) p numbers
$1 = {<std::_Vector_base<int, std::allocator<int> >> = {_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, _M_start = 0x6060
10, _M_finish = 0x606020, _M_end_of_storage = 0x606020}}, <No data fields>}
(gdb) p contacts
$2 = {_M_t = {_M_impl = {<std::allocator<std::_Rb_tree_node<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_
traits<char>, std::allocator<char> > > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std
::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<No data fields>}, <No data fields>}, _M_key_compare = {<std::binary_function<std::basic_string<char, std
::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool>> = {<No data fields>}, <No data fields>}, _M_header = {_M_co
lor = std::_S_red, _M_parent = 0x606080, _M_left = 0x606080, _M_right = 0x606120}, _M_node_count = 2}}}

解法一: 土法煉鋼

參考這篇的作法, 先取得目標位址再取值:

(gdb) p numbers
$5 = {<std::_Vector_base<int, std::allocator<int> >> = {_M_impl = {<std::allocator<int>> = {<__gnu_cxx::new_allocator<int>> = {<No data fields>}, <No data fields>}, _M_start = 0x60601
0, _M_finish = 0x606020, _M_end_of_storage = 0x606020}}, <No data fields>}
(gdb) ptype numbers._M_impl
type = struct std::_Vector_base<int, std::allocator<int> >::_Vector_impl
    : public std::allocator<int> {
    std::allocator<int>::pointer _M_start;
    std::allocator<int>::pointer _M_finish;
    std::allocator<int>::pointer _M_end_of_storage;
  public:
    void _Vector_impl(void);
    void _Vector_impl(
    const std::_Vector_base<int, std::allocator<int> >::_Vector_impl::_Tp_alloc_type &);
}
(gdb) ptype numbers._M_impl._M_start
type = int *
(gdb) p numbers._M_impl._M_start[1]
$7 = 3
(gdb) p *(numbers._M_impl._M_start)@numbers.size()
$12 = {0, 3, 6, 9}

先用 ptype 逐步了解各欄位的型別, 繼而找到儲存資料的欄位 numbers._M_impl._M_start。 由於 _M_start 是 int*, 倒數第二個指令直接取出 numbers[1] 的值, 最後一個指令則是用 print P@N 印出從位置 P 開始 N 筆資料。

嫌麻煩的話, 可以定義新的 gdb command:

(gdb) define pv
Type commands for definition of "pv".
End with a line saying just "end".
>if $argc == 1
 >p *($arg0._M_impl._M_start)@$arg0.size()
 >end
>if $argc == 2
 >p $arg0._M_impl._M_start[$arg1]
 >end
>end
(gdb) pv numbers
$21 = {0, 3, 6, 9}
(gdb) pv numbers 1
$22 = 3

將上述的 define pv ... end 寫入 /.gdbinit, 之後就不用重寫一次。

解法二: 使用別人寫好的 pretty-printers

土法煉鋼的目的是讓我們有辦法應付日後各種需求, 但是針對 STL 這種大家都有的需求, 已經有善心人士提供完整的套件了, 見 STLSupport - GDB Wiki 的第一點。

摘要作法如下:

$ cd /path/to/gdb_script/
$ svn co svn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python
$ cat <<EOF >> ~/.gdbinit
python
import sys
sys.path.insert(0, '/path/to/gdb_script/python')
from libstdcxx.v6.printers import register_libstdcxx_printers
register_libstdcxx_printers (None)
end
EOF

載入 pretty-printers 後, 輸出變數的結果如下:

(gdb) p numbers
$1 = std::vector of length 4, capacity 4 = {0, 3, 6, 9}
(gdb) p contacts
$2 = std::map with 2 elements = {["john"] = "0987-654321", ["mary"] = ""}

有需要的時候, 也可以參考 python/libstdcxx/v6/printers.py 學習如何用 python 寫 gdb script

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...