使用GDB的C ++调试/打印自定义类型:nlohmann json库的情况

问题描述 投票:15回答:1

我正在使用nlohmann's json C++ implementation开展一个项目。

如何在GDB中轻松探索nlohmann的JSON键/值?

我试图使用这个STL gdb wrapping,因为它提供帮助来探索lohmann的JSON lib正在使用的STL结构。但我觉得不方便。

这是一个简单的用例:

json foo;
foo["flex"] = 0.2;
foo["awesome_str"] = "bleh";
foo["nested"] = {{"bar", "barz"}}; 

我想在GDB中拥有什么:

(gdb) p foo
{
    "flex" : 0.2,
    "awesome_str": "bleh",
    "nested": etc.
}

目前的行为

(gdb) p foo
$1 = {
  m_type = nlohmann::detail::value_t::object, 
  m_value = {
    object = 0x129ccdd0, 
    array = 0x129ccdd0, 
    string = 0x129ccdd0, 
    boolean = 208, 
    number_integer = 312266192, 
    number_unsigned = 312266192, 
    number_float = 1.5427999782486669e-315
  }
}
(gdb) p foo.at("flex")
Cannot evaluate function -- may be inlined // I suppose it depends on my compilation process. But I guess it does not invalidate the question.
(gdb) p *foo.m_value.object
$2 = {
  _M_t = {
    _M_impl = {
      <std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer> > > >> = {
        <__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer> > > >> = {<No data fields>}, <No data fields>}, 
      <std::_Rb_tree_key_compare<std::less<void> >> = {
        _M_key_compare = {<No data fields>}
      }, 
      <std::_Rb_tree_header> = {
        _M_header = {
          _M_color = std::_S_red, 
          _M_parent = 0x4d72d0, 
          _M_left = 0x4d7210, 
          _M_right = 0x4d7270
        }, 
        _M_node_count = 5
      }, <No data fields>}
  }
}
c++ json gdb pretty-print nlohmann-json
1个回答
21
投票

我找到了自己的答案,进一步阅读了有关print of std::string的GDB功能和堆栈溢出问题。短路是目前最好的选择。

短路

我只是定义了一个gdb命令,如下所示:

# this is a gdb script
# can be loaded from gdb using
# source my_script.txt (or. gdb or whatever you like)
define pjson
# use the lohmann's builtin dump method, ident 4 and use space separator
printf "%s\n", $arg0.dump(4, ' ', true).c_str()
end
# configure command helper (text displayed when typing 'help pjson' in gdb)
document pjson
Prints a lohmann's JSON C++ variable as a human-readable JSON string
end

在gdb中使用它:

(gdb) source my_custom_script.gdb
(gdb) pjson foo
{
    "flex" : 0.2,
    "awesome_str": "bleh",
    "nested": {
        "bar": "barz"
    }
}

超过顶部(但不适合我)

另一种方法是在python中定义一个GDB漂亮的打印机,并使其与您的项目紧密相关(激活自动加载的东西)。请参阅this link以获得深入的方法。

基本上,当你在gdb中输入时:

(gdb) p foo

并且GDB将自动测试foo的类型并调用相关的漂亮打印机(如果有的话)。这最终将导致同样的结果。主要区别在于它是使用众所周知的print命令完成的,更重要的是,即使没有较差的过程来调用方法也是有效的(感谢Employed Russian的精确度)。调试人员不必学习新命令(如简短答案中定义的pjson)。

下面,一些GDB doc提取+一个​​不起作用的python代码尝试。


引用:

漂亮的打印机由两部分组成:一个用于检测类型是否受支持的查找功能,以及打印机本身。

这是一个示例,显示如何编写std::string打印机。有关此类必须提供的API的详细信息,请参阅Pretty Printing API

class StdStringPrinter(object):
    "Print a std::string"

    def __init__(self, val):
        self.val = val

    def to_string(self):
        return self.val['_M_dataplus']['_M_p']

    def display_hint(self):
        return 'string'

仍然为了完整性而引用:

以下是一个示例,说明如何编写上述打印机示例的查找功能。

def str_lookup_function(val):
    lookup_tag = val.type.tag
    if lookup_tag == None:
        return None
    regex = re.compile("^std::basic_string<char,.*>$")
    if regex.match(lookup_tag):
        return StdStringPrinter(val)
    return None

我试着用这种方式实现它。但是,我有以下代码的100%失败率,带有神秘的GDB错误消息(参见下面的代码示例)

注意:它依赖于trick provided here,它应该允许在GDB中进行C ++类方法调用,绕过Value.Type检查(可以找到对象方法,它们的value.Type将是gdb.TYPE_CODE_METHOD,但是gdb python不会认为它们是可调用的。只有gdb.TYPE_CODE_FUNC是因此,parse_and_evalacts作为执行实际方法调用的hack)。

import gdb
import re

class StdStringPrinter(object):
    """Print a std::string"""
    def __init__(self, val):
        self.val = val
    def to_string(self):
        eval_string = "(*("+str(self.val.type)+"*)("+str(self.val.address)+")).c_str()" # works 50% of the time ...
        return gdb.parse_and_eval(eval_string)
    def display_hint(self):
        return 'string'

class LohmannJSONPrinter(object):
    """Print a nlohmann::json"""
    def __init__(self, val):
        self.val = val
    def to_string(self):

        # workaround from here:
        # https://stackoverflow.com/a/22798055/7237062
        # "(*("+str(self.val.type)+"*)("+str(self.val.address)+")).method()"
        eval_string = '(*('+str(self.val.type)+'*)('+str(self.val.address)+')).dump(4, " ", true)'
        return gdb.parse_and_eval(eval_string) # fails 100% of the time
    def display_hint(self):
        return self.val.type

def build_pretty_printer():
    pp = gdb.printing.RegexpCollectionPrettyPrinter("foo")
    json = r"nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer>"
    pp.add_printer('nlohmann::json', json, LohmannJSONPrinter)
    return pp

# executed at autoload gdb.printing.register_pretty_printer(gdb.current_objfile(),
                                     build_pretty_printer())

错误:

Cannot insert breakpoint -18. // or any negative value
Cannot access memory at address 0x111a2180 // appears to be a fixed value at each execution
Python Exception <class 'gdb.error'> Command aborted.

要么

$2 = Python Exception <class 'gdb.error'> Attempt to take address of value not located in memory.:

编辑2019-march-24:增加雇用俄语给出的精确度。

© www.soinside.com 2019 - 2024. All rights reserved.