910e62b5创建于 1月15日历史提交
# 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)