diff --git a/.drone.star b/.drone.star index 32ca713c5..ad7906f84 100644 --- a/.drone.star +++ b/.drone.star @@ -26,6 +26,7 @@ def main(ctx): linux_cxx("UBSan GCC", "g++-12", packages="g++-12", buildscript="drone", buildtype="boost", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-12', 'B2_CXXSTD': '11,14,17', 'B2_UBSAN': '1', 'B2_DEFINES': 'define=BOOST_NO_STRESS_TEST=1', 'B2_LINKFLAGS': '-fuse-ld=gold'}, globalenv=globalenv), linux_cxx("UBSan Clang", "clang++-14", packages="clang-14 libstdc++-10-dev", buildtype="boost", buildscript="drone", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'ubsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'clang-14', 'B2_CXXSTD': '11,14,17', 'B2_UBSAN': '1', 'B2_DEFINES': 'define=BOOST_NO_STRESS_TEST=1'}, globalenv=globalenv), linux_cxx("TSan", "g++-12", packages="g++-12", buildscript="drone", buildtype="boost", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'tsan', 'B2_VARIANT': 'debug', 'B2_TOOLSET': 'gcc-12', 'B2_CXXSTD': '11,14,17', 'B2_TSAN': '1', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1'}, globalenv=globalenv, privileged=True), + linux_cxx("GDB printers", "g++-12", packages="g++-12 gdb", buildscript="drone", buildtype="boost", image="cppalliance/droneubuntu2204:1", environment={'COMMENT': 'gdb', 'B2_TOOLSET': 'gcc-12', 'B2_CXXSTD': '11,14,17', 'B2_TARGETS': 'libs/json/test//gdb-printers', 'B2_VARIANT': 'debug', 'B2_DEFINES': 'BOOST_NO_STRESS_TEST=1'}, globalenv=globalenv), linux_cxx("gcc 4.8 C++03 (no op)", "g++-4.8", packages="g++-4.8", image=linuxglobalimage, buildtype="boost", buildscript="drone", environment={"B2_TOOLSET": "gcc-4.8", "B2_CXXSTD": "03", 'B2_SEPARATE_BOOTSTRAP': '1'}, globalenv=globalenv), linux_cxx("gcc 4.8", "g++-4.8", packages="g++-4.8", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-4.8', 'B2_CXXSTD': '11', 'B2_SEPARATE_BOOTSTRAP': '1', 'DRONE_JOB_UUID': '0ade7c2cf9', 'B2_FLAGS': 'warnings=extra warnings-as-errors=on define=BOOST_JSON_ALLOW_DEPRECATED'}, globalenv=globalenv), linux_cxx("gcc 4.9", "g++-4.9", buildtype="boost", buildscript="drone", image=linuxglobalimage, environment={'B2_TOOLSET': 'gcc-4.9', 'B2_CXXSTD': '11', 'B2_SEPARATE_BOOTSTRAP': '1', 'DRONE_JOB_UUID': 'b1d5781111', 'B2_FLAGS': 'warnings=extra warnings-as-errors=on define=BOOST_JSON_ALLOW_DEPRECATED'}, globalenv=globalenv), diff --git a/.drone/drone.sh b/.drone/drone.sh index 917d22e6e..281b9bddf 100755 --- a/.drone/drone.sh +++ b/.drone/drone.sh @@ -87,6 +87,8 @@ common_install echo '==================================> SCRIPT' +printf "add-auto-load-safe-path $PWD/bin.v2\n" > ~/.gdbinit + export B2_TARGETS=${B2_TARGETS:-"libs/$SELF/test libs/$SELF/example"} $BOOST_ROOT/libs/$SELF/ci/travis/build.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 077340066..f5d159c96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,16 @@ if(BOOST_JSON_IS_ROOT) unset(CMAKE_FOLDER) endif() +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/pretty_printers") +find_package(BoostPrettyPrinters QUIET) +if(BoostPrettyPrinters_FOUND) + boost_pretty_printers_gdb_python_header( + TARGET boost_json_regenerate_printers + INPUT src/boost_json_gdb_printers.py + OUTPUT include/boost/json/detail/gdb_printers.hpp + HEADER_GUARD BOOST_JSON_DETAIL_GDB_PRINTERS_HPP + EXCLUDE_FROM_ALL) +endif() function(boost_json_setup_properties target) if(MSVC) diff --git a/build.jam b/build.jam index 01ed18fe3..3b7750c36 100644 --- a/build.jam +++ b/build.jam @@ -5,6 +5,11 @@ require-b2 5.2 ; +import path ; +import-search [ path.join $(__file__:D) pretty_printers ] ; + +using boost-pretty-printers ; + constant boost_dependencies : /boost/align//boost_align /boost/assert//boost_assert diff --git a/build/Jamfile b/build/Jamfile index c7ab7e7be..23bd03799 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -8,9 +8,11 @@ # Official repository: https://github.com/boostorg/json # -import modules ; import-search /boost/config/checks ; + +import boost-pretty-printers ; import config ; +import modules ; # These make sure we only build on compatible C++11 or later toolchains. obj cxx11_basic_alignas : ../check/basic_alignas.cpp ; @@ -82,3 +84,12 @@ project alias json_deps ; alias json_sources : src.cpp ; lib boost_json : json_sources ; + +boost-pretty-printers.gdb-python-header gdb_printers.hpp + : boost_json_gdb_printers.py + : ../include/boost/json/detail + --header-guard=BOOST_JSON_DETAIL_GDB_PRINTERS_HPP + ; +always gdb_printers.hpp ; +explicit regenerate-printers gdb_printers.hpp ; +alias regenerate-printers : gdb_printers.hpp ; diff --git a/include/boost/json/detail/config.hpp b/include/boost/json/detail/config.hpp index 24062b7a5..dd89f20f5 100644 --- a/include/boost/json/detail/config.hpp +++ b/include/boost/json/detail/config.hpp @@ -233,4 +233,6 @@ constexpr T static_const::value; # define BOOST_JSON_DEPRECATED(x) #endif +#include + #endif diff --git a/include/boost/json/detail/gdb_printers.hpp b/include/boost/json/detail/gdb_printers.hpp new file mode 100644 index 000000000..1c0c7bb4a --- /dev/null +++ b/include/boost/json/detail/gdb_printers.hpp @@ -0,0 +1,348 @@ +// +// Copyright (c) 2024 Dmitry Arkhipov (grisumbras@yandex.ru) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/json +// + +// Autogenerated from boost_json_gdb_printers.py by boost-gdb + +#ifndef BOOST_JSON_DETAIL_GDB_PRINTERS_HPP +#define BOOST_JSON_DETAIL_GDB_PRINTERS_HPP + +#ifndef BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS + +#if defined(__ELF__) + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Woverlength-strings" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Woverlength-strings" +#endif + +__asm__( + ".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n" + ".ascii \"\\4gdb.inlined-script.BOOST_JSON_DETAIL_GDB_PRINTERS_HPP\\n\"\n" + ".ascii \"import gdb\\n\"\n" + ".ascii \"import gdb.printing\\n\"\n" + + + ".ascii \"class PrettyPrinterBuilder():\\n\"\n" + ".ascii \" '''decorator that accumulates pretty printers for types'''\\n\"\n" + + ".ascii \" def __init__(self, name=None):\\n\"\n" + ".ascii \" self.result = gdb.printing.RegexpCollectionPrettyPrinter(\\n\"\n" + ".ascii \" name or 'Boost.JSON')\\n\"\n" + + ".ascii \" def __call__(self, pp=None, ns=None, template=False):\\n\"\n" + ".ascii \" if pp is None:\\n\"\n" + ".ascii \" def decorator(pp):\\n\"\n" + ".ascii \" return self(pp, ns=ns, template=template)\\n\"\n" + ".ascii \" return decorator\\n\"\n" + + ".ascii \" typename = getattr(pp, '__name__')\\n\"\n" + ".ascii \" ns = ns or 'boost::json'\\n\"\n" + ".ascii \" self.result.add_printer(\\n\"\n" + ".ascii \" typename,\\n\"\n" + ".ascii \" '^{ns}::{typename}{marker}'.format(\\n\"\n" + ".ascii \" ns=ns,\\n\"\n" + ".ascii \" typename=typename,\\n\"\n" + ".ascii \" marker='<'if template else '$'),\\n\"\n" + ".ascii \" pp)\\n\"\n" + ".ascii \" return pp\\n\"\n" + + ".ascii \"pretty_printer = PrettyPrinterBuilder()\\n\"\n" + + + ".ascii \"class static_property:\\n\"\n" + ".ascii \" '''decorator for lazy evaluation of static members'''\\n\"\n" + + ".ascii \" def __init__(self, wrapped):\\n\"\n" + ".ascii \" self.wrapped = wrapped\\n\"\n" + ".ascii \" self.__name__ = wrapped.__name__\\n\"\n" + ".ascii \" self.__doc__ = wrapped.__doc__\\n\"\n" + + ".ascii \" def __get__(self, inst, objtype=None):\\n\"\n" + ".ascii \" val = self.wrapped()\\n\"\n" + ".ascii \" if objtype is not None:\\n\"\n" + ".ascii \" setattr(objtype, self.wrapped.__name__, val)\\n\"\n" + ".ascii \" return val\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class storage_ptr:\\n\"\n" + ".ascii \" '''boost::json::storage_ptr pretty printer'''\\n\"\n" + + ".ascii \" @static_property\\n\"\n" + ".ascii \" def _size_t():\\n\"\n" + ".ascii \" return gdb.lookup_type('std::size_t')\\n\"\n" + + ".ascii \" @static_property\\n\"\n" + ".ascii \" def _shared_resource_ptr():\\n\"\n" + ".ascii \" return gdb.lookup_type(\\n\"\n" + ".ascii \" 'boost::json::detail::shared_resource').pointer()\\n\"\n" + + ".ascii \" @static_property\\n\"\n" + ".ascii \" def _memory_resource_ptr():\\n\"\n" + ".ascii \" return gdb.lookup_type(\\n\"\n" + ".ascii \" 'boost::container::pmr::memory_resource').pointer()\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val['i_']\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" items = []\\n\"\n" + + ".ascii \" if bool(self.val & 2):\\n\"\n" + ".ascii \" items.append('trivial')\\n\"\n" + ".ascii \" shared = bool(self.val & 1)\\n\"\n" + ".ascii \" if shared:\\n\"\n" + ".ascii \" items.append('shared')\\n\"\n" + + ".ascii \" pointer = self.val & ~3\\n\"\n" + ".ascii \" if not pointer:\\n\"\n" + ".ascii \" items.append('resource=default')\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" if shared:\\n\"\n" + ".ascii \" resource = pointer.cast(\\n\"\n" + ".ascii \" self._shared_resource_ptr).dereference()\\n\"\n" + ".ascii \" impl_t = resource.dynamic_type\\n\"\n" + ".ascii \" impl_ptr = impl_t.pointer()\\n\"\n" + ".ascii \" resource = resource.address.cast(impl_ptr).dereference()\\n\"\n" + + ".ascii \" items.append(\\n\"\n" + ".ascii \" 'refs=%s' % resource['refs'].cast(self._size_t))\\n\"\n" + + ".ascii \" resource = resource['t']\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" resource = pointer.cast(\\n\"\n" + ".ascii \" self._memory_resource_ptr).dereference()\\n\"\n" + + + ".ascii \" derived_t = resource.dynamic_type\\n\"\n" + ".ascii \" derived_ptr = derived_t.pointer()\\n\"\n" + ".ascii \" resource = resource.address.cast(derived_ptr).dereference()\\n\"\n" + ".ascii \" items.append('resource=%s' % resource)\\n\"\n" + + ".ascii \" return 'storage_ptr [' + ', '.join(items) + ']'\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class monotonic_resource:\\n\"\n" + ".ascii \" '''boost::json::monotonic_resource pretty printer'''\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" buffer = self.val['buffer_']\\n\"\n" + ".ascii \" buffer = buffer['p'] - ( int(buffer['size']) - int(buffer['avail']) )\\n\"\n" + + ".ascii \" head = self.val['head_'].dereference()\\n\"\n" + + ".ascii \" block = head['p'] - ( int(head['size']) - int(head['avail']) )\\n\"\n" + + ".ascii \" upstream = self.val['upstream_']\\n\"\n" + + ".ascii \" items = []\\n\"\n" + ".ascii \" items.append('buffer=%s' % buffer)\\n\"\n" + ".ascii \" items.append('block=%s' % block)\\n\"\n" + ".ascii \" items.append('head=%s' % head['p'])\\n\"\n" + ".ascii \" items.append('free=%s' % head['avail'])\\n\"\n" + ".ascii \" if upstream['i_'] != 0:\\n\"\n" + ".ascii \" items.append('upstream=%s' % upstream)\\n\"\n" + + ".ascii \" return 'monotonic_resource [%s]' % ', '.join(items)\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class static_resource:\\n\"\n" + ".ascii \" '''boost::json::static_resource pretty printer'''\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" buf = self.val['p_'] - ( int(self.val['size_']) - int(self.val['n_']) )\\n\"\n" + ".ascii \" return 'static_resource [buffer=%s, head=%s, free=%s]' % (\\n\"\n" + ".ascii \" buf, self.val['p_'], self.val['n_'])\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class string:\\n\"\n" + ".ascii \" '''boost::json::string pretty printer'''\\n\"\n" + + ".ascii \" @static_property\\n\"\n" + ".ascii \" def _char_const_ptr():\\n\"\n" + ".ascii \" return gdb.lookup_type('char').const().pointer()\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.impl = val['impl_']\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return 'string'\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" kind = self.impl['s_']['k']\\n\"\n" + ".ascii \" if kind == self.impl['short_string_']:\\n\"\n" + ".ascii \" sbo_size = self.impl['sbo_chars_']\\n\"\n" + ".ascii \" size = sbo_size - self.impl['s_']['buf'][sbo_size]\\n\"\n" + ".ascii \" pointer = self.impl['s_']['buf']\\n\"\n" + ".ascii \" elif kind == self.impl['key_string_']:\\n\"\n" + ".ascii \" size = self.impl['k_']['n']\\n\"\n" + ".ascii \" pointer = self.impl['k_']['s']\\n\"\n" + ".ascii \" else:\\n\"\n" + ".ascii \" size = self.impl['p_']['t'].dereference()['size']\\n\"\n" + ".ascii \" pointer = self.impl['p_']['t']\\n\"\n" + ".ascii \" pointer += 1\\n\"\n" + ".ascii \" pointer = pointer.cast(self._char_const_ptr)\\n\"\n" + ".ascii \" return pointer.lazy_string(length=size)\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class array:\\n\"\n" + ".ascii \" '''boost::json::array pretty printer'''\\n\"\n" + + ".ascii \" @static_property\\n\"\n" + ".ascii \" def _value_ptr():\\n\"\n" + ".ascii \" return gdb.lookup_type('boost::json::value').pointer()\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return 'array'\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" capacity = int(self.val['t_'].dereference()['capacity'])\\n\"\n" + ".ascii \" return 'array [size={0}, capacity={1}]'.format(\\n\"\n" + ".ascii \" self.num_children(), capacity)\\n\"\n" + + ".ascii \" def num_children(self):\\n\"\n" + ".ascii \" return int(self.val['t_'].dereference()['size'])\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" for i in range(0, self.num_children()):\\n\"\n" + ".ascii \" yield self.child(i)\\n\"\n" + + ".ascii \" def child(self, n):\\n\"\n" + ".ascii \" table = (self.val['t_'] + 1).cast(self._value_ptr)\\n\"\n" + ".ascii \" return str(n), table[n]\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class key_value_pair:\\n\"\n" + ".ascii \" '''boost::json::key_value_pair pretty printer'''\\n\"\n" + + ".ascii \" @staticmethod\\n\"\n" + ".ascii \" def _pair(kv):\\n\"\n" + ".ascii \" return kv['key_'].lazy_string(length=kv['len_']), kv['value_']\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" k, v = self._pair(self.val)\\n\"\n" + ".ascii \" return '[%s] = %s' % (k.value(), v)\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class object:\\n\"\n" + ".ascii \" '''boost::json::object pretty printer'''\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + ".ascii \" self.kv_ptr = gdb.lookup_type('boost::json::key_value_pair').pointer()\\n\"\n" + + ".ascii \" def display_hint(self):\\n\"\n" + ".ascii \" return 'map'\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" capacity = int(self.val['t_'].dereference()['capacity'])\\n\"\n" + ".ascii \" return 'object [size={}, capacity={}]'.format(\\n\"\n" + ".ascii \" self.num_children(), capacity)\\n\"\n" + + ".ascii \" def num_children(self):\\n\"\n" + ".ascii \" return int(self.val['t_'].dereference()['size'])\\n\"\n" + + ".ascii \" def children(self):\\n\"\n" + ".ascii \" table = (self.val['t_'] + 1).cast(self.kv_ptr)\\n\"\n" + ".ascii \" for i in range(0, self.num_children()):\\n\"\n" + ".ascii \" k, v = key_value_pair._pair(table[i])\\n\"\n" + ".ascii \" yield str(2 * i), k\\n\"\n" + ".ascii \" yield str(2 * i + 1), v\\n\"\n" + + + ".ascii \"@pretty_printer\\n\"\n" + ".ascii \"class value:\\n\"\n" + ".ascii \" '''boost::json::value pretty printer'''\\n\"\n" + + ".ascii \" @static_property\\n\"\n" + ".ascii \" def _kind_t():\\n\"\n" + ".ascii \" result = gdb.lookup_type('boost::json::kind')\\n\"\n" + ".ascii \" return dict(result.items())\\n\"\n" + + ".ascii \" def __init__(self, val):\\n\"\n" + ".ascii \" self.val = val\\n\"\n" + + ".ascii \" def to_string(self):\\n\"\n" + ".ascii \" kind = self.val['sca_']['k']\\n\"\n" + + ".ascii \" if self._compare_kind(kind, 'null'):\\n\"\n" + ".ascii \" return 'null'\\n\"\n" + + ".ascii \" elif self._compare_kind(kind, 'bool_'):\\n\"\n" + ".ascii \" return self.val['sca_']['b']\\n\"\n" + + ".ascii \" elif self._compare_kind(kind, 'int64'):\\n\"\n" + ".ascii \" return self.val['sca_']['i']\\n\"\n" + + ".ascii \" elif self._compare_kind(kind, 'uint64'):\\n\"\n" + ".ascii \" return self.val['sca_']['u']\\n\"\n" + + ".ascii \" elif self._compare_kind(kind, 'double_'):\\n\"\n" + ".ascii \" return self.val['sca_']['d']\\n\"\n" + + ".ascii \" elif self._compare_kind(kind, 'array'):\\n\"\n" + ".ascii \" return self.val['arr_']\\n\"\n" + + ".ascii \" elif self._compare_kind(kind, 'object'):\\n\"\n" + ".ascii \" return self.val['obj_']\\n\"\n" + + ".ascii \" else:\\n\"\n" + ".ascii \" return self.val['str_']\\n\"\n" + + ".ascii \" def _compare_kind(self, kind, name):\\n\"\n" + ".ascii \" return kind == self._kind_t['boost::json::kind::' + name].enumval\\n\"\n" + + + ".ascii \"def register(obj_file):\\n\"\n" + ".ascii \" mod = obj_file or gdb\\n\"\n" + ".ascii \" for printer in getattr(mod, 'pretty_printers', []):\\n\"\n" + ".ascii \" if getattr(printer, 'name') == pretty_printer.result.name:\\n\"\n" + ".ascii \" return\\n\"\n" + + ".ascii \" gdb.printing.register_pretty_printer(\\n\"\n" + ".ascii \" obj_file,\\n\"\n" + ".ascii \" pretty_printer.result)\\n\"\n" + + + ".ascii \"if __name__ == '__main__':\\n\"\n" + ".ascii \" register(gdb.current_objfile())\\n\"\n" + ".byte 0\n" + ".popsection\n"); +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif // defined(__ELF__) + +#endif // BOOST_ALL_NO_EMBEDDED_GDB_SCRIPTS + +#endif // BOOST_JSON_DETAIL_GDB_PRINTERS_HPP diff --git a/src/boost_json_gdb_printers.py b/src/boost_json_gdb_printers.py new file mode 100644 index 000000000..b1a93c989 --- /dev/null +++ b/src/boost_json_gdb_printers.py @@ -0,0 +1,315 @@ +# +# Copyright (c) 2024 Dmitry Arkhipov (grisumbras@yandex.ru) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/boostorg/json +# + +import gdb +import gdb.printing + + +class PrettyPrinterBuilder(): + '''decorator that accumulates pretty printers for types''' + + def __init__(self, name=None): + self.result = gdb.printing.RegexpCollectionPrettyPrinter( + name or 'Boost.JSON') + + def __call__(self, pp=None, ns=None, template=False): + if pp is None: + def decorator(pp): + return self(pp, ns=ns, template=template) + return decorator + + typename = getattr(pp, '__name__') + ns = ns or 'boost::json' + self.result.add_printer( + typename, + '^{ns}::{typename}{marker}'.format( + ns=ns, + typename=typename, + marker='<'if template else '$'), + pp) + return pp + +pretty_printer = PrettyPrinterBuilder() + + +class static_property: + '''decorator for lazy evaluation of static members''' + + def __init__(self, wrapped): + self.wrapped = wrapped + self.__name__ = wrapped.__name__ + self.__doc__ = wrapped.__doc__ + + def __get__(self, inst, objtype=None): + val = self.wrapped() + if objtype is not None: + setattr(objtype, self.wrapped.__name__, val) + return val + + +@pretty_printer +class storage_ptr: + '''boost::json::storage_ptr pretty printer''' + + @static_property + def _size_t(): + return gdb.lookup_type('std::size_t') + + @static_property + def _shared_resource_ptr(): + return gdb.lookup_type( + 'boost::json::detail::shared_resource').pointer() + + @static_property + def _memory_resource_ptr(): + return gdb.lookup_type( + 'boost::container::pmr::memory_resource').pointer() + + def __init__(self, val): + self.val = val['i_'] + + def to_string(self): + items = [] + + if bool(self.val & 2): + items.append('trivial') + shared = bool(self.val & 1) + if shared: + items.append('shared') + + pointer = self.val & ~3 + if not pointer: + items.append('resource=default') + else: + if shared: + resource = pointer.cast( + self._shared_resource_ptr).dereference() + impl_t = resource.dynamic_type + impl_ptr = impl_t.pointer() + resource = resource.address.cast(impl_ptr).dereference() + + items.append( + 'refs=%s' % resource['refs'].cast(self._size_t)) + + resource = resource['t'] + else: + resource = pointer.cast( + self._memory_resource_ptr).dereference() + + + derived_t = resource.dynamic_type + derived_ptr = derived_t.pointer() + resource = resource.address.cast(derived_ptr).dereference() + items.append('resource=%s' % resource) + + return 'storage_ptr [' + ', '.join(items) + ']' + + +@pretty_printer +class monotonic_resource: + '''boost::json::monotonic_resource pretty printer''' + + def __init__(self, val): + self.val = val + + def to_string(self): + buffer = self.val['buffer_'] + buffer = buffer['p'] - ( int(buffer['size']) - int(buffer['avail']) ) + + head = self.val['head_'].dereference() + + block = head['p'] - ( int(head['size']) - int(head['avail']) ) + + upstream = self.val['upstream_'] + + items = [] + items.append('buffer=%s' % buffer) + items.append('block=%s' % block) + items.append('head=%s' % head['p']) + items.append('free=%s' % head['avail']) + if upstream['i_'] != 0: + items.append('upstream=%s' % upstream) + + return 'monotonic_resource [%s]' % ', '.join(items) + + +@pretty_printer +class static_resource: + '''boost::json::static_resource pretty printer''' + + def __init__(self, val): + self.val = val + + def to_string(self): + buf = self.val['p_'] - ( int(self.val['size_']) - int(self.val['n_']) ) + return 'static_resource [buffer=%s, head=%s, free=%s]' % ( + buf, self.val['p_'], self.val['n_']) + + +@pretty_printer +class string: + '''boost::json::string pretty printer''' + + @static_property + def _char_const_ptr(): + return gdb.lookup_type('char').const().pointer() + + def __init__(self, val): + self.impl = val['impl_'] + + def display_hint(self): + return 'string' + + def to_string(self): + kind = self.impl['s_']['k'] + if kind == self.impl['short_string_']: + sbo_size = self.impl['sbo_chars_'] + size = sbo_size - self.impl['s_']['buf'][sbo_size] + pointer = self.impl['s_']['buf'] + elif kind == self.impl['key_string_']: + size = self.impl['k_']['n'] + pointer = self.impl['k_']['s'] + else: + size = self.impl['p_']['t'].dereference()['size'] + pointer = self.impl['p_']['t'] + pointer += 1 + pointer = pointer.cast(self._char_const_ptr) + return pointer.lazy_string(length=size) + + +@pretty_printer +class array: + '''boost::json::array pretty printer''' + + @static_property + def _value_ptr(): + return gdb.lookup_type('boost::json::value').pointer() + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'array' + + def to_string(self): + capacity = int(self.val['t_'].dereference()['capacity']) + return 'array [size={0}, capacity={1}]'.format( + self.num_children(), capacity) + + def num_children(self): + return int(self.val['t_'].dereference()['size']) + + def children(self): + for i in range(0, self.num_children()): + yield self.child(i) + + def child(self, n): + table = (self.val['t_'] + 1).cast(self._value_ptr) + return str(n), table[n] + + +@pretty_printer +class key_value_pair: + '''boost::json::key_value_pair pretty printer''' + + @staticmethod + def _pair(kv): + return kv['key_'].lazy_string(length=kv['len_']), kv['value_'] + + def __init__(self, val): + self.val = val + + def to_string(self): + k, v = self._pair(self.val) + return '[%s] = %s' % (k.value(), v) + + +@pretty_printer +class object: + '''boost::json::object pretty printer''' + + def __init__(self, val): + self.val = val + self.kv_ptr = gdb.lookup_type('boost::json::key_value_pair').pointer() + + def display_hint(self): + return 'map' + + def to_string(self): + capacity = int(self.val['t_'].dereference()['capacity']) + return 'object [size={}, capacity={}]'.format( + self.num_children(), capacity) + + def num_children(self): + return int(self.val['t_'].dereference()['size']) + + def children(self): + table = (self.val['t_'] + 1).cast(self.kv_ptr) + for i in range(0, self.num_children()): + k, v = key_value_pair._pair(table[i]) + yield str(2 * i), k + yield str(2 * i + 1), v + + +@pretty_printer +class value: + '''boost::json::value pretty printer''' + + @static_property + def _kind_t(): + result = gdb.lookup_type('boost::json::kind') + return dict(result.items()) + + def __init__(self, val): + self.val = val + + def to_string(self): + kind = self.val['sca_']['k'] + + if self._compare_kind(kind, 'null'): + return 'null' + + elif self._compare_kind(kind, 'bool_'): + return self.val['sca_']['b'] + + elif self._compare_kind(kind, 'int64'): + return self.val['sca_']['i'] + + elif self._compare_kind(kind, 'uint64'): + return self.val['sca_']['u'] + + elif self._compare_kind(kind, 'double_'): + return self.val['sca_']['d'] + + elif self._compare_kind(kind, 'array'): + return self.val['arr_'] + + elif self._compare_kind(kind, 'object'): + return self.val['obj_'] + + else: + return self.val['str_'] + + def _compare_kind(self, kind, name): + return kind == self._kind_t['boost::json::kind::' + name].enumval + + +def register(obj_file): + mod = obj_file or gdb + for printer in getattr(mod, 'pretty_printers', []): + if getattr(printer, 'name') == pretty_printer.result.name: + return + + gdb.printing.register_pretty_printer( + obj_file, + pretty_printer.result) + + +if __name__ == '__main__': + register(gdb.current_objfile()) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0287fd6b6..68becd75a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,6 +26,7 @@ list(FILTER BOOST_JSON_TESTS_FILES EXCLUDE REGEX cmake-subdir/.*$) list(FILTER BOOST_JSON_TESTS_FILES EXCLUDE REGEX intrusive_macros\.cpp$) list(FILTER BOOST_JSON_TESTS_FILES EXCLUDE REGEX limits\.cpp$) list(FILTER BOOST_JSON_TESTS_FILES EXCLUDE REGEX no_exceptions\.cpp$) +list(FILTER BOOST_JSON_TESTS_FILES EXCLUDE REGEX printers\.cpp$) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${BOOST_JSON_TESTS_FILES}) add_executable(boost_json-tests ${EXCLUDE_TESTS_FROM_ALL} ${BOOST_JSON_TESTS_FILES}) @@ -75,3 +76,13 @@ target_compile_definitions(boost_json-intrusive-macro-tests PRIVATE ) add_test(NAME boost_json-intrusive-macro-tests COMMAND boost_json-intrusive-macro-tests) add_dependencies(tests boost_json-intrusive-macro-tests) + +if(BoostPrettyPrinters_HAS_GDB) + boost_pretty_printers_test_gdb_printers( + TEST boost_json_test_gdb_printers + SOURCES printers.cpp + ${EXCLUDE_TESTS_FROM_ALL}) + boost_json_setup_properties(boost_json_test_gdb_printers) + target_link_libraries(boost_json_test_gdb_printers PRIVATE Boost::json) + add_dependencies(tests boost_json_test_gdb_printers) +endif() diff --git a/test/Jamfile b/test/Jamfile index aec42a3e6..b9bf751ed 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -7,6 +7,7 @@ # Official repository: https://github.com/boostorg/json # +import boost-pretty-printers ; import testing ; local SOURCES = @@ -90,3 +91,10 @@ run no_exceptions.cpp main.cpp /boost/json//json_sources ; run intrusive_macros.cpp main.cpp /boost/json//json_deps ; + +boost-pretty-printers.test-gdb-printers gdb-printers + : printers.cpp + /boost/json//boost_json + : ../src/boost_json_gdb_printers.py + ; +explicit gdb-printers ; diff --git a/test/printers.cpp b/test/printers.cpp new file mode 100644 index 000000000..a2f560cd0 --- /dev/null +++ b/test/printers.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + + +using namespace boost::json; + + +int main() +{ + value jv; + // TEST_EXPR( 'jv', 'null' ) + + jv = true; + // TEST_EXPR( 'jv', 'true' ) + + jv = false; + // TEST_EXPR( 'jv', 'false' ) + + jv = 1; + // TEST_EXPR( 'jv', '1' ) + + jv = 1u; + // TEST_EXPR( 'jv', '1' ) + + jv = 1.5; + // TEST_EXPR( 'jv', '1.5' ) + + string js; + // TEST_EXPR( 'js', '""' ) + + js = "1"; + // TEST_EXPR( 'js', '"1"' ) + + js = "this is a very long string, unusually long even, definitely not short"; + // TEST_EXPR( 'js', '"this is a very long string, unusually long even, definitely not short"' ) + + array ja; + // TEST_EXPR( 'ja', 'array [size=0, capacity=0]' ) + + ja.push_back("a"); + // TEST_EXPR( 'ja', 'array [size=1, capacity=1] = {"a"}' ) + + ja.push_back(true); + // TEST_EXPR( 'ja', 'array [size=2, capacity=2] = {"a", true}' ) + + ja.insert(ja.end(), {1, 2, 3, 4}); + // TEST_EXPR( 'ja', 'array [size=6, capacity=6] = {"a", true, 1, 2, 3, 4}' ) + + ja.push_back(5); + // TEST_EXPR( 'ja', 'array [size=7, capacity=9] = {"a", true, 1, 2, 3, 4, 5}' ) + + ja[ja.size() - 1] = array{1,2,3}; + // TEST_EXPR( 'ja', 'array [size=7, capacity=9] = {"a", true, 1, 2, 3, 4, array [size=3, capacity=3] = {1, 2, 3}}' ) + + object jo; + // TEST_EXPR( 'jo', 'object [size=0, capacity=0]' ) + + jo["a"] = "b"; + // TEST_EXPR( 'jo', 'object [size=1, capacity=1] = {["a"] = "b"}' ) + + jo["b"] = "c"; + // TEST_EXPR( 'jo', 'object [size=2, capacity=2] = {["a"] = "b", ["b"] = "c"}' ) + + jo.insert({ {"c", "d"}, {"d", "e"} }); + // TEST_EXPR( 'jo', 'object [size=4, capacity=4] = {["a"] = "b", ["b"] = "c", ["c"] = "d", ["d"] = "e"}' ) + + jo["e"] = "f"; + // TEST_EXPR( 'jo', 'object [size=5, capacity=6] = {["a"] = "b", ["b"] = "c", ["c"] = "d", ["d"] = "e", ["e"] = "f"}' ) + + key_value_pair kv = *jo.begin(); + (void)kv; + // TEST_EXPR( 'kv', '["a"] = "b"' ) + + storage_ptr sp = jv.storage(); + // TEST_EXPR( 'sp', 'storage_ptr [resource=default]' ) + + unsigned char buf[1024]; + { + static_resource sr(buf); + // TEST_EXPR( 'sr', 'static_resource [buffer={0}, head={0}, free=1024]', '/a &buf' ) + + sr.allocate(200); + unsigned char* new_head = buf + 200; + (void)new_head; + // TEST_EXPR( 'sr', 'static_resource [buffer={0}, head={1}, free=824]', '/a &buf', '/a new_head' ) + + sp = &sr; + // TEST_EXPR( 'sp', 'storage_ptr [trivial, resource=static_resource [buffer={0}, head={1}, free=824]]', '/a &buf', '/a new_head' ) + + sr.release(); + } + + sp = make_shared_resource(buf); + // TEST_EXPR( 'sp', 'storage_ptr [trivial, shared, refs=1, resource=static_resource [buffer={0}, head={0}, free=1024]]', '/a &buf' ) + { + auto sp2 = sp; + // TEST_EXPR( 'sp', 'storage_ptr [trivial, shared, refs=2, resource=static_resource [buffer={0}, head={0}, free=1024]]', '/a &buf' ) + (void)sp; + } + + { + monotonic_resource mr; + // TEST_EXPR( 'mr', 'monotonic_resource [buffer=0x0, block=0x0, head=0x0, free=0]' ) + + storage_ptr sp2 = &mr; + // TEST_EXPR( 'sp2', 'storage_ptr [trivial, resource=monotonic_resource [buffer=0x0, block=0x0, head=0x0, free=0]]' ) + (void)sp2; + } + + monotonic_resource mr(buf, 10, sp); + // TEST_EXPR( 'mr', 'monotonic_resource [buffer={0}, block={0}, head={0}, free=10, upstream=storage_ptr [trivial, shared, refs=2, resource=static_resource [buffer={0}, head={0}, free=1024]]]', '/a &buf' ) + + mr.allocate(4); + unsigned char* new_head = buf + 4; + (void)new_head; + // TEST_EXPR( 'mr', 'monotonic_resource [buffer={0}, block={0}, head={1}, free=6, upstream=storage_ptr [trivial, shared, refs=2, resource=static_resource [buffer={0}, head={0}, free=1024]]]', '/a &buf', '/a new_head' ) + + return EXIT_SUCCESS; +}