From 8c3210b0f2dcfafca672558f67ecb1af7d24edd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Fri, 5 Jun 2026 23:50:40 +0200 Subject: [PATCH] Add hub pretty printers --- extra/boost_hub.natvis | 102 ++++++++++++++++++++++ extra/boost_hub_printers.py | 164 ++++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 extra/boost_hub.natvis create mode 100644 extra/boost_hub_printers.py diff --git a/extra/boost_hub.natvis b/extra/boost_hub.natvis new file mode 100644 index 0000000..de15411 --- /dev/null +++ b/extra/boost_hub.natvis @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ size={size_} }} + + + num_blocks * N + + + *reinterpret_cast<allocator_type*>(static_cast<allocator_base*>(this)) + + + + + + + + n = countr_zero(mask) + mask &= (mask - 1) + *(to_address(&static_cast<block*>(pbb)->data_) + n) + + pbb = to_address(&pbb->next) + mask = pbb->mask + + i += 1 + + + + + + + + + + + + + + + + + + + + + + {*operator_arrow()} + {{ end iterator }} + + *operator_arrow() + + + + diff --git a/extra/boost_hub_printers.py b/extra/boost_hub_printers.py new file mode 100644 index 0000000..dd5d1a0 --- /dev/null +++ b/extra/boost_hub_printers.py @@ -0,0 +1,164 @@ +# Copyright 2026 Joaquin M Lopez Munoz. +# 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) + +import gdb.printing +import gdb.xmethod + +class BoostContainerHubHelpers: + def countr_zero(n): + for i in range(64): + if (n & (1 << i)) != 0: return i + return 64 + + def popcount(n): + c = 0 + while n: + c += 1 + n &= n - 1 + return c + + # Fancy pointer support: fancy pointer's visualizer is assumed to provide + # a static boost_to_address(fancy_ptr) method returning a gdb.Value with the + # equivalent raw pointer. + + def to_address(ptr): + if ptr.type.strip_typedefs().code == gdb.TYPE_CODE_PTR: # strip_typedefs() needed for some unknown reason + return ptr + try: + return type(gdb.default_visualizer(ptr)).boost_to_address(ptr) + except AttributeError: + raise gdb.error( + f"{ptr.type.strip_typedefs().name}'s visualizer must have a static boost_to_address(ptr) method " + "for proper visualization") + +class BoostContainerHubProxy: + def __init__(self, val): + self.N = 64 + self.blist = val["blist"] + self.num_blocks = int(val["num_blocks"]) + self.size_ = int(val["size_"]) + # block_list derives from block + self.block_type = next(f for f in self.blist.type.fields() if f.is_base_class).type + + def __iter__(self): + pbb = BoostContainerHubHelpers.to_address(self.blist["next"]) + mask = int(pbb.dereference()["mask"]) + count = 0 + while count != self.size_: + n = BoostContainerHubHelpers.countr_zero(mask) + pb = pbb.cast(self.block_type.pointer()) + pd = BoostContainerHubHelpers.to_address(pb.dereference()["data_"]) + yield (pd + n).dereference() + mask &= mask - 1 + if mask == 0: + pbb = BoostContainerHubHelpers.to_address(pbb.dereference()["next"]) + mask = int(pbb.dereference()["mask"]) + count += 1 + + def __getitem__(self, n): + if self.size_ == 0: + raise IndexError("Container is empty") + elif n < 0 or n >= self.size_: + raise IndexError("Out of bounds") + pbb = BoostContainerHubHelpers.to_address(self.blist["next"]) + while True: + m = BoostContainerHubHelpers.popcount(int(pbb.dereference()["mask"])) + if m <= n: + n -= m + pbb = BoostContainerHubHelpers.to_address(pbb.dereference()["next"]) + else: + break + mask = int(pbb.dereference()["mask"]) + pb = pbb.cast(self.block_type.pointer()) + pd = BoostContainerHubHelpers.to_address(pb.dereference()["data_"]) + while n: + mask &= mask - 1 + n -= 1 + pb = pbb.cast(self.block_type.pointer()) + pd = BoostContainerHubHelpers.to_address(pb.dereference()["data_"]) + return (pd + BoostContainerHubHelpers.countr_zero(mask)).dereference() + +class BoostContainerHubPrinter: + def __init__(self, val): + self.hub = BoostContainerHubProxy(val) + + def to_string(self): + return f"boost::container::hub with {{size = {self.hub.size_}, capacity = {self.hub.num_blocks * self.hub.N}}}" + + def display_hint(self): + return "array" + + def children(self): + for count, v in enumerate(self.hub): + yield f"[{count}]", v + +class BoostContainerHubIteratorPrinter: + def __init__(self, val): + # TODO: ::block typedef may have been stripped away by compiler + block_type = gdb.lookup_type(f"{val.type.strip_typedefs().name}::block") + self.pb = BoostContainerHubHelpers.to_address(val["pbb"]).cast(block_type.pointer()) + if self.pb != 0: + self.px = BoostContainerHubHelpers.to_address(self.pb.dereference()["data_"]) + int(val["n"]) + + def to_string(self): + if self.pb == 0: + return "iterator = { invalid }" + elif self.px == 0: + return "iterator = { end iterator }" + else: + return f"iterator = {{ {self.px.dereference()} }}" + +def boost_container_hub_build_pretty_printers(): + pp = gdb.printing.RegexpCollectionPrettyPrinter("boost_container_hub") + add_template_printer = lambda name, printer: pp.add_printer(name, f"^{name}<.*>$", printer) + + add_template_printer("boost::container::hub", BoostContainerHubPrinter) + add_template_printer("boost::container::hub_detail::iterator", BoostContainerHubIteratorPrinter) + + return pp + +gdb.printing.register_pretty_printer(gdb.current_objfile(), boost_container_hub_build_pretty_printers()) + +# https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-an-Xmethod.html + +class BoostContainerHubSubscriptMethod(gdb.xmethod.XMethod): + def __init__(self): + gdb.xmethod.XMethod.__init__(self, "subscript") + + def get_worker(self, class_type, method_name): + if method_name == "operator[]": + return BoostContainerHubSubscriptWorker(class_type.template_argument(0)) + +class BoostContainerHubSubscriptWorker(gdb.xmethod.XMethodWorker): + def __init__(self, value_type): + self.value_type = value_type + + def get_arg_types(self): + return [gdb.lookup_type("std::size_t")] + + def get_result_type(self, obj): + return self.value_type.reference() + + def __call__(self, obj, index): + return BoostContainerHubProxy(obj)[index] + +class BoostContainerHubMatcher(gdb.xmethod.XMethodMatcher): + def __init__(self): + gdb.xmethod.XMethodMatcher.__init__(self, "BoostContainerHubMatcher") + self.methods = [BoostContainerHubSubscriptMethod()] + + def match(self, class_type, method_name): + if not class_type.tag.startswith("boost::container::hub<"): + return None + + workers = [] + for method in self.methods: + if method.enabled: + worker = method.get_worker(class_type, method_name) + if worker: + workers.append(worker) + return workers + +gdb.xmethod.register_xmethod_matcher(None, BoostContainerHubMatcher())