fune/python/gdbpp/gdbpp/thashtable.py
Cristian Tuns c1b52fd95e Backed out 5 changesets (bug 1811850) for causing linting bustages(bugzilla) CLOSED TREE
Backed out changeset e8fcfc7f8108 (bug 1811850)
Backed out changeset f8950d716c9e (bug 1811850)
Backed out changeset f650123cc188 (bug 1811850)
Backed out changeset d96f90c2c58b (bug 1811850)
Backed out changeset c3b0f9666183 (bug 1811850)
2023-03-16 22:16:30 -04:00

151 lines
6.4 KiB
Python

# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import gdb
from gdbpp import GeckoPrettyPrinter
def walk_template_to_given_base(value, desired_tag_prefix):
"""Given a value of some template subclass, walk up its ancestry until we
hit the desired type, then return the appropriate value (which will then
have that type).
"""
# Base case
t = value.type
# It's possible that we're dealing with an alias template that looks like:
# template<typename Protocol>
# using ManagedContainer = nsTHashtable<nsPtrHashKey<Protocol>>;
# In which case we want to strip the indirection, and strip_typedefs()
# accomplishes this. (Disclaimer: I tried it and it worked and it didn't
# break my other use cases, if things start exploding, do reconsider.)
t = t.strip_typedefs()
if t.tag.startswith(desired_tag_prefix):
return value
for f in t.fields():
# we only care about the inheritance hierarchy
if not f.is_base_class:
continue
# This is the answer or something we're going to need to recurse into.
fv = value[f]
ft = fv.type
# slightly optimize by checking the tag rather than in the recursion
if ft.tag.startswith(desired_tag_prefix):
# found it!
return fv
return walk_template_to_given_base(fv, desired_tag_prefix)
return None
# The templates and their inheritance hierarchy form an onion of types around
# the nsTHashtable core at the center. All we care about is that nsTHashtable,
# but we register for the descendant types in order to avoid the default pretty
# printers having to unwrap those onion layers, wasting precious lines.
@GeckoPrettyPrinter("nsClassHashtable", "^nsClassHashtable<.*>$")
@GeckoPrettyPrinter("nsDataHashtable", "^nsDataHashtable<.*>$")
@GeckoPrettyPrinter("nsInterfaceHashtable", "^nsInterfaceHashtable<.*>$")
@GeckoPrettyPrinter("nsRefPtrHashtable", "^nsRefPtrHashtable<.*>$")
@GeckoPrettyPrinter("nsBaseHashtable", "^nsBaseHashtable<.*>$")
@GeckoPrettyPrinter("nsTHashtable", "^nsTHashtable<.*>$")
class thashtable_printer(object):
def __init__(self, outer_value):
self.outermost_type = outer_value.type
value = walk_template_to_given_base(outer_value, "nsTHashtable<")
self.value = value
self.entry_type = value.type.template_argument(0)
# -- Determine whether we're a hashTABLE or a hashSET
# If we're a table, the entry type will be a nsBaseHashtableET template.
# If we're a set, it will be something like nsPtrHashKey.
#
# So, assume we're a set if we're not nsBaseHashtableET<
# (It should ideally also be true that the type ends with HashKey, but
# since nsBaseHashtableET causes us to assume "mData" exists, let's
# pivot based on that.)
self.is_table = self.entry_type.tag.startswith("nsBaseHashtableET<")
# While we know that it has a field `mKeyHash` for the hash-code and
# book-keeping, and a DataType field mData for the value (if we're a
# table), the key field frustratingly varies by key type.
#
# So we want to walk its key type to figure out the field name. And we
# do mean field name. The field object is no good for subscripting the
# value unless the field was directly owned by that value's type. But
# by using a string name, we save ourselves all that fanciness.
if self.is_table:
# For nsBaseHashtableET<KeyClass, DataType>, we want the KeyClass
key_type = self.entry_type.template_argument(0)
else:
# If we're a set, our entry type is the key class already!
key_type = self.entry_type
self.key_field_name = None
for f in key_type.fields():
# No need to traverse up the type hierarchy...
if f.is_base_class:
continue
# ...just to skip the fields we know exist...
if f.name == "mKeyHash" or f.name == "mData":
continue
# ...and assume the first one we find is the key.
self.key_field_name = f.name
break
def children(self):
table = self.value["mTable"]
# mEntryCount is the number of occupied slots/entries in the table.
# We can use this to avoid doing wasted memory reads.
entryCount = table["mEntryCount"]
if entryCount == 0:
return
# The table capacity is tracked "cleverly" in terms of how many bits
# the hash needs to be shifted. CapacityFromHashShift calculates this
# quantity, but may be inlined, so we replicate the calculation here.
hashType = gdb.lookup_type("mozilla::HashNumber")
hashBits = hashType.sizeof * 8
capacity = 1 << (hashBits - table["mHashShift"])
# Pierce generation-tracking EntryStore class to get at buffer. The
# class instance always exists, but this char* may be null.
store = table["mEntryStore"]["mEntryStore"]
key_field_name = self.key_field_name
# The entry store is laid out with hashes for all possible entries
# first, followed by all the entries.
pHashes = store.cast(hashType.pointer())
pEntries = pHashes + capacity
pEntries = pEntries.cast(self.entry_type.pointer())
seenCount = 0
for i in range(0, int(capacity)):
entryHash = (pHashes + i).dereference()
# An entry hash of 0 means empty, 1 means deleted sentinel, so skip
# if that's the case.
if entryHash <= 1:
continue
entry = (pEntries + i).dereference()
yield ("%d" % i, entry[key_field_name])
if self.is_table:
yield ("%d" % i, entry["mData"])
# Stop iterating if we know there are no more occupied slots.
seenCount += 1
if seenCount >= entryCount:
break
def to_string(self):
# The most specific template type is the most interesting.
return str(self.outermost_type)
def display_hint(self):
if self.is_table:
return "map"
else:
return "array"