|  | # Copyright 2022 The Dawn & Tint Authors | 
|  | # | 
|  | # Redistribution and use in source and binary forms, with or without | 
|  | # modification, are permitted provided that the following conditions are met: | 
|  | # | 
|  | # 1. Redistributions of source code must retain the above copyright notice, this | 
|  | #    list of conditions and the following disclaimer. | 
|  | # | 
|  | # 2. Redistributions in binary form must reproduce the above copyright notice, | 
|  | #    this list of conditions and the following disclaimer in the documentation | 
|  | #    and/or other materials provided with the distribution. | 
|  | # | 
|  | # 3. Neither the name of the copyright holder nor the names of its | 
|  | #    contributors may be used to endorse or promote products derived from | 
|  | #    this software without specific prior written permission. | 
|  | # | 
|  | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
|  | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
|  | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
|  | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
|  | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | # Pretty printers for the Tint project. | 
|  | # Add a line to your ~/.gdbinit to source this file, e.g.: | 
|  | # | 
|  | # source /path/to/dawn/src/tint/tint_gdb.py | 
|  |  | 
|  | import gdb | 
|  | import gdb.printing | 
|  | from itertools import chain | 
|  |  | 
|  | # When debugging this module, set _DEBUGGING = True so that re-sourcing this file in gdb replaces | 
|  | # the existing printers. | 
|  | _DEBUGGING = True | 
|  |  | 
|  | # Enable to display other data members along with child elements of compound data types (arrays, etc.). | 
|  | # This is useful in debuggers like VS Code that doesn't display the `to_string()` result in the watch window. | 
|  | # OTOH, it's less useful when using gdb/lldb's print command. | 
|  | _DISPLAY_MEMBERS_AS_CHILDREN = False | 
|  |  | 
|  |  | 
|  | # Tips for debugging using VS Code: | 
|  | # - Set a breakpoint where you can view the types you want to debug/write pretty printers for. | 
|  | # - Debug Console: source /path/to/dawn/src/tint/tint_gdb.py | 
|  | # - To execute Python code, in the Debug Console: | 
|  | #   -exec python foo = gdb.parse_and_eval('map.set_') | 
|  | #   -exec python v = (foo['slots_']['impl_']['slice']['data'] + 8).dereference()['value'] | 
|  | # | 
|  | # - Useful docs: | 
|  | #   Python API: https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API | 
|  | #   Especially: | 
|  | #     Types: https://sourceware.org/gdb/onlinedocs/gdb/Types-In-Python.html#Types-In-Python | 
|  | #     Values: https://sourceware.org/gdb/onlinedocs/gdb/Values-From-Inferior.html#Values-From-Inferior | 
|  |  | 
|  |  | 
|  | pp_set = gdb.printing.RegexpCollectionPrettyPrinter("tint") | 
|  |  | 
|  |  | 
|  | class Printer(object): | 
|  | '''Base class for Printers''' | 
|  |  | 
|  | def __init__(self, val): | 
|  | self.val = val | 
|  |  | 
|  | def template_type(self, index): | 
|  | '''Returns template type at index''' | 
|  | return self.val.type.template_argument(index) | 
|  |  | 
|  |  | 
|  | class UtilsSlicePrinter(Printer): | 
|  | '''Printer for tint::Slice<T>''' | 
|  |  | 
|  | def __init__(self, val): | 
|  | super(UtilsSlicePrinter, self).__init__(val) | 
|  | self.len = self.val['len'] | 
|  | self.cap = self.val['cap'] | 
|  | self.data = self.val['data'] | 
|  | self.elem_type = self.data.type.target().unqualified() | 
|  |  | 
|  | def length(self): | 
|  | return self.len | 
|  |  | 
|  | def value_at(self, index): | 
|  | '''Returns array value at index''' | 
|  | return (self.data + index).dereference().cast(self.elem_type) | 
|  |  | 
|  | def to_string(self): | 
|  | return 'length={} capacity={}'.format(self.len, self.cap) | 
|  |  | 
|  | def members(self): | 
|  | if _DISPLAY_MEMBERS_AS_CHILDREN: | 
|  | return [ | 
|  | ('length', self.len), | 
|  | ('capacity', self.cap), | 
|  | ] | 
|  | else: | 
|  | return [] | 
|  |  | 
|  | def children(self): | 
|  | for m in self.members(): | 
|  | yield m | 
|  | for i in range(self.len): | 
|  | yield str(i), self.value_at(i) | 
|  |  | 
|  | def display_hint(self): | 
|  | return 'array' | 
|  |  | 
|  |  | 
|  | pp_set.add_printer('UtilsSlicePrinter', '^tint::Slice<.*>$', UtilsSlicePrinter) | 
|  |  | 
|  |  | 
|  | class UtilsVectorPrinter(Printer): | 
|  | '''Printer for tint::Vector<T, N>''' | 
|  |  | 
|  | def __init__(self, val): | 
|  | super(UtilsVectorPrinter, self).__init__(val) | 
|  | self.slice = self.val['impl_']['slice'] | 
|  | self.using_heap = self.slice['cap'] > self.template_type(1) | 
|  |  | 
|  | def slice_printer(self): | 
|  | return UtilsSlicePrinter(self.slice) | 
|  |  | 
|  | def to_string(self): | 
|  | return 'heap={} {}'.format(self.using_heap, self.slice) | 
|  |  | 
|  | def members(self): | 
|  | if _DISPLAY_MEMBERS_AS_CHILDREN: | 
|  | return [ | 
|  | ('heap', self.using_heap), | 
|  | ] | 
|  | else: | 
|  | return [] | 
|  |  | 
|  | def children(self): | 
|  | return chain(self.members(), self.slice_printer().children()) | 
|  |  | 
|  | def display_hint(self): | 
|  | return 'array' | 
|  |  | 
|  |  | 
|  | pp_set.add_printer('UtilsVector', '^tint::Vector<.*>$', UtilsVectorPrinter) | 
|  |  | 
|  |  | 
|  | class UtilsVectorRefPrinter(Printer): | 
|  | '''Printer for tint::VectorRef<T>''' | 
|  |  | 
|  | def __init__(self, val): | 
|  | super(UtilsVectorRefPrinter, self).__init__(val) | 
|  | self.slice = self.val['slice_'] | 
|  | self.can_move = self.val['can_move_'] | 
|  |  | 
|  | def to_string(self): | 
|  | return 'can_move={} {}'.format(self.can_move, self.slice) | 
|  |  | 
|  | def members(self): | 
|  | if _DISPLAY_MEMBERS_AS_CHILDREN: | 
|  | return [ | 
|  | ('can_move', self.can_move), | 
|  | ] | 
|  | else: | 
|  | return [] | 
|  |  | 
|  | def children(self): | 
|  | return chain(self.members(), UtilsSlicePrinter(self.slice).children()) | 
|  |  | 
|  | def display_hint(self): | 
|  | return 'array' | 
|  |  | 
|  |  | 
|  | pp_set.add_printer('UtilsVector', '^tint::VectorRef<.*>$', | 
|  | UtilsVectorRefPrinter) | 
|  |  | 
|  |  | 
|  | class UtilsHashmapBasePrinter(Printer): | 
|  | '''Base Printer for HashmapBase-derived types''' | 
|  |  | 
|  | def __init__(self, val): | 
|  | super(UtilsHashmapBasePrinter, self).__init__(val) | 
|  | self.slice = UtilsVectorPrinter(self.val['slots_']).slice_printer() | 
|  | self.try_read_std_optional_func = self.try_read_std_optional | 
|  |  | 
|  | def to_string(self): | 
|  | length = 0 | 
|  | for slot in range(0, self.slice.length()): | 
|  | v = self.slice.value_at(slot) | 
|  | if v['hash'] != 0: | 
|  | length += 1 | 
|  | return 'length={}'.format(length) | 
|  |  | 
|  | def children(self): | 
|  | for slot in range(0, self.slice.length()): | 
|  | v = self.slice.value_at(slot) | 
|  | if v['hash'] != 0: | 
|  | entry = v['entry'] | 
|  |  | 
|  | # entry is a std::optional, let's try to extract its value for display | 
|  | kvp = self.try_read_std_optional_func(slot, entry) | 
|  | if kvp is None: | 
|  | # If we failed, just output the slot and entry as is, which will use | 
|  | # the default visualizer for each. | 
|  | kvp = slot, entry | 
|  |  | 
|  | yield str(kvp[0]), kvp[1] | 
|  |  | 
|  | def display_hint(self): | 
|  | return 'array' | 
|  |  | 
|  | def try_read_std_optional(self, slot, entry): | 
|  | return None | 
|  |  | 
|  |  | 
|  | class UtilsHashsetPrinter(UtilsHashmapBasePrinter): | 
|  | '''Printer for Hashset<T, N, HASH, EQUAL>''' | 
|  |  | 
|  | def try_read_std_optional(self, slot, entry): | 
|  | try: | 
|  | # libstdc++ | 
|  | v = entry['_M_payload']['_M_payload']['_M_value'] | 
|  | return slot, v | 
|  | except: | 
|  | return None | 
|  |  | 
|  |  | 
|  | pp_set.add_printer('UtilsHashset', '^tint::Hashset<.*>$', UtilsHashsetPrinter) | 
|  |  | 
|  |  | 
|  | class UtilsHashmapPrinter(UtilsHashmapBasePrinter): | 
|  | '''Printer for Hashmap<K, V, N, HASH, EQUAL>''' | 
|  |  | 
|  | def try_read_std_optional(self, slot, entry): | 
|  | try: | 
|  | # libstdc++ | 
|  | kvp = entry['_M_payload']['_M_payload']['_M_value'] | 
|  | return str(kvp['key']), kvp['value'] | 
|  | except: | 
|  | return None | 
|  |  | 
|  |  | 
|  | pp_set.add_printer('UtilsHashmap', '^tint::Hashmap<.*>$', UtilsHashmapPrinter) | 
|  |  | 
|  |  | 
|  | gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING) |