summaryrefslogtreecommitdiffstats
path: root/python/gdbpp
diff options
context:
space:
mode:
Diffstat (limited to 'python/gdbpp')
-rw-r--r--python/gdbpp/gdbpp/__init__.py32
-rw-r--r--python/gdbpp/gdbpp/enumset.py31
-rw-r--r--python/gdbpp/gdbpp/linkedlist.py48
-rw-r--r--python/gdbpp/gdbpp/owningthread.py26
-rw-r--r--python/gdbpp/gdbpp/smartptr.py60
-rw-r--r--python/gdbpp/gdbpp/string.py21
-rw-r--r--python/gdbpp/gdbpp/syncedcontext.py40
-rw-r--r--python/gdbpp/gdbpp/tarray.py29
-rw-r--r--python/gdbpp/gdbpp/thashtable.py152
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"