背景:
当我们用gdb调试stl时候,往往看到的是stl内部变量的值,比如打印一个string a
输出:
(gdb) p a
$1 = {
static npos = 18446744073709551615,
_M_dataplus = {
<__gnu_cxx::new_allocator
members ofstd::basic_string
_M_p = 0x50b028 "test"
}
}
打印一个vector v:
(gdb) p v
$2 = {
_M_impl = {
<__gnu_cxx::new_allocator
members ofstd::_Vector_base
_M_start = 0x50b060,
_M_finish = 0x50b068,
_M_end_of_storage = 0x50b068
}
},
打印其中的某个元素:
(gdb) p v[0]
One of the arguments you tried to pass to operator[] couldnot be converted to what the function wants.
(gdb) p v.at(0)
Cannot evaluate function -- may be inlined
当我们不熟悉stl内部实现的时候,这给我们调试问题带来了一些麻烦。
1) 使用gdb user define command
其实就是一些gdb语法实现一些宏,一个比较常用的是writen by Dan Marinescu 的dbinit_stl_views。内容参考如下:
http://www.yolinux.com/TUTORIALS/src/dbinit_stl_views-1.03.txt
把上面文件写入.gdbinit,gdb启动的时候自动加载;一般来说这样就可以按照上面文档的说明接口调试stl,比如pvector pstring,但当我使用这些接口的时候,遇到下面的情况:
原因:我的gcc貌似不支持 $argc (所在的版本应该是支持的,可能是没有加某些编译选项,未找到原因),没想到更好的方案,如果不升级gcc,只能自己修改上面的接口,为多参数增加更多的宏定义;参考下面的源码:
#
# STL GDB evaluators/views/utilities - 1.03
#
# The new GDB commands:
# are entirely non instrumental
# do not depend on any "inline"(s) - e.g. size(), [], etc
# are extremely tolerant to debugger settings
#
# This file should be "included" in .gdbinit as following:
# source stl-views.gdb or just paste it into your .gdbinit file
#
# The following STL containers are currently supported:
#
# std::vector -- via pvector command
# std::list -- via plist or plist_member command
# std::map -- via pmap or pmap_member command
# std::multimap -- via pmap or pmap_member command
# std::set -- via pset command
# std::multiset -- via pset command
# std::deque -- via pdequeue command
# std::stack -- via pstack command
# std::queue -- via pqueue command
# std::priority_queue -- via ppqueue command
# std::bitset -- via pbitset command
# std::string -- via pstring command
# std::widestring -- via pwstring command
#
# The end of this file contains (optional) C++ beautifiers
# Make sure your debugger supports $argc
#
# Simple GDB Macros writen by Dan Marinescu (H-PhD) - License GPL
# Inspired by intial work of Tom Malnar,
# Tony Novac (PhD) / Cornell / Stanford,
# Gilad Mishne (PhD) and Many Many Others.
# Contact: [email protected] (Subject: STL)
#
# Modified to work with g++ 4.3 by Anders Elton
# Also added _member functions, that instead of printing the entire class in map, prints a member.
#
# std::vector<>
#
# 输出vector中的全部元素,size, capacity, 元素类型
define pvector
set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start
set $capacity = $arg0._M_impl._M_end_of_storage - $arg0._M_impl._M_start
set $size_max = $size - 1
set $i = 0
while $i < $size
printf "elem[%u]: ", $i
p *($arg0._M_impl._M_start + $i)
set $i++
end
printf "Vector size = %u\n", $size
printf "Vector capacity = %u\n", $capacity
printf "Element "
whatis $arg0._M_impl._M_start
end
#输出vector中指定索引位置的元素
define pvector_index
set $idx = $arg1
set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start
set $size_max = $size - 1
if $idx < 0 || $idx > $size_max
printf "idx is not in acceptable range: [0..%u].\n", $size_max
else
printf "elem[%u]: ", $idx
p *($arg0._M_impl._M_start + $idx)
end
end
#输出vector中指定索引范围内的元素
define pvector_range
set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start
set $size_max = $size - 1
printf "size_max[%u]: ", $size_max
set $start_idx = $arg1
set $stop_idx = $arg2
if $start_idx > $stop_idx
set $tmp_idx = $start_idx
set $start_idx = $stop_idx
set $stop_idx = $tmp_idx
end
if $start_idx < 0 || $stop_idx < 0 || $start_idx > $size_max || $stop_idx > $size_max
printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
else
set $i = $start_idx
while $i <= $stop_idx
printf "elem[%u]: ", $i
p *($arg0._M_impl._M_start + $i)
set $i++
end
end
end
define pvector_itr
printf "iter: "
p *($arg1*)($arg0._M_current)
end
document pvector
Prints std::vector information.
Syntax: pvector
Syntax: pvector_index
Syntax: pvector_range
Note: idx, idx1 and idx2 must be in acceptable range [0...size()-1].
Examples:
pvector v - Prints vector content, size, capacity and T typedef
pvector_index v 0 - Prints element[idx] from vector
pvector_range v 1 2 - Prints elements in range [idx1..idx2] from vector
pvector_itr iter int - Prints itr
end
#
# std::list<>
#
define plist
set $head = &$arg0._M_impl._M_node
set $current = $arg0._M_impl._M_node._M_next
set $size = 0
while $current != $head
set $current = $current._M_next
set $size++
end
printf "List size = %u \n", $size
printf "List "
whatis $arg0
printf "Use plist_all plist_index to see the elements in the list.\n"
end
#打印list中全部元素, 注意需要传入list中每个元素的类型
define plist_all
set $head = &$arg0._M_impl._M_node
set $current = $arg0._M_impl._M_node._M_next
set $size = 0
while $current != $head
printf "elem[%u]: ", $size
p *($arg1*)($current + 1)
set $current = $current._M_next
set $size++
end
printf "List size = %u \n", $size
end
#打印list中指定索引位置的元素(索引位置从0开始)
define plist_index
set $head = &$arg0._M_impl._M_node
set $current = $arg0._M_impl._M_node._M_next
set $size = 0
while $current != $head
if $size == $arg2
printf "elem[%u]: ", $size
p *($arg1*)($current + 1)
end
set $current = $current._M_next
set $size++
end
printf "List size = %u \n", $size
end
define plist_itr
set $itr = $arg0
printf "iter: "
p *($arg1*)($itr._M_node+1)
end
document plist
Prints std::list information.
Syntax: plist : Prints list size, if T defined all elements or just element at idx
Examples:
plist l - prints list size and definition
plist_all l int - prints all elements and list size
plist_index l int 2 - prints the third element in the list (if exists) and list size
plist_iter iter int prints iterator
end
#输出list中所有元素的某个成员 (plist_member l_pos pos x 1)
define plist_member
set $head = &$arg0._M_impl._M_node
set $current = $arg0._M_impl._M_node._M_next
set $size = 0
while $current != $head
printf "elem[%u]: ", $size
p (*($arg1*)($current + 1)).$arg2
set $current = $current._M_next
set $size++
end
end
#输出list中指定索引位置的某个成员 (plist_member_index l_pos pos x 1)
define plist_member_index
set $head = &$arg0._M_impl._M_node
set $current = $arg0._M_impl._M_node._M_next
set $size = 0
while $current != $head
if $size == $arg3
printf "elem[%u]: ", $size
p (*($arg1*)($current + 1)).$arg2
end
set $current = $current._M_next
set $size++
end
end
document plist_member
Prints std::list information.
Syntax: plist_member : Prints list size, if T defined all elements or just element at idx
Examples:
plist_member l int member - prints all elements and list size
plist_member_index l int member 2 - prints the third element in the list (if exists) and list size
end
#
# std::map and std::multimap
#
# 输出map中元素的类型,与map size
define pmap
set $tree = $arg0
set $i = 0
set $node = $tree._M_t._M_impl._M_header._M_left
set $end = $tree._M_t._M_impl._M_header
set $tree_size = $tree._M_t._M_impl._M_node_count
printf "Map size = %u\n", $tree_size
printf "Map "
whatis $tree
printf "Use pmap_all to see the elements in the map.\n"
end
# 打印map中的所有元素 pmap_all mapStudent int string
define pmap_all
set $tree = $arg0
set $i = 0
set $node = $tree._M_t._M_impl._M_header._M_left
set $end = $tree._M_t._M_impl._M_header
set $tree_size = $tree._M_t._M_impl._M_node_count
while $i < $tree_size
set $value = (void *)($node + 1)
printf "elem[%u].left: ", $i
p *($arg1*)$value
set $value = $value + sizeof($arg1)
printf "elem[%u].right: ", $i
p *($arg2*)$value
if $node._M_right != 0
set $node = $node._M_right
while $node._M_left != 0
set $node = $node._M_left
end
else
set $tmp_node = $node._M_parent
while $node == $tmp_node._M_right
set $node = $tmp_node
set $tmp_node = $tmp_node._M_parent
end
if $node._M_right != $tmp_node
set $node = $tmp_node
end
end
set $i++
end
end
#打印map中左值为某个元素的匹配对与个数,pmap_left_match mapStudent int string 3
define pmap_left_match
set $tree = $arg0
set $i = 0
set $node = $tree._M_t._M_impl._M_header._M_left
set $end = $tree._M_t._M_impl._M_header
set $tree_size = $tree._M_t._M_impl._M_node_count
set $idx = $arg3
set $ElementsFound = 0
while $i < $tree_size
set $value = (void *)($node + 1)
if *($arg1*)$value == $idx
printf "elem[%u].left: ", $i
p *($arg1*)$value
set $value = $value + sizeof($arg1)
printf "elem[%u].right: ", $i
p *($arg2*)$value
set $ElementsFound++
end
if $node._M_right != 0
set $node = $node._M_right
while $node._M_left != 0
set $node = $node._M_left
end
else
set $tmp_node = $node._M_parent
while $node == $tmp_node._M_right
set $node = $tmp_node
set $tmp_node = $tmp_node._M_parent
end
if $node._M_right != $tmp_node
set $node = $tmp_node
end
end
set $i++
end
printf "Number of elements found = %u\n", $ElementsFound
end
# 打印左值和右值匹配的个数 pmap_left_right_match mappair int int 2 200
define pmap_left_right_match
set $tree = $arg0
set $i = 0
set $node = $tree._M_t._M_impl._M_header._M_left
set $end = $tree._M_t._M_impl._M_header
set $tree_size = $tree._M_t._M_impl._M_node_count
set $idx1 = $arg3
set $idx2 = $arg4
set $ElementsFound = 0
while $i < $tree_size
printf "input[%u].left: ", $i
set $value = (void *)($node + 1)
set $valueLeft = *($arg1*)$value
set $valueRight = *($arg2*)($value + sizeof($arg1))
if $valueLeft == $idx1 && $valueRight == $idx2
printf "elem[%u].left: ", $i
p $valueLeft
printf "elem[%u].right: ", $i
p $valueRight
set $ElementsFound++
end
if $node._M_right != 0
set $node = $node._M_right
while $node._M_left != 0
set $node = $node._M_left
end
else
set $tmp_node = $node._M_parent
while $node == $tmp_node._M_right
set $node = $tmp_node
set $tmp_node = $tmp_node._M_parent
end
if $node._M_right != $tmp_node
set $node = $tmp_node
end
end
set $i++
end
printf "Number of elements found = %u\n", $ElementsFound
end
# pmap_itr mapiter int int
define pmap_itr
echo $arg0
printf "->left: "
p *($arg1*)($arg0._M_node+1)
echo $arg0
printf "->right: "
p *($arg2*)((void*)($arg0._M_node+1)+sizeof($arg1))
end
document pmap
Prints std::map or std::multimap information. Works for std::multimap as well.
Syntax: pmap
但是缺陷,我们需要自己记忆很多的命令,不支持内嵌类型
2)使用python pretty-printers
Gdb 7.0 加入python脚本支持,可以使用python pretty-printers显示stl内容,pretty-printers使用方法:
https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Developer_Guide/debuggingprettyprinters.html
具体使用:
1)安装gdb.7以上版本(需要安装过python的机器):
./configure--prefix=/home/users/liufeng01/bin/gdb/--with-python=/home/users/liufeng01/python/
2)通过svn下载python libstdc++ printers:
svn cosvn://gcc.gnu.org/svn/gcc/trunk/libstdc++-v3/python
3)在.gdbinit中添加:
python
import sys
sys.path.insert(0,'/home/maude/gdb_printers/python')
from libstdcxx.v6.printers importregister_libstdcxx_printers
register_libstdcxx_printers (None)
end
直接可以通过 p 打印vectorstring等内容,p /r 可以打印原来 print打印的内容。
Pretty print是个看似完美的解决方案,但是但是在我的测试机器上对list与map的支持还有问题,可能是stl版本的问题。