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 absl::{flat,node}_hash_{map,set}."""

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


# Returns the type of each base class field of type t.
def _base_classes(t):
  return [field.type for field in t.fields() if field.is_base_class]


class SwissTablePrinter(object):
  short_type_name = None
  is_map = False

  def __init__(self, val):
    self.val = val

    # The behavior of raw_hash_set is controlled by a Policy template argument.
    # There is not enough detail in debug info to call Policy::element directly,
    # so we deduce which one it is and hard-code the right behavior.

    # Distinguish between the flat and node/string containers.  All of them
    # derive from a "raw" type in absl::container_internal that has a policy
    # template argument we can use to tell the different kinds apart.
    actual_type = self.val.type
    if actual_type.code == gdb.TYPE_CODE_REF:
      actual_type = actual_type.target()

    # Get the slot_type. If base is raw_hash_map, we need to go to its
    # parent-class (raw_hash_set).
    base = actual_type
    while not base.name.startswith('absl::container_internal::raw_hash_set'):
      base = _base_classes(base)[0]
    policy_type_name = base.template_argument(0).name
    self._is_flat = policy_type_name.startswith(
        'absl::container_internal::FlatHash')
    slot_type_name = f'{base.name}::slot_type'
    self._slot_type_ptr = gdb.lookup_type(slot_type_name).pointer()

  def _common(self):
    return self.val['settings_']['value']

  def _heap(self):
    return self._common()['heap_or_soo_']['heap']

  def _size(self):
    size = self._common()['size_']
    data = size['data_']
    shift = size['kSizeShift']
    return int(data) >> shift

  def _yield_slot(self, slot, i):
    """Yields the value_type at index i."""
    if not self._is_flat:
      slot = slot.dereference()

    if self.is_map:
      if self._is_flat:
        key, val = slot['value']['first'], slot['value']['second']
      else:
        key, val = slot['first'], slot['second']
      yield f'key[{i}]', key
      yield f'val[{i}]', val
    else:
      yield f'[{i}]', slot.cast(gdb.types.get_basic_type(slot.type))

  def to_string(self):
    return f'{self.short_type_name} of length {self._size()}'

  def display_hint(self):
    return 'map' if self.is_map else None

  def children(self):
    for i in range(self._common()['capacity_']):
      ctrl = int(self._heap()['control']['p'][i])
      if ctrl & 0x80:
        # Empty or deleted.
        continue
      slot = self._heap()['slot_array']['p'].cast(self._slot_type_ptr)[i]
      for y in self._yield_slot(slot, i):
        yield y

  def num_children(self, max_count):
    del max_count
    if (self.is_map):
      return int(self._size()) * 2
    return int(self._size())


class FlatHashSetPrinter(SwissTablePrinter):
  """Pretty-printer for flat_hash_set."""
  short_type_name = 'flat_hash_set'


class FlatHashMapPrinter(SwissTablePrinter):
  """Pretty-printer for flat_hash_map."""
  short_type_name = 'flat_hash_map'
  is_map = True


class NodeHashSetPrinter(SwissTablePrinter):
  """Pretty-printer for node_hash_set."""
  short_type_name = 'node_hash_set'


class NodeHashMapPrinter(SwissTablePrinter):
  """Pretty-printer for node_hash_map."""
  short_type_name = 'node_hash_map'
  is_map = True


absl_printer = reload_helper.find_or_create_printer('absl')


def _add(subprinter_name, regex, printer):
  reload_helper.remove_printer(absl_printer, subprinter_name)
  absl_printer.add_printer(subprinter_name, regex, printer)


_add('FlatHashSet', 'absl::flat_hash_set<.*>', FlatHashSetPrinter)
_add('FlatHashMap', 'absl::flat_hash_map<.*>', FlatHashMapPrinter)
_add('NodeHashSet', 'absl::node_hash_set<.*>', NodeHashSetPrinter)
_add('NodeHashMap', 'absl::node_hash_map<.*>', NodeHashMapPrinter)