编写你自己的美化器

前言 在CrackingOysters:你还在用GDB调试程序吗?介绍了使用Python拓展gdb方便平时的debug体验。 其中的一项功能是pretty printer。本文详细介绍编写pretty printer,用于打印自己的数据结构。 比如你有一个结构体很多数据成员,

struct MyStruct {
  std::name mName;
  std::map mField1;
  std::set mField2;
  int mI;
  int mj;
};

但是你大部分时候打印都是只看字段mName和mI,那么就可以定义一个针对这个数据结构的pretty printer,这样大部分时候你就只看到需要的字段。而不用在几十个字段找你所关心。 如果不使用任何的pretty printer,打印一个MyStruct的数据结构会得到

$2 = {mName = {static npos = <optimized out>, 
    _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, 
      _M_p = 0x618c38 "student"}}, mField1 = {_M_t = {
      _M_impl = {<std::allocator<std::_Rb_tree_node<std::pair<int const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<int const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >> = {<No data fields>}, <No data fields>}, <std::_Rb_tree_key_compare<std::less<int> >> = {
          _M_key_compare = {<std::binary_function<int, int, bool>> = {<No data fields>}, <No data fields>}}, <std::_Rb_tree_header> = {
          _M_header = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffe4e0, _M_right = 0x7fffffffe4e0}, 
          _M_node_count = 0}, <No data fields>}}}, mField2 = {_M_t = {
      _M_impl = {<std::allocator<std::_Rb_tree_node<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {<No data fields>}, <No data fields>}, <std::_Rb_tree_key_compare<std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >> = {
          _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>}}, <std::_Rb_tree_header> = {_M_header = {
            _M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffe510, _M_right = 0x7fffffffe510}, 
          _M_node_count = 0}, <No data fields>}}}, mI = 3, mj = 4}

看起来会头皮发麻! 如果使用gdb 自带的STL pretty printer,那么我们会得到如下简洁的结果,

(gdb) p s
$1 = {mName = "student", mField1 = std::map with 0 elements, mField2 = std::set with 0 elements, mI = 3, mj = 4}\

如果自己编写pretty printer,那么就会得到如下的结果,

(gdb) p s
$2 = MyStruct
 name: "student"  integer: 3

这样子,只会打印自己关心的数据,如果希望看看原始的数据,那么p /r s 整体思路 需要做的是三件事情: 定义打印类,提供to_string()方法,这个方法返回你要打印出来的字符串。 判断一个value,是否需要使用你定义的类来打印。 注册你的判断函数到gdb pretty printing里面 定义打印类

class MyPrinter:
    def __init__(self, val):
        self.val = val
    def to_string(self):
         return ”name: {}  integer: {}".format(self.val['mName'], self.val['mI']

判断一个value,是否需要使用自己定义的打印类

def lookup_pretty_printer(val):
    if val.type.code == gdb.TYPE_CODE_PTR:
        return None # to add 
    if 'MyStruct' == val.type.tag:
        return MyPrinter(val)
    return None

注册到gdb

gdb.printing.register_pretty_printer(
    gdb.current_objfile(),
    lookup_pretty_printer, replace=True)

将下面的程序编译,并测试

struct MyStruct {
  std::string mName;
  std::map<int, std::string> mField1;
  std::set<std::string> mField2;
  int mI;
  int mj;
};

int main() {
   MyStruct s = {std::string("student"), lm, ls, 3, 4}
   return 0;
}