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

Lon*_*rer 17 c++ json gdb pretty-print nlohmann-json

我正在使用nlohmann的json C ++实现进行项目开发

如何在GDB中轻松浏览nlohmann的JSON键/值?

我尝试使用此STL gdb包装,因为它提供了帮助者探索nlohmann的JSON库正在使用的标准C ++库结构。但是我觉得不方便。

这是一个简单的用例:

json foo;
foo["flex"] = 0.2;
foo["awesome_str"] = "bleh";
foo["nested"] = {{"bar", "barz"}}; 
Run Code Online (Sandbox Code Playgroud)

我想在GDB中拥有什么:

(gdb) p foo
{
    "flex" : 0.2,
    "awesome_str": "bleh",
    "nested": etc.
}
Run Code Online (Sandbox Code Playgroud)

当前行为

(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>}
  }
}
Run Code Online (Sandbox Code Playgroud)

Lon*_*rer 23

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

短路径v3.1.2

我只是简单地定义了一个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
Run Code Online (Sandbox Code Playgroud)

在gdb中使用它:

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

短路径v3.7.0 [编辑] 2019-onv-06 也可以使用新的to_string()方法,但是我无法通过较差的实时处理方法与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, json::error_handler_t::strict).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
Run Code Online (Sandbox Code Playgroud)

顶过(但不适合我)

另一种方法是在python中定义一个GDB 漂亮打印机,并使它与您的项目紧密关联(激活自动加载功能)。请参阅此链接以获取深入的方法。

基本上,在gdb中时,您将输入:

(gdb) p foo
Run Code Online (Sandbox Code Playgroud)

并且GDB会自动测试foo的类型,并调用关联的漂亮打印机(如果有)。最终结果会相同。主要区别在于它是使用众所周知的print命令完成的,而且更重要的是,即使没有劣等的方法来调用方法,该方法也有效(感谢Employed Russian的准确性)。调试人员不必学习新命令(如pjson简短答案中定义的命令)。

下面,一些GDB文档解压缩+ 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'
Run Code Online (Sandbox Code Playgroud)

为了完整起见,仍在引用:

这是一个示例,显示了如何编写上述打印机示例的查找功能。

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
Run Code Online (Sandbox Code Playgroud)

我试图以这种方式实现它。但是,使用以下代码以及隐秘的GDB错误消息,我的失败率为100%(请参见下面的代码示例)

注意:它依赖于此处提供技巧,该技巧应该允许GDB中的C ++类方法调用,而绕过Value.Type检查(可以找到对象方法,它们value.Type将是gdb.TYPE_CODE_METHOD,但是gdb python不会将它们视为可调用的。只能gdb.TYPE_CODE_FUNC被调用。因此,parse_and_eval充当执行实际方法调用的技巧。

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:
        # /sf/answers/1595863881/
        # "(*("+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())
Run Code Online (Sandbox Code Playgroud)

错误:

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.
Run Code Online (Sandbox Code Playgroud)

要么

$2 = Python Exception <class 'gdb.error'> Attempt to take address of value not located in memory.:
Run Code Online (Sandbox Code Playgroud)

编辑2019年3月24日:增加受雇俄语提供的精度

  • “主要区别在于……”-否:主要区别在于,GDB漂亮打印机可以在诸如核心转储的形式下工作,而调用劣质进程则需要一个(活动的)劣质(并且没有太腐败了)。 (3认同)