tint: add pretty printers for gdb and lldb
Currently supports pretty printing of: - tint::Utils::Vector, VectorRef, and Slice - tint::Utils::Hashset, Hashmap Change-Id: Ifbf2547b0f87d7fde8d9ff0dd458aa35c5fd57f4 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/106720 Reviewed-by: dan sinclair <dsinclair@google.com> Reviewed-by: Dan Sinclair <dsinclair@chromium.org> Commit-Queue: Antonio Maiorano <amaiorano@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
c3cbc35650
commit
14b5fb6f17
3
Doxyfile
3
Doxyfile
|
@ -1008,7 +1008,8 @@ RECURSIVE = YES
|
||||||
# Note that relative paths are relative to the directory from which doxygen is
|
# Note that relative paths are relative to the directory from which doxygen is
|
||||||
# run.
|
# run.
|
||||||
|
|
||||||
EXCLUDE =
|
EXCLUDE = src/tint/tint_gdb.py \
|
||||||
|
src/tint/tint_lldb.py
|
||||||
|
|
||||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||||
|
|
|
@ -0,0 +1,249 @@
|
||||||
|
# Copyright 2022 The Tint Authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# 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::utils::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::utils::Slice<.*>$', UtilsSlicePrinter)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsVectorPrinter(Printer):
|
||||||
|
'''Printer for tint::utils::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::utils::Vector<.*>$', UtilsVectorPrinter)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsVectorRefPrinter(Printer):
|
||||||
|
'''Printer for tint::utils::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::utils::VectorRef<.*>$', UtilsVectorRefPrinter)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsHashsetPrinter(Printer):
|
||||||
|
'''Printer for Hashset<T, N, HASH, EQUAL>'''
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
super(UtilsHashsetPrinter, 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:
|
||||||
|
value = v['value']
|
||||||
|
|
||||||
|
# value is a std::optional, let's try to extract its value for display
|
||||||
|
kvp = self.try_read_std_optional_func(slot, value)
|
||||||
|
if kvp is None:
|
||||||
|
# If we failed, just output the slot and value as is, which will use
|
||||||
|
# the default visualizer for each.
|
||||||
|
kvp = slot, value
|
||||||
|
|
||||||
|
yield str(kvp[0]), kvp[1]
|
||||||
|
|
||||||
|
def display_hint(self):
|
||||||
|
return 'array'
|
||||||
|
|
||||||
|
def try_read_std_optional(self, slot, value):
|
||||||
|
try:
|
||||||
|
# libstdc++
|
||||||
|
v = value['_M_payload']['_M_payload']['_M_value']
|
||||||
|
return slot, v
|
||||||
|
# return str(kvp['key']), kvp['value']
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
pp_set.add_printer(
|
||||||
|
'UtilsHashset', '^tint::utils::Hashset<.*>$', UtilsHashsetPrinter)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsHashmapPrinter(Printer):
|
||||||
|
'''Printer for Hashmap<K, V, N, HASH, EQUAL>'''
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
super(UtilsHashmapPrinter, self).__init__(val)
|
||||||
|
self.hash_set = UtilsHashsetPrinter(self.val['set_'])
|
||||||
|
# Replace the lookup function so we can extract the key and value out of the std::optionals in the Hashset
|
||||||
|
self.hash_set.try_read_std_optional_func = self.try_read_std_optional
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
return self.hash_set.to_string()
|
||||||
|
|
||||||
|
def children(self):
|
||||||
|
return self.hash_set.children()
|
||||||
|
|
||||||
|
def display_hint(self):
|
||||||
|
return 'array'
|
||||||
|
|
||||||
|
def try_read_std_optional(self, slot, value):
|
||||||
|
try:
|
||||||
|
# libstdc++
|
||||||
|
kvp = value['_M_payload']['_M_payload']['_M_value']
|
||||||
|
return str(kvp['key']), kvp['value']
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
# Failed, fall back on hash_set
|
||||||
|
return self.hash_set.try_read_std_optional(slot, value)
|
||||||
|
|
||||||
|
|
||||||
|
pp_set.add_printer(
|
||||||
|
'UtilsHashmap', '^tint::utils::Hashmap<.*>$', UtilsHashmapPrinter)
|
||||||
|
|
||||||
|
|
||||||
|
gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
|
|
@ -0,0 +1,397 @@
|
||||||
|
# Copyright 2022 The Tint Authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
# Pretty printers for the Tint project.
|
||||||
|
#
|
||||||
|
# If using lldb from command line, add a line to your ~/.lldbinit to import the printers:
|
||||||
|
#
|
||||||
|
# command script import /path/to/dawn/src/tint/tint_lldb.py
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# If using VS Code on MacOS with the Microsoft C/C++ extension, add the following to
|
||||||
|
# your launch.json (make sure you specify an absolute path to tint_lldb.py):
|
||||||
|
#
|
||||||
|
# "name": "Launch",
|
||||||
|
# "type": "cppdbg",
|
||||||
|
# "request": "launch",
|
||||||
|
# ...
|
||||||
|
# "setupCommands": [
|
||||||
|
# {
|
||||||
|
# "description": "Load tint pretty printers",
|
||||||
|
# "ignoreFailures": false,
|
||||||
|
# "text": "command script import /path/to/dawn/src/tint/tint_lldb.py,
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
#
|
||||||
|
# If using VS Code with the CodeLLDB extension (https://github.com/vadimcn/vscode-lldb),
|
||||||
|
# add the following to your launch.json:
|
||||||
|
#
|
||||||
|
# "name": "Launch",
|
||||||
|
# "type": "lldb",
|
||||||
|
# "request": "launch",
|
||||||
|
# ...
|
||||||
|
# "initCommands": [
|
||||||
|
# "command script import /path/to/dawn/src/tint/tint_lldb.py"
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# Based on pretty printers for:
|
||||||
|
# Rust: https://github.com/vadimcn/vscode-lldb/blob/master/formatters/rust.py
|
||||||
|
# Dlang: https://github.com/Pure-D/dlang-debug/blob/master/lldb_dlang.py
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# 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: -exec command script import /path/to/dawn/src/tint/tint_lldb.py
|
||||||
|
# - You can re-run the above command to reload the printers after modifying the python script.
|
||||||
|
|
||||||
|
# - Useful docs:
|
||||||
|
# Formattesr: https://lldb.llvm.org/use/variable.html
|
||||||
|
# Python API: https://lldb.llvm.org/python_api.html
|
||||||
|
# Especially:
|
||||||
|
# SBType: https://lldb.llvm.org/python_api/lldb.SBType.html
|
||||||
|
# SBValue: https://lldb.llvm.org/python_api/lldb.SBValue.html
|
||||||
|
|
||||||
|
from __future__ import print_function, division
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
import lldb
|
||||||
|
import types
|
||||||
|
|
||||||
|
if sys.version_info[0] == 2:
|
||||||
|
# python2-based LLDB accepts utf8-encoded ascii strings only.
|
||||||
|
def to_lldb_str(s): return s.encode(
|
||||||
|
'utf8', 'backslashreplace') if isinstance(s, unicode) else s
|
||||||
|
range = xrange
|
||||||
|
else:
|
||||||
|
to_lldb_str = str
|
||||||
|
|
||||||
|
string_encoding = "escape" # remove | unicode | escape
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
module = sys.modules[__name__]
|
||||||
|
tint_category = None
|
||||||
|
|
||||||
|
|
||||||
|
def __lldb_init_module(debugger, dict):
|
||||||
|
global tint_category
|
||||||
|
|
||||||
|
tint_category = debugger.CreateCategory('tint')
|
||||||
|
tint_category.SetEnabled(True)
|
||||||
|
|
||||||
|
attach_synthetic_to_type(
|
||||||
|
UtilsSlicePrinter, r'^tint::utils::Slice<.+>$', True)
|
||||||
|
|
||||||
|
attach_synthetic_to_type(
|
||||||
|
UtilsVectorPrinter, r'^tint::utils::Vector<.+>$', True)
|
||||||
|
|
||||||
|
attach_synthetic_to_type(
|
||||||
|
UtilsVectorRefPrinter, r'^tint::utils::VectorRef<.+>$', True)
|
||||||
|
|
||||||
|
attach_synthetic_to_type(
|
||||||
|
UtilsHashsetPrinter, r'^tint::utils::Hashset<.+>$', True)
|
||||||
|
|
||||||
|
attach_synthetic_to_type(
|
||||||
|
UtilsHashmapPrinter, r'^tint::utils::Hashmap<.+>$', True)
|
||||||
|
|
||||||
|
|
||||||
|
def attach_synthetic_to_type(synth_class, type_name, is_regex=False):
|
||||||
|
global module, tint_category
|
||||||
|
synth = lldb.SBTypeSynthetic.CreateWithClassName(
|
||||||
|
__name__ + '.' + synth_class.__name__)
|
||||||
|
synth.SetOptions(lldb.eTypeOptionCascade)
|
||||||
|
ret = tint_category.AddTypeSynthetic(
|
||||||
|
lldb.SBTypeNameSpecifier(type_name, is_regex), synth)
|
||||||
|
log.debug('attaching synthetic %s to "%s", is_regex=%s -> %s',
|
||||||
|
synth_class.__name__, type_name, is_regex, ret)
|
||||||
|
|
||||||
|
def summary_fn(valobj, dict): return get_synth_summary(
|
||||||
|
synth_class, valobj, dict)
|
||||||
|
# LLDB accesses summary fn's by name, so we need to create a unique one.
|
||||||
|
summary_fn.__name__ = '_get_synth_summary_' + synth_class.__name__
|
||||||
|
setattr(module, summary_fn.__name__, summary_fn)
|
||||||
|
attach_summary_to_type(summary_fn, type_name, is_regex)
|
||||||
|
|
||||||
|
|
||||||
|
def attach_summary_to_type(summary_fn, type_name, is_regex=False):
|
||||||
|
global module, tint_category
|
||||||
|
summary = lldb.SBTypeSummary.CreateWithFunctionName(
|
||||||
|
__name__ + '.' + summary_fn.__name__)
|
||||||
|
summary.SetOptions(lldb.eTypeOptionCascade)
|
||||||
|
ret = tint_category.AddTypeSummary(
|
||||||
|
lldb.SBTypeNameSpecifier(type_name, is_regex), summary)
|
||||||
|
log.debug('attaching summary %s to "%s", is_regex=%s -> %s',
|
||||||
|
summary_fn.__name__, type_name, is_regex, ret)
|
||||||
|
|
||||||
|
|
||||||
|
def get_synth_summary(synth_class, valobj, dict):
|
||||||
|
''''
|
||||||
|
get_summary' is annoyingly not a part of the standard LLDB synth provider API.
|
||||||
|
This trick allows us to share data extraction logic between synth providers and their sibling summary providers.
|
||||||
|
'''
|
||||||
|
synth = synth_class(valobj.GetNonSyntheticValue(), dict)
|
||||||
|
synth.update()
|
||||||
|
summary = synth.get_summary()
|
||||||
|
return to_lldb_str(summary)
|
||||||
|
|
||||||
|
|
||||||
|
def member(valobj, *chain):
|
||||||
|
'''Performs chained GetChildMemberWithName lookups'''
|
||||||
|
for name in chain:
|
||||||
|
valobj = valobj.GetChildMemberWithName(name)
|
||||||
|
return valobj
|
||||||
|
|
||||||
|
|
||||||
|
class Printer(object):
|
||||||
|
'''Base class for Printers'''
|
||||||
|
|
||||||
|
def __init__(self, valobj, dict={}):
|
||||||
|
self.valobj = valobj
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_child_index(self, name):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_summary(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def member(self, *chain):
|
||||||
|
'''Performs chained GetChildMemberWithName lookups'''
|
||||||
|
return member(self.valobj, *chain)
|
||||||
|
|
||||||
|
def template_params(self):
|
||||||
|
'''Returns list of template params values (as strings)'''
|
||||||
|
type_name = self.valobj.GetTypeName()
|
||||||
|
params = []
|
||||||
|
level = 0
|
||||||
|
start = 0
|
||||||
|
for i, c in enumerate(type_name):
|
||||||
|
if c == '<':
|
||||||
|
level += 1
|
||||||
|
if level == 1:
|
||||||
|
start = i + 1
|
||||||
|
elif c == '>':
|
||||||
|
level -= 1
|
||||||
|
if level == 0:
|
||||||
|
params.append(type_name[start:i].strip())
|
||||||
|
elif c == ',' and level == 1:
|
||||||
|
params.append(type_name[start:i].strip())
|
||||||
|
start = i + 1
|
||||||
|
return params
|
||||||
|
|
||||||
|
def template_param_at(self, index):
|
||||||
|
'''Returns template param value at index (as string)'''
|
||||||
|
return self.template_params()[index]
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsSlicePrinter(Printer):
|
||||||
|
'''Printer for tint::utils::Slice<T>'''
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.len = self.valobj.GetChildMemberWithName('len')
|
||||||
|
self.cap = self.valobj.GetChildMemberWithName('cap')
|
||||||
|
self.data = self.valobj.GetChildMemberWithName('data')
|
||||||
|
self.elem_type = self.data.GetType().GetPointeeType()
|
||||||
|
self.elem_size = self.elem_type.GetByteSize()
|
||||||
|
|
||||||
|
def get_summary(self):
|
||||||
|
return 'length={} capacity={}'.format(self.len.GetValueAsUnsigned(), self.cap.GetValueAsUnsigned())
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
# NOTE: VS Code on MacOS hangs if we try to expand something too large, so put an artificial limit
|
||||||
|
# until we can figure out how to know if this is a valid instance.
|
||||||
|
return min(self.len.GetValueAsUnsigned(), 256)
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
try:
|
||||||
|
if not 0 <= index < self.num_children():
|
||||||
|
return None
|
||||||
|
# TODO: return self.value_at(index)
|
||||||
|
offset = index * self.elem_size
|
||||||
|
return self.data.CreateChildAtOffset('[%s]' % index, offset, self.elem_type)
|
||||||
|
except Exception as e:
|
||||||
|
log.error('%s', e)
|
||||||
|
raise
|
||||||
|
|
||||||
|
def value_at(self, index):
|
||||||
|
'''Returns array value at index'''
|
||||||
|
offset = index * self.elem_size
|
||||||
|
return self.data.CreateChildAtOffset('[%s]' % index, offset, self.elem_type)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsVectorPrinter(Printer):
|
||||||
|
'''Printer for tint::utils::Vector<T, N>'''
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.slice = self.member('impl_', 'slice')
|
||||||
|
self.slice_printer = UtilsSlicePrinter(self.slice)
|
||||||
|
self.fixed_size = int(self.template_param_at(1))
|
||||||
|
self.cap = self.slice_printer.member('cap')
|
||||||
|
|
||||||
|
def get_summary(self):
|
||||||
|
using_heap = self.cap.GetValueAsUnsigned() > self.fixed_size
|
||||||
|
return 'heap={} {}'.format(using_heap, self.slice_printer.get_summary())
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
return self.slice_printer.num_children()
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
return self.slice_printer.has_children()
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
return self.slice_printer.get_child_at_index(index)
|
||||||
|
|
||||||
|
def make_slice_printer(self):
|
||||||
|
return UtilsSlicePrinter(self.slice)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsVectorRefPrinter(Printer):
|
||||||
|
'''Printer for tint::utils::VectorRef<T>'''
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.slice = self.member('slice_')
|
||||||
|
self.slice_printer = UtilsSlicePrinter(self.slice)
|
||||||
|
self.can_move = self.member('can_move_')
|
||||||
|
|
||||||
|
def get_summary(self):
|
||||||
|
return 'can_move={} {}'.format(self.can_move.GetValue(), self.slice_printer.get_summary())
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
return self.slice_printer.num_children()
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
return self.slice_printer.has_children()
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
return self.slice_printer.get_child_at_index(index)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsHashsetPrinter(Printer):
|
||||||
|
'''Printer for Hashset<T, N, HASH, EQUAL>'''
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.slice = UtilsVectorPrinter(
|
||||||
|
self.member('slots_')).make_slice_printer()
|
||||||
|
|
||||||
|
self.try_read_std_optional_func = self.try_read_std_optional
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.valid_slots = []
|
||||||
|
for slot in range(0, self.slice.num_children()):
|
||||||
|
v = self.slice.value_at(slot)
|
||||||
|
if member(v, 'hash').GetValueAsUnsigned() != 0:
|
||||||
|
self.valid_slots.append(slot)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_summary(self):
|
||||||
|
return 'length={}'.format(self.num_children())
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
return len(self.valid_slots)
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
slot = self.valid_slots[index]
|
||||||
|
v = self.slice.value_at(slot)
|
||||||
|
value = member(v, 'value')
|
||||||
|
|
||||||
|
# value is a std::optional, let's try to extract its value for display
|
||||||
|
kvp = self.try_read_std_optional_func(slot, value)
|
||||||
|
if kvp is None:
|
||||||
|
# If we failed, just output the slot and value as is, which will use
|
||||||
|
# the default printer for std::optional.
|
||||||
|
kvp = slot, value
|
||||||
|
|
||||||
|
return kvp[1].CreateChildAtOffset('[{}]'.format(kvp[0]), 0, kvp[1].GetType())
|
||||||
|
|
||||||
|
def try_read_std_optional(self, slot, value):
|
||||||
|
try:
|
||||||
|
# libc++
|
||||||
|
v = value.EvaluateExpression('__val_')
|
||||||
|
if v.name is not None:
|
||||||
|
return slot, v
|
||||||
|
|
||||||
|
# libstdc++
|
||||||
|
v = value.EvaluateExpression('_M_payload._M_payload._M_value')
|
||||||
|
if v.name is not None:
|
||||||
|
return slot, v
|
||||||
|
return None
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsHashmapPrinter(Printer):
|
||||||
|
'''Printer for Hashmap<K, V, N, HASH, EQUAL>'''
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.hash_set = UtilsHashsetPrinter(self.member('set_'))
|
||||||
|
# Replace the lookup function so we can extract the key and value out of the std::optionals in the Hashset
|
||||||
|
self.hash_set.try_read_std_optional_func = self.try_read_std_optional
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.hash_set.update()
|
||||||
|
|
||||||
|
def get_summary(self):
|
||||||
|
return self.hash_set.get_summary()
|
||||||
|
|
||||||
|
def num_children(self):
|
||||||
|
return self.hash_set.num_children()
|
||||||
|
|
||||||
|
def has_children(self):
|
||||||
|
return self.hash_set.has_children()
|
||||||
|
|
||||||
|
def get_child_at_index(self, index):
|
||||||
|
return self.hash_set.get_child_at_index(index)
|
||||||
|
|
||||||
|
def try_read_std_optional(self, slot, value):
|
||||||
|
try:
|
||||||
|
# libc++
|
||||||
|
val = value.EvaluateExpression('__val_')
|
||||||
|
k = val.EvaluateExpression('key')
|
||||||
|
v = val.EvaluateExpression('value')
|
||||||
|
if k.name is not None and v.name is not None:
|
||||||
|
return k.GetValue(), v
|
||||||
|
|
||||||
|
# libstdc++
|
||||||
|
val = value.EvaluateExpression('_M_payload._M_payload._M_value')
|
||||||
|
k = val.EvaluateExpression('key')
|
||||||
|
v = val.EvaluateExpression('value')
|
||||||
|
if k.name is not None and v.name is not None:
|
||||||
|
return k.GetValue(), v
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
# Failed, fall back on hash_set
|
||||||
|
return self.hash_set.try_read_std_optional(slot, value)
|
Loading…
Reference in New Issue