summaryrefslogtreecommitdiffstats
path: root/python/gdbpp
diff options
context:
space:
mode:
Diffstat (limited to 'python/gdbpp')
-rw-r--r--python/gdbpp/gdbpp/__init__.py28
-rw-r--r--python/gdbpp/gdbpp/linkedlist.py49
-rw-r--r--python/gdbpp/gdbpp/owningthread.py24
-rw-r--r--python/gdbpp/gdbpp/smartptr.py55
-rw-r--r--python/gdbpp/gdbpp/string.py19
-rw-r--r--python/gdbpp/gdbpp/tarray.py30
-rw-r--r--python/gdbpp/gdbpp/thashtable.py143
7 files changed, 348 insertions, 0 deletions
diff --git a/python/gdbpp/gdbpp/__init__.py b/python/gdbpp/gdbpp/__init__.py
new file mode 100644
index 000000000..d20de23a7
--- /dev/null
+++ b/python/gdbpp/gdbpp/__init__.py
@@ -0,0 +1,28 @@
+# -*- 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.linkedlist
+import gdbpp.owningthread
+import gdbpp.smartptr
+import gdbpp.string
+import gdbpp.tarray
+import gdbpp.thashtable
+
+gdb.printing.register_pretty_printer(None, GeckoPrettyPrinter.pp)
diff --git a/python/gdbpp/gdbpp/linkedlist.py b/python/gdbpp/gdbpp/linkedlist.py
new file mode 100644
index 000000000..966f9b9c0
--- /dev/null
+++ b/python/gdbpp/gdbpp/linkedlist.py
@@ -0,0 +1,49 @@
+# -*- 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 itertools
+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 000000000..d102bef24
--- /dev/null
+++ b/python/gdbpp/gdbpp/owningthread.py
@@ -0,0 +1,24 @@
+# -*- 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 000000000..c35215426
--- /dev/null
+++ b/python/gdbpp/gdbpp/smartptr.py
@@ -0,0 +1,55 @@
+# -*- 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('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()['mReferent']
+ 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 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))
+
+@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 000000000..33d536a02
--- /dev/null
+++ b/python/gdbpp/gdbpp/string.py
@@ -0,0 +1,19 @@
+# -*- 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('nsString', '^ns.*String$')
+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/tarray.py b/python/gdbpp/gdbpp/tarray.py
new file mode 100644
index 000000000..66797e4c9
--- /dev/null
+++ b/python/gdbpp/gdbpp/tarray.py
@@ -0,0 +1,30 @@
+# -*- 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 itertools
+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 000000000..10aee4946
--- /dev/null
+++ b/python/gdbpp/gdbpp/thashtable.py
@@ -0,0 +1,143 @@
+# -*- 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 itertools
+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 the
+ # actual entry capacity via ((uint32_t)1 << (kHashBits - mHashShift));
+ capacity = 1 << (table['kHashBits'] - 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
+
+ seenCount = 0
+ pEntry = store.cast(self.entry_type.pointer())
+ for i in range(0, int(capacity)):
+ entry = (pEntry + i).dereference()
+ # An mKeyHash of 0 means empty, 1 means deleted sentinel, so skip
+ # if that's the case.
+ if entry['mKeyHash'] <= 1:
+ continue
+
+ 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'