mirror of
https://github.com/boostorg/container.git
synced 2026-07-05 10:00:47 +02:00
Add hub pretty printers
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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)
|
||||
-->
|
||||
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
|
||||
<!--
|
||||
Fancy pointer support: fancy pointer's Natvis Type element is assumed to provide
|
||||
an intrinsic boost_to_address() returning an equivalent raw pointer.
|
||||
-->
|
||||
|
||||
<Type Name="boost::container::hub<*>" Inheritable="false">
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="block_base_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="block_base_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="check_bit" Expression="(n & ((unsigned long long)1 << i)) != 0">
|
||||
<Parameter Name="n" Type="unsigned long long" />
|
||||
<Parameter Name="i" Type="int" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="countr_zero" Expression="
|
||||
check_bit(n, 0) ? 0 : check_bit(n, 1) ? 1 : check_bit(n, 2) ? 2 : check_bit(n, 3) ? 3 :
|
||||
check_bit(n, 4) ? 4 : check_bit(n, 5) ? 5 : check_bit(n, 6) ? 6 : check_bit(n, 7) ? 7 :
|
||||
check_bit(n, 8) ? 8 : check_bit(n, 9) ? 9 : check_bit(n, 10) ? 10 : check_bit(n, 11) ? 11 :
|
||||
check_bit(n, 12) ? 12 : check_bit(n, 13) ? 13 : check_bit(n, 14) ? 14 : check_bit(n, 15) ? 15 :
|
||||
check_bit(n, 16) ? 16 : check_bit(n, 17) ? 17 : check_bit(n, 18) ? 18 : check_bit(n, 19) ? 19 :
|
||||
check_bit(n, 20) ? 20 : check_bit(n, 21) ? 21 : check_bit(n, 22) ? 22 : check_bit(n, 23) ? 23 :
|
||||
check_bit(n, 24) ? 24 : check_bit(n, 25) ? 25 : check_bit(n, 26) ? 26 : check_bit(n, 27) ? 27 :
|
||||
check_bit(n, 28) ? 28 : check_bit(n, 29) ? 29 : check_bit(n, 30) ? 30 : check_bit(n, 31) ? 31 :
|
||||
check_bit(n, 32) ? 32 : check_bit(n, 33) ? 33 : check_bit(n, 34) ? 34 : check_bit(n, 35) ? 35 :
|
||||
check_bit(n, 36) ? 36 : check_bit(n, 37) ? 37 : check_bit(n, 38) ? 38 : check_bit(n, 39) ? 39 :
|
||||
check_bit(n, 40) ? 40 : check_bit(n, 41) ? 41 : check_bit(n, 42) ? 42 : check_bit(n, 43) ? 43 :
|
||||
check_bit(n, 44) ? 44 : check_bit(n, 45) ? 45 : check_bit(n, 46) ? 46 : check_bit(n, 47) ? 47 :
|
||||
check_bit(n, 48) ? 48 : check_bit(n, 49) ? 49 : check_bit(n, 50) ? 50 : check_bit(n, 51) ? 51 :
|
||||
check_bit(n, 52) ? 52 : check_bit(n, 53) ? 53 : check_bit(n, 54) ? 54 : check_bit(n, 55) ? 55 :
|
||||
check_bit(n, 56) ? 56 : check_bit(n, 57) ? 57 : check_bit(n, 58) ? 58 : check_bit(n, 59) ? 59 :
|
||||
check_bit(n, 60) ? 60 : check_bit(n, 61) ? 61 : check_bit(n, 62) ? 62 : check_bit(n, 63) ? 63 : 64">
|
||||
<Parameter Name="n" Type="unsigned long long" />
|
||||
</Intrinsic>
|
||||
|
||||
<DisplayString>{{ size={size_} }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]">
|
||||
num_blocks * N
|
||||
</Item>
|
||||
<Item Name="[allocator]">
|
||||
*reinterpret_cast<allocator_type*>(static_cast<allocator_base*>(this))
|
||||
</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pbb" InitialValue="to_address(&blist.next)" />
|
||||
<Variable Name="mask" InitialValue="pbb->mask" />
|
||||
<Variable Name="i" InitialValue="(size_t)0" />
|
||||
<Variable Name="n" InitialValue="(int)0" />
|
||||
<Loop Condition="i < size_">
|
||||
<Exec>n = countr_zero(mask)</Exec>
|
||||
<Exec>mask &= (mask - 1)</Exec>
|
||||
<Item>*(to_address(&static_cast<block*>(pbb)->data_) + n)</Item>
|
||||
<If Condition="!mask">
|
||||
<Exec>pbb = to_address(&pbb->next)</Exec>
|
||||
<Exec>mask = pbb->mask</Exec>
|
||||
</If>
|
||||
<Exec>i += 1</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::container::hub_detail::iterator<*>" Inheritable="false">
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="nonconst_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="nonconst_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="block_base_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="block_base_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="operator_arrow" Expression="to_address(&(static_cast<block*>(to_address(&pbb)))->data_) + n" />
|
||||
<Intrinsic Name="dereferenceable" Expression="operator_arrow() != nullptr" />
|
||||
|
||||
<DisplayString Condition="dereferenceable()">{*operator_arrow()}</DisplayString>
|
||||
<DisplayString Condition="!dereferenceable()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="dereferenceable()">*operator_arrow()</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
</AutoVisualizer>
|
||||
@@ -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())
|
||||
Reference in New Issue
Block a user