C++ debug/print custom type with GDB : the case of nlohmann json library












14















I'm working on a project using nlohmann's json C++ implementation.



How can one easily explore nlohmann's JSON keys/vals in GDB ?



I tried to use this STL gdb wrapping since it provides helpers to explore STL structures that lohmann's JSON lib is using.
But I don't find it convenient.



Here is a simple use case:



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


What I would like to have in GDB:



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


Current behavior



(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>}
}
}









share|improve this question




















  • 1





    You mean you don't find great joy manually digging through red/black trees to try and find something? ;)

    – Retired Ninja
    Mar 24 at 0:03
















14















I'm working on a project using nlohmann's json C++ implementation.



How can one easily explore nlohmann's JSON keys/vals in GDB ?



I tried to use this STL gdb wrapping since it provides helpers to explore STL structures that lohmann's JSON lib is using.
But I don't find it convenient.



Here is a simple use case:



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


What I would like to have in GDB:



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


Current behavior



(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>}
}
}









share|improve this question




















  • 1





    You mean you don't find great joy manually digging through red/black trees to try and find something? ;)

    – Retired Ninja
    Mar 24 at 0:03














14












14








14


1






I'm working on a project using nlohmann's json C++ implementation.



How can one easily explore nlohmann's JSON keys/vals in GDB ?



I tried to use this STL gdb wrapping since it provides helpers to explore STL structures that lohmann's JSON lib is using.
But I don't find it convenient.



Here is a simple use case:



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


What I would like to have in GDB:



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


Current behavior



(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>}
}
}









share|improve this question
















I'm working on a project using nlohmann's json C++ implementation.



How can one easily explore nlohmann's JSON keys/vals in GDB ?



I tried to use this STL gdb wrapping since it provides helpers to explore STL structures that lohmann's JSON lib is using.
But I don't find it convenient.



Here is a simple use case:



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


What I would like to have in GDB:



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


Current behavior



(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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 23 at 23:39







LoneWanderer

















asked Mar 23 at 17:44









LoneWandererLoneWanderer

1,206825




1,206825








  • 1





    You mean you don't find great joy manually digging through red/black trees to try and find something? ;)

    – Retired Ninja
    Mar 24 at 0:03














  • 1





    You mean you don't find great joy manually digging through red/black trees to try and find something? ;)

    – Retired Ninja
    Mar 24 at 0:03








1




1





You mean you don't find great joy manually digging through red/black trees to try and find something? ;)

– Retired Ninja
Mar 24 at 0:03





You mean you don't find great joy manually digging through red/black trees to try and find something? ;)

– Retired Ninja
Mar 24 at 0:03












1 Answer
1






active

oldest

votes


















20














I found my own answer reading further the GDB capabilities and stack overflow questions concerning print of std::string.
The short path is the best option for now.



Short path



I simply defined a gdb command as follows:



# 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 "%sn", $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


Using it in gdb:



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




Over the top (but not working for me)



The other way is to define a GDB pretty printer in python and make it tightly associated to your project (autoloading stuff activated). See this link for an in-depth approach.



Basically, when in gdb you would type:



(gdb) p foo


and GDB will automagically test for foo's type and invoke the associated pretty printer if any. That would end-up in the same result. The main difference is that it is done using the well-known print command and more importantly, be effective even if there is no inferior process to call methods from (thanks Employed Russian for the precision). The person debugging would not have to learn a new command (like the pjson defined in the short answer).



Below, some GDB doc extract + a python code attempt that does not work.





Quoting:




A pretty-printer consists of two parts: a lookup function to detect if the type is supported, and the printer itself.



Here is an example showing how a std::string printer might be written. See Pretty Printing API, for details on the API this class must provide.




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'


Still quoting for the sake of completness:




And here is an example showing how a lookup function for the printer example above might be written.




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




I tried to implement it this way. However, I have a 100% failure rate with the following code, with cryptic GDB error messages (see below the code sample)



Nota: it relies on the trick provided here that is supposed to allow a C++ class method call within GDB, bypassing the Value.Type check (object methods can be found and their value.Type would be gdb.TYPE_CODE_METHOD, but gdb python won't consider them callable. Only gdb.TYPE_CODE_FUNC are callable. So, parse_and_evalacts as an hack to perform actual method call).



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())


Errors:



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.


or



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




Edit 2019-march-24 : add precision given by employed russian.






share|improve this answer





















  • 3





    "The main difference is that ..." -- no: the main difference is that GDB pretty printer will work form e.g. a core dump, whereas calling into the inferior process requires a (live) inferior (and one that hasn't been too corrupted).

    – Employed Russian
    Mar 24 at 20:43













  • Thanks for the remark, I edited the post accordingly.

    – LoneWanderer
    Mar 24 at 22:17













Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55316620%2fc-debug-print-custom-type-with-gdb-the-case-of-nlohmann-json-library%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









20














I found my own answer reading further the GDB capabilities and stack overflow questions concerning print of std::string.
The short path is the best option for now.



Short path



I simply defined a gdb command as follows:



# 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 "%sn", $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


Using it in gdb:



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




Over the top (but not working for me)



The other way is to define a GDB pretty printer in python and make it tightly associated to your project (autoloading stuff activated). See this link for an in-depth approach.



Basically, when in gdb you would type:



(gdb) p foo


and GDB will automagically test for foo's type and invoke the associated pretty printer if any. That would end-up in the same result. The main difference is that it is done using the well-known print command and more importantly, be effective even if there is no inferior process to call methods from (thanks Employed Russian for the precision). The person debugging would not have to learn a new command (like the pjson defined in the short answer).



Below, some GDB doc extract + a python code attempt that does not work.





Quoting:




A pretty-printer consists of two parts: a lookup function to detect if the type is supported, and the printer itself.



Here is an example showing how a std::string printer might be written. See Pretty Printing API, for details on the API this class must provide.




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'


Still quoting for the sake of completness:




And here is an example showing how a lookup function for the printer example above might be written.




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




I tried to implement it this way. However, I have a 100% failure rate with the following code, with cryptic GDB error messages (see below the code sample)



Nota: it relies on the trick provided here that is supposed to allow a C++ class method call within GDB, bypassing the Value.Type check (object methods can be found and their value.Type would be gdb.TYPE_CODE_METHOD, but gdb python won't consider them callable. Only gdb.TYPE_CODE_FUNC are callable. So, parse_and_evalacts as an hack to perform actual method call).



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())


Errors:



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.


or



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




Edit 2019-march-24 : add precision given by employed russian.






share|improve this answer





















  • 3





    "The main difference is that ..." -- no: the main difference is that GDB pretty printer will work form e.g. a core dump, whereas calling into the inferior process requires a (live) inferior (and one that hasn't been too corrupted).

    – Employed Russian
    Mar 24 at 20:43













  • Thanks for the remark, I edited the post accordingly.

    – LoneWanderer
    Mar 24 at 22:17


















20














I found my own answer reading further the GDB capabilities and stack overflow questions concerning print of std::string.
The short path is the best option for now.



Short path



I simply defined a gdb command as follows:



# 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 "%sn", $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


Using it in gdb:



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




Over the top (but not working for me)



The other way is to define a GDB pretty printer in python and make it tightly associated to your project (autoloading stuff activated). See this link for an in-depth approach.



Basically, when in gdb you would type:



(gdb) p foo


and GDB will automagically test for foo's type and invoke the associated pretty printer if any. That would end-up in the same result. The main difference is that it is done using the well-known print command and more importantly, be effective even if there is no inferior process to call methods from (thanks Employed Russian for the precision). The person debugging would not have to learn a new command (like the pjson defined in the short answer).



Below, some GDB doc extract + a python code attempt that does not work.





Quoting:




A pretty-printer consists of two parts: a lookup function to detect if the type is supported, and the printer itself.



Here is an example showing how a std::string printer might be written. See Pretty Printing API, for details on the API this class must provide.




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'


Still quoting for the sake of completness:




And here is an example showing how a lookup function for the printer example above might be written.




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




I tried to implement it this way. However, I have a 100% failure rate with the following code, with cryptic GDB error messages (see below the code sample)



Nota: it relies on the trick provided here that is supposed to allow a C++ class method call within GDB, bypassing the Value.Type check (object methods can be found and their value.Type would be gdb.TYPE_CODE_METHOD, but gdb python won't consider them callable. Only gdb.TYPE_CODE_FUNC are callable. So, parse_and_evalacts as an hack to perform actual method call).



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())


Errors:



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.


or



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




Edit 2019-march-24 : add precision given by employed russian.






share|improve this answer





















  • 3





    "The main difference is that ..." -- no: the main difference is that GDB pretty printer will work form e.g. a core dump, whereas calling into the inferior process requires a (live) inferior (and one that hasn't been too corrupted).

    – Employed Russian
    Mar 24 at 20:43













  • Thanks for the remark, I edited the post accordingly.

    – LoneWanderer
    Mar 24 at 22:17
















20












20








20







I found my own answer reading further the GDB capabilities and stack overflow questions concerning print of std::string.
The short path is the best option for now.



Short path



I simply defined a gdb command as follows:



# 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 "%sn", $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


Using it in gdb:



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




Over the top (but not working for me)



The other way is to define a GDB pretty printer in python and make it tightly associated to your project (autoloading stuff activated). See this link for an in-depth approach.



Basically, when in gdb you would type:



(gdb) p foo


and GDB will automagically test for foo's type and invoke the associated pretty printer if any. That would end-up in the same result. The main difference is that it is done using the well-known print command and more importantly, be effective even if there is no inferior process to call methods from (thanks Employed Russian for the precision). The person debugging would not have to learn a new command (like the pjson defined in the short answer).



Below, some GDB doc extract + a python code attempt that does not work.





Quoting:




A pretty-printer consists of two parts: a lookup function to detect if the type is supported, and the printer itself.



Here is an example showing how a std::string printer might be written. See Pretty Printing API, for details on the API this class must provide.




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'


Still quoting for the sake of completness:




And here is an example showing how a lookup function for the printer example above might be written.




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




I tried to implement it this way. However, I have a 100% failure rate with the following code, with cryptic GDB error messages (see below the code sample)



Nota: it relies on the trick provided here that is supposed to allow a C++ class method call within GDB, bypassing the Value.Type check (object methods can be found and their value.Type would be gdb.TYPE_CODE_METHOD, but gdb python won't consider them callable. Only gdb.TYPE_CODE_FUNC are callable. So, parse_and_evalacts as an hack to perform actual method call).



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())


Errors:



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.


or



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




Edit 2019-march-24 : add precision given by employed russian.






share|improve this answer















I found my own answer reading further the GDB capabilities and stack overflow questions concerning print of std::string.
The short path is the best option for now.



Short path



I simply defined a gdb command as follows:



# 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 "%sn", $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


Using it in gdb:



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




Over the top (but not working for me)



The other way is to define a GDB pretty printer in python and make it tightly associated to your project (autoloading stuff activated). See this link for an in-depth approach.



Basically, when in gdb you would type:



(gdb) p foo


and GDB will automagically test for foo's type and invoke the associated pretty printer if any. That would end-up in the same result. The main difference is that it is done using the well-known print command and more importantly, be effective even if there is no inferior process to call methods from (thanks Employed Russian for the precision). The person debugging would not have to learn a new command (like the pjson defined in the short answer).



Below, some GDB doc extract + a python code attempt that does not work.





Quoting:




A pretty-printer consists of two parts: a lookup function to detect if the type is supported, and the printer itself.



Here is an example showing how a std::string printer might be written. See Pretty Printing API, for details on the API this class must provide.




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'


Still quoting for the sake of completness:




And here is an example showing how a lookup function for the printer example above might be written.




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




I tried to implement it this way. However, I have a 100% failure rate with the following code, with cryptic GDB error messages (see below the code sample)



Nota: it relies on the trick provided here that is supposed to allow a C++ class method call within GDB, bypassing the Value.Type check (object methods can be found and their value.Type would be gdb.TYPE_CODE_METHOD, but gdb python won't consider them callable. Only gdb.TYPE_CODE_FUNC are callable. So, parse_and_evalacts as an hack to perform actual method call).



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())


Errors:



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.


or



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




Edit 2019-march-24 : add precision given by employed russian.







share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 24 at 22:16

























answered Mar 23 at 17:44









LoneWandererLoneWanderer

1,206825




1,206825








  • 3





    "The main difference is that ..." -- no: the main difference is that GDB pretty printer will work form e.g. a core dump, whereas calling into the inferior process requires a (live) inferior (and one that hasn't been too corrupted).

    – Employed Russian
    Mar 24 at 20:43













  • Thanks for the remark, I edited the post accordingly.

    – LoneWanderer
    Mar 24 at 22:17
















  • 3





    "The main difference is that ..." -- no: the main difference is that GDB pretty printer will work form e.g. a core dump, whereas calling into the inferior process requires a (live) inferior (and one that hasn't been too corrupted).

    – Employed Russian
    Mar 24 at 20:43













  • Thanks for the remark, I edited the post accordingly.

    – LoneWanderer
    Mar 24 at 22:17










3




3





"The main difference is that ..." -- no: the main difference is that GDB pretty printer will work form e.g. a core dump, whereas calling into the inferior process requires a (live) inferior (and one that hasn't been too corrupted).

– Employed Russian
Mar 24 at 20:43







"The main difference is that ..." -- no: the main difference is that GDB pretty printer will work form e.g. a core dump, whereas calling into the inferior process requires a (live) inferior (and one that hasn't been too corrupted).

– Employed Russian
Mar 24 at 20:43















Thanks for the remark, I edited the post accordingly.

– LoneWanderer
Mar 24 at 22:17







Thanks for the remark, I edited the post accordingly.

– LoneWanderer
Mar 24 at 22:17






















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55316620%2fc-debug-print-custom-type-with-gdb-the-case-of-nlohmann-json-library%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Plaza Victoria

Puebla de Zaragoza

Musa