我正在使用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>}
}
}
我找到了自己的答案,进一步阅读了有关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_eval
acts作为执行实际方法调用的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:增加雇用俄语给出的精确度。