diff options
Diffstat (limited to 'python/gdbpp')
-rw-r--r-- | python/gdbpp/gdbpp/__init__.py | 28 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/linkedlist.py | 49 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/owningthread.py | 24 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/smartptr.py | 55 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/string.py | 19 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/tarray.py | 30 | ||||
-rw-r--r-- | python/gdbpp/gdbpp/thashtable.py | 143 |
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' |