blob: 498db90b13ee8f76b73cb175189e0a6946d3c6f9 [file] [log] [blame]
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""GDB pretty-printers for common classes in ipcz:: namespace."""
import gdb
import gdb.printing
import os
sys.path.insert(
1, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'util'))
import reload_helper
class RefPrinter:
"""Pretty-printer for ipcz::Ref<T>."""
def __init__(self, val):
self.val = val
def to_string(self):
raw_ptr = self.val['ptr_']
if not raw_ptr or raw_ptr == 0:
return 'nullptr'
try:
pointer_type = raw_ptr.dynamic_type
casted_ptr = raw_ptr.cast(pointer_type)
return f'Ref to {casted_ptr.dereference()}'
except gdb.error:
return str(raw_ptr)
class RouteEdgePrinter:
"""Pretty-printer for ipcz::RouteEdge."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'RouteEdge'
def children(self):
yield 'primary_link_', self.val['primary_link_']
yield 'decaying_link_', self.val['decaying_link_']
class LocalRouterLinkPrinter:
"""Pretty-printer for ipcz::LocalRouterLink."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'LocalRouterLink'
def children(self):
# The state_ member is a Ref<SharedState>. The members below are on the
# SharedState object.
try:
state = self.val['state_']['ptr_']
if not state or state == 0:
yield 'state_', 'nullptr'
return
# Deliberately not dereferencing the Ref to routers to avoid
# common infinite recursion.
yield 'router_a_', state['router_a_']['ptr_']
yield 'router_b_', state['router_b_']['ptr_']
yield 'link_state_', state['link_state_']
except gdb.error:
yield 'state_', '<error reading state_>'
class RouterLinkStatePrinter:
"""Pretty-printer for ipcz::RouterLinkState."""
def __init__(self, val):
self.val = val
def to_string(self):
try:
status = int(self.val['status'])
side_a_parts = []
if status & 1:
side_a_parts.append('stable')
else:
side_a_parts.append('unstable')
if status & 4:
side_a_parts.append('waiting')
side_b_parts = []
if status & 2:
side_b_parts.append('stable')
else:
side_b_parts.append('unstable')
if status & 8:
side_b_parts.append('waiting')
lock_str = ''
if status & 16:
lock_str = ', locked by A'
elif status & 32:
lock_str = ', locked by B'
return (f'RouterLinkState {{A: {', '.join(side_a_parts)}; '
f'B: {', '.join(side_b_parts)}{lock_str}}}')
except gdb.error:
return 'RouterLinkState'
def children(self):
yield 'status', self.val['status']
yield 'allowed_bypass_request_source', self.val[
'allowed_bypass_request_source']
class RemoteRouterLinkPrinter:
"""Pretty-printer for ipcz::RemoteRouterLink."""
def __init__(self, val):
self.val = val
def to_string(self):
link_type = str(self.val['type_']['value_'])
link_type_str = 'unknown link type'
if link_type.endswith('kCentral'):
link_type_str = 'central'
elif link_type.endswith('kPeripheralInward'):
link_type_str = 'peripheral inward'
elif link_type.endswith('kPeripheralOutward'):
link_type_str = 'peripheral outward'
elif link_type.endswith('kBridge'):
link_type_str = 'bridge'
side_str = '.B'
if str(self.val['side_']['value_']).endswith('kA'):
side_str = '.A'
# Convert to str to use the default printer because the gdb.Value for
# std::atomic<bool> does not convert to python bool correctly.
stable_str = str(self.val['side_is_stable_'])
stable_msg = 'not stable'
if str(stable_str) == 'true':
stable_msg = 'stable side'
sublink_value = self.val['sublink_']['value_']
return (f'RemoteRouterLink with sublink {sublink_value}' +
f'{side_str} ({stable_msg}, {link_type_str})')
class RouterPrinter:
"""Pretty-printer for ipcz::Router."""
def __init__(self, val):
self.val = val
def to_string(self):
return f'Router {self.val.address}'
def children(self):
status_str = None
try:
status_int = int(self.val['status_flags_'])
statuses = []
if (status_int & 1):
statuses.append('closed')
if (status_int & 2):
statuses.append('dead')
if not statuses:
status_str = 'normal'
else:
status_str = ', '.join(statuses)
except gdb.error:
status_str = 'unknown'
yield 'status_flags_', status_str
# Reorder the remaining members according to subjective importance.
members = [
'outward_edge_',
'inward_edge_',
'is_peer_closed_',
'is_disconnected_',
'bridge_',
'inbound_parcels_',
'outbound_parcels_',
'traps_',
'pending_gets_',
'pending_puts_',
'is_pending_get_exclusive_',
]
for m in members:
yield m, self.val[m]
class ParcelQueuePrinter:
"""Pretty-printer for ipcz::ParcelQueue."""
def __init__(self, val):
self.val = val
# TODO(pasko): Implement it. Add children for is_final_length_known_.
def to_string(self):
# Make it more compact when empty.
try:
entries_vec = self.val['entries_']
begin_ptr = entries_vec['__begin_']
end_ptr = entries_vec['__end_']
if begin_ptr == end_ptr:
return 'ParcelQueue is empty'
except gdb.error:
pass
return str(self.val.type)
class NodeLinkPrinter:
"""Pretty-printer for ipcz::NodeLink."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'NodeLink'
def children(self):
yield 'link_side_', str(self.val['link_side_']['value_'])
try:
node_ptr = self.val['node_']['ptr_']
node_name = str(node_ptr['assigned_name_'])
yield 'node_', f'Ref to (ipcz::Node *) {node_ptr} with {node_name}'
except:
yield 'node_', 'unknown'
important_members = [
'remote_node_type_',
'activation_state_',
'sublinks_',
'partial_parcels_',
'early_parcels_for_sublink_',
'next_referral_id_',
'local_node_name_',
'remote_node_name_',
]
for m in important_members:
yield m, self.val[m]
yield 'note', 'Hiding many members in this pretty-printer'
class NodeNamePrinter:
"""Pretty-printer for ipcz::NodeName."""
def __init__(self, val):
self.val = val
def to_string(self):
try:
high = int(self.val['high_'])
low = int(self.val['low_'])
return f'NodeName {high:016x}{low:016x}'
except gdb.error:
return '<unreadable NodeName>'
class NodePrinter:
"""Pretty-printer for ipcz::Node."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'Node'
def children(self):
important_members = [
'type_',
'assigned_name_',
'connections_',
'pending_introductions_',
]
for m in important_members:
yield m, self.val[m]
yield 'note', 'Hiding many members in this pretty-printer'
class DriverTransportPrinter:
"""Pretty-printer for ipcz::DriverTransport."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'DriverTransport'
class ParcelPrinter:
"""Pretty-printer for ipcz::Parcel."""
def __init__(self, val):
self.val = val
def to_string(self):
try:
data_view = self.val['data_']['view']
data_size = data_view['size_']
objects_view = self.val['objects_']['view']
num_objects = objects_view['size_']
return f'Parcel with {data_size} bytes and {num_objects} objects'
except gdb.error:
return 'Parcel'
def children(self):
yield 'sequence_number_', self.val['sequence_number_']
yield 'objects_', self.val['objects_']
yield 'num_subparcels_', self.val['num_subparcels_']
yield 'subparcel_index_', self.val['subparcel_index_']
yield 'remote_source_', self.val['remote_source_']
class BoxPrinter:
"""Pretty-printer for ipcz::Box."""
def __init__(self, val):
self.val = val
def to_string(self):
try:
contents = self.val['contents_']
index = contents.index
if index == 0:
return 'Box (empty)'
if index == 1:
o = contents['driver_object']
return f'Box (DriverObject: {o})'
if index == 2:
o = contents['application_object']
return f'Box (ApplicationObject: {o})'
if index == 3:
o = contents['subparcel']
return f'Box (Subparcel: {o})'
except gdb.error:
return 'Box'
class NodeConnectorForBrokerToNonBrokerPrinter:
"""Pretty-printer for ipcz::NodeConnectorForBrokerToNonBroker."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'NodeConnectorForBrokerToNonBroker'
def children(self):
yield 'broker_name_', self.val['broker_name_']
yield 'new_remote_node_name_', self.val['new_remote_node_name_']
yield 'waiting_routers_', self.val['waiting_routers_']
def _decode_trap_condition_flags(flags_val):
flags = int(flags_val)
flag_names = []
if flags & 1:
flag_names.append('DEAD')
if flags & 2:
flag_names.append('NEW_PARCEL_COUNT')
if flags & 4:
flag_names.append('NEW_PARCEL_BYTES')
if not flag_names:
return 'NONE'
return ' | '.join(flag_names)
class TrapConditionsPrinter:
"""Pretty-printer for IpczTrapConditions."""
def __init__(self, val):
self.val = val
def to_string(self):
try:
flags = _decode_trap_condition_flags(self.val['flags'])
threshold = self.val['threshold']
return f'{{flags={flags}, threshold={threshold}}}'
except gdb.error:
return 'IpczTrapConditions'
class TrapPrinter:
"""Pretty-printer for ipcz::TrapSet::Trap."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'Trap'
def children(self):
yield 'conditions', self.val['conditions']
yield 'handler', self.val['handler']
yield 'context', self.val['context']
class TrapSetPrinter:
"""Pretty-printer for ipcz::TrapSet."""
def __init__(self, val):
self.val = val
def to_string(self):
try:
traps_vec = self.val['traps_']
begin_ptr = traps_vec['__begin_']
end_ptr = traps_vec['__end_']
size = end_ptr - begin_ptr
if size == 1:
return 'TrapSet with 1 trap'
return f'TrapSet with {size} traps'
except gdb.error:
return 'TrapSet'
def children(self):
yield 'traps_', self.val['traps_']
class SublinkIdPrinter:
"""Pretty-printer for ipcz::SublinkId.
The real type is ipcz::StrongAlias<class SublinkIdTag, uint64_t>.
"""
def __init__(self, val):
self.val = val
def to_string(self):
try:
kLinkSideBIdBit = 63
kIdMask = (1 << kLinkSideBIdBit) - 1
value = int(self.val['value_'])
numeric_part = value & kIdMask
suffix = '.B' if (value >> kLinkSideBIdBit) else '.A'
return f'SublinkId {numeric_part}{suffix}'
except gdb.error:
return '<unreadable SublinkId>'
class SublinkPrinter:
"""Pretty-printer for ipcz::NodeLink::Sublink."""
def __init__(self, val):
self.val = val
def to_string(self):
return 'Sublink'
def children(self):
yield 'router_link', self.val['router_link']
receiver_ptr = self.val['receiver']['ptr_']
yield 'receiver', f'Ref to (ipcz::Router *) {receiver_ptr}'
ipcz_printer = reload_helper.find_or_create_printer('ipcz')
def _add(subprinter_name, regex, printer):
reload_helper.remove_printer(ipcz_printer, subprinter_name)
ipcz_printer.add_printer(subprinter_name, regex, printer)
_add('Box', '^ipcz::Box$', BoxPrinter)
_add('DriverTransport', '^ipcz::DriverTransport$', DriverTransportPrinter)
_add('IpczRef', '^ipcz::Ref<.*>$', RefPrinter)
_add('LocalRouterLink', '^ipcz::LocalRouterLink$', LocalRouterLinkPrinter)
_add('NodeConnectorForBrokerToNonBroker',
'^ipcz::.*NodeConnectorForBrokerToNonBroker$',
NodeConnectorForBrokerToNonBrokerPrinter)
_add('Node', '^ipcz::Node$', NodePrinter)
_add('NodeLink', '^ipcz::NodeLink$', NodeLinkPrinter)
_add('NodeName', '^ipcz::NodeName$', NodeNamePrinter)
_add('Parcel', '^ipcz::Parcel$', ParcelPrinter)
_add('ParcelQueue', '^ipcz::SequencedQueue<.*ipcz::ParcelQueueTraits>$',
ParcelQueuePrinter)
_add('RemoteRouterLink', '^ipcz::RemoteRouterLink$', RemoteRouterLinkPrinter)
_add('RouteEdge', '^ipcz::RouteEdge$', RouteEdgePrinter)
_add('Router', '^ipcz::Router$', RouterPrinter)
_add('RouterLinkState', '^ipcz::RouterLinkState$', RouterLinkStatePrinter)
_add('SublinkId', '^ipcz::StrongAlias<ipcz::SublinkIdTag', SublinkIdPrinter)
_add('Sublink', '^ipcz::NodeLink::Sublink$', SublinkPrinter)
_add('TrapConditions', '^IpczTrapConditions$', TrapConditionsPrinter)
_add('Trap', '^ipcz::TrapSet::Trap$', TrapPrinter)
_add('TrapSet', '^ipcz::TrapSet$', TrapSetPrinter)