diff options
Diffstat (limited to 'python/gdbpp')
-rw-r--r-- | python/gdbpp/gdbpp/__init__.py | 32 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/enumset.py | 31 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/linkedlist.py | 48 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/owningthread.py | 26 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/smartptr.py | 60 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/string.py | 21 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/syncedcontext.py | 40 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/tarray.py | 29 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/thashtable.py | 152 |
9 files changed, 439 insertions, 0 deletions
diff --git a/python/gdbpp/gdbpp/__init__.py b/python/gdbpp/gdbpp/__init__.py new file mode 100644 index 0000000000..9edc626eb6 --- /dev/null +++ b/python/gdbpp/gdbpp/__init__.py @@ -0,0 +1,32 @@ +# -*- 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 +import gdb.printing + + +class GeckoPrettyPrinter(object): + pp = gdb.printing.RegexpCollectionPrettyPrinter("GeckoPrettyPrinters") + + def __init__(self, name, regexp): + self.name = name + self.regexp = regexp + + def __call__(self, wrapped): + GeckoPrettyPrinter.pp.add_printer(self.name, self.regexp, wrapped) + return wrapped + + +import gdbpp.enumset # noqa: F401 +import gdbpp.linkedlist # noqa: F401 +import gdbpp.owningthread # noqa: F401 +import gdbpp.smartptr # noqa: F401 +import gdbpp.string # noqa: F401 +import gdbpp.syncedcontext # noqa: F401 +import gdbpp.tarray # noqa: F401 +import gdbpp.thashtable # noqa: F401 + +gdb.printing.register_pretty_printer(None, GeckoPrettyPrinter.pp) diff --git a/python/gdbpp/gdbpp/enumset.py b/python/gdbpp/gdbpp/enumset.py new file mode 100644 index 0000000000..71315389c1 --- /dev/null +++ b/python/gdbpp/gdbpp/enumset.py @@ -0,0 +1,31 @@ +# -*- 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 + + +@GeckoPrettyPrinter("mozilla::EnumSet", "^mozilla::EnumSet<.*>$") +class enumset_printer(object): + def __init__(self, value): + self.value = value + self.enum_type = value.type.template_argument(0) + + def children(self): + bitfield = self.value["mBitField"] + max_bit = (self.enum_type.sizeof * 8) - 1 + return ( + ("flag", gdb.Value(i).cast(self.enum_type)) + for i in range(0, max_bit) + if ((bitfield & (2**i)) != 0) + ) + + def to_string(self): + return str(self.value.type) + + def display_hint(self): + return "array" diff --git a/python/gdbpp/gdbpp/linkedlist.py b/python/gdbpp/gdbpp/linkedlist.py new file mode 100644 index 0000000000..1ba25a1cf0 --- /dev/null +++ b/python/gdbpp/gdbpp/linkedlist.py @@ -0,0 +1,48 @@ +# -*- 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/. + +from gdbpp import GeckoPrettyPrinter + + +# mfbt's LinkedList<T> is a doubly-linked list where the items in the list store +# the next/prev pointers as part of themselves rather than the list structure be +# its own independent data structure. This means: +# - Every item may belong to at most one LinkedList instance. +# - For our pretty printer, we only want to pretty-print the LinkedList object +# itself. We do not want to start printing every item in the list whenever +# we run into a LinkedListElement<T>. +@GeckoPrettyPrinter("mozilla::LinkedList", "^mozilla::LinkedList<.*>$") +class linkedlist_printer(object): + def __init__(self, value): + self.value = value + # mfbt's LinkedList has the elements of the linked list subclass from + # LinkedListElement<T>. We want its pointer type for casting purposes. + # + # (We want to list pointers since we expect all of these objects to be + # complex enough that we don't want to automatically expand them. The + # LinkedListElement type itself isn't small.) + self.t_ptr_type = value.type.template_argument(0).pointer() + + def children(self): + # Walk mNext until we loop back around to the sentinel. The sentinel + # item always exists and in the zero-length base-case mNext == sentinel, + # so extract that immediately and update it throughout the loop. + sentinel = self.value["sentinel"] + pSentinel = sentinel.address + pNext = sentinel["mNext"] + i = 0 + while pSentinel != pNext: + list_elem = pNext.dereference() + list_value = pNext.cast(self.t_ptr_type) + yield ("%d" % i, list_value) + pNext = list_elem["mNext"] + i += 1 + + def to_string(self): + return str(self.value.type) + + def display_hint(self): + return "array" diff --git a/python/gdbpp/gdbpp/owningthread.py b/python/gdbpp/gdbpp/owningthread.py new file mode 100644 index 0000000000..52055b3c99 --- /dev/null +++ b/python/gdbpp/gdbpp/owningthread.py @@ -0,0 +1,26 @@ +# -*- 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 + + +@GeckoPrettyPrinter("nsAutoOwningThread", "^nsAutoOwningThread$") +class owning_thread_printer(object): + def __init__(self, value): + self.value = value + + def to_string(self): + prthread_type = gdb.lookup_type("PRThread").pointer() + prthread = self.value["mThread"].cast(prthread_type) + name = prthread["name"] + + # if the thread doesn't have a name try to get its thread id (might not + # work on !linux) + name = prthread["tid"] + + return name if name else "(PRThread *) %s" % prthread diff --git a/python/gdbpp/gdbpp/smartptr.py b/python/gdbpp/gdbpp/smartptr.py new file mode 100644 index 0000000000..1a9f503412 --- /dev/null +++ b/python/gdbpp/gdbpp/smartptr.py @@ -0,0 +1,60 @@ +# -*- 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/. + +from gdbpp import GeckoPrettyPrinter + + +@GeckoPrettyPrinter("nsWeakPtr", "^nsCOMPtr<nsIWeakReference>$") +class weak_ptr_printer(object): + def __init__(self, value): + self.value = value + + def to_string(self): + proxy = self.value["mRawPtr"] + if not proxy: + return "[(%s) 0x0]" % proxy.type + + ref_type = proxy.dynamic_type + weak_ptr = proxy.cast(ref_type).dereference()["mObject"] + if not weak_ptr: + return "[(%s) %s]" % (weak_ptr.type, weak_ptr) + + return "[(%s) %s]" % (weak_ptr.dynamic_type, weak_ptr) + + +@GeckoPrettyPrinter("mozilla::StaticAutoPtr", "^mozilla::StaticAutoPtr<.*>$") +@GeckoPrettyPrinter("mozilla::StaticRefPtr", "^mozilla::StaticRefPtr<.*>$") +@GeckoPrettyPrinter("nsAutoPtr", "^nsAutoPtr<.*>$") +@GeckoPrettyPrinter("nsCOMPtr", "^nsCOMPtr<.*>$") +@GeckoPrettyPrinter("RefPtr", "^RefPtr<.*>$") +class smartptr_printer(object): + def __init__(self, value): + self.value = value["mRawPtr"] + + def children(self): + yield ("mRawPtr", self.value) + + def to_string(self): + if not self.value: + type_name = str(self.value.type) + else: + type_name = str(self.value.dereference().dynamic_type.pointer()) + + return "[(%s)]" % (type_name) + + +@GeckoPrettyPrinter("UniquePtr", "^mozilla::UniquePtr<.*>$") +class uniqueptr_printer(object): + def __init__(self, value): + self.value = value["mTuple"]["mFirstA"] + + def to_string(self): + if not self.value: + type_name = str(self.value.type) + else: + type_name = str(self.value.dereference().dynamic_type.pointer()) + + return "[(%s) %s]" % (type_name, str(self.value)) diff --git a/python/gdbpp/gdbpp/string.py b/python/gdbpp/gdbpp/string.py new file mode 100644 index 0000000000..b5722f2c62 --- /dev/null +++ b/python/gdbpp/gdbpp/string.py @@ -0,0 +1,21 @@ +# -*- 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/. + +from gdbpp import GeckoPrettyPrinter + + +@GeckoPrettyPrinter("nsTSubstring", "^nsTSubstring<.*>$") +@GeckoPrettyPrinter("nsTAutoStringN", "^nsTAutoStringN<.*>$") +@GeckoPrettyPrinter("nsString", "^nsTString<.*>$") +class string_printer(object): + def __init__(self, value): + self.value = value + + def to_string(self): + return self.value["mData"] + + def display_hint(self): + return "string" diff --git a/python/gdbpp/gdbpp/syncedcontext.py b/python/gdbpp/gdbpp/syncedcontext.py new file mode 100644 index 0000000000..c9c80a5562 --- /dev/null +++ b/python/gdbpp/gdbpp/syncedcontext.py @@ -0,0 +1,40 @@ +# -*- 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 re + +import gdb + +from gdbpp import GeckoPrettyPrinter + + +@GeckoPrettyPrinter( + "syncedcontext::FieldValues", "^mozilla::dom::syncedcontext::FieldValues<.*>$" +) +class synced_context_field_printer(object): + def __init__(self, value): + self.type = gdb.types.get_basic_type(value.type).template_argument(0) + self.value = value.cast(self.type) + self.IDX_re = re.compile(r"<(.*?),") + + def to_string(self): + return "" + + def children(self): + for base_field in self.type.fields(): + base = self.value[base_field] + for field in base.type.fields(): + field_value = base[field] + if gdb.types.has_field(field_value.type, "mField"): + field_name = self.IDX_re.search(f"{field_value.type.name}") + field_type = field.type.template_argument(1) + yield ( + f"Field<{field_name[1]}, {field_type}>", + field_value["mField"], + ) + + def display_hint(self): + return "array" diff --git a/python/gdbpp/gdbpp/tarray.py b/python/gdbpp/gdbpp/tarray.py new file mode 100644 index 0000000000..a5c82253b9 --- /dev/null +++ b/python/gdbpp/gdbpp/tarray.py @@ -0,0 +1,29 @@ +# -*- 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/. + +from gdbpp import GeckoPrettyPrinter + + +@GeckoPrettyPrinter("InfallibleTArray", "^InfallibleTArray<.*>$") +@GeckoPrettyPrinter("FallibleTArray", "^FallibleTArray<.*>$") +@GeckoPrettyPrinter("AutoTArray", "^AutoTArray<.*>$") +@GeckoPrettyPrinter("nsTArray", "^nsTArray<.*>$") +class tarray_printer(object): + def __init__(self, value): + self.value = value + self.elem_type = value.type.template_argument(0) + + def children(self): + length = self.value["mHdr"].dereference()["mLength"] + data = self.value["mHdr"] + 1 + elements = data.cast(self.elem_type.pointer()) + return (("%d" % i, (elements + i).dereference()) for i in range(0, int(length))) + + def to_string(self): + return str(self.value.type) + + def display_hint(self): + return "array" diff --git a/python/gdbpp/gdbpp/thashtable.py b/python/gdbpp/gdbpp/thashtable.py new file mode 100644 index 0000000000..8b0294acf6 --- /dev/null +++ b/python/gdbpp/gdbpp/thashtable.py @@ -0,0 +1,152 @@ +# -*- 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" |