Skip to content

Commit 14b5fb6

Browse files
amaioranoDawn LUCI CQ
authored and
Dawn LUCI CQ
committed
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>
1 parent c3cbc35 commit 14b5fb6

File tree

3 files changed

+648
-1
lines changed

3 files changed

+648
-1
lines changed

Doxyfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,8 @@ RECURSIVE = YES
10081008
# Note that relative paths are relative to the directory from which doxygen is
10091009
# run.
10101010

1011-
EXCLUDE =
1011+
EXCLUDE = src/tint/tint_gdb.py \
1012+
src/tint/tint_lldb.py
10121013

10131014
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
10141015
# directories that are symbolic links (a Unix file system feature) are excluded

src/tint/tint_gdb.py

+249
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Copyright 2022 The Tint Authors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Pretty printers for the Tint project.
16+
# Add a line to your ~/.gdbinit to source this file, e.g.:
17+
#
18+
# source /path/to/dawn/src/tint/tint_gdb.py
19+
20+
import gdb
21+
import gdb.printing
22+
from itertools import chain
23+
24+
# When debugging this module, set _DEBUGGING = True so that re-sourcing this file in gdb replaces
25+
# the existing printers.
26+
_DEBUGGING = True
27+
28+
# Enable to display other data members along with child elements of compound data types (arrays, etc.).
29+
# This is useful in debuggers like VS Code that doesn't display the `to_string()` result in the watch window.
30+
# OTOH, it's less useful when using gdb/lldb's print command.
31+
_DISPLAY_MEMBERS_AS_CHILDREN = False
32+
33+
34+
# Tips for debugging using VS Code:
35+
# - Set a breakpoint where you can view the types you want to debug/write pretty printers for.
36+
# - Debug Console: source /path/to/dawn/src/tint/tint_gdb.py
37+
# - To execute Python code, in the Debug Console:
38+
# -exec python foo = gdb.parse_and_eval('map.set_')
39+
# -exec python v = (foo['slots_']['impl_']['slice']['data'] + 8).dereference()['value']
40+
#
41+
# - Useful docs:
42+
# Python API: https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API
43+
# Especially:
44+
# Types: https://sourceware.org/gdb/onlinedocs/gdb/Types-In-Python.html#Types-In-Python
45+
# Values: https://sourceware.org/gdb/onlinedocs/gdb/Values-From-Inferior.html#Values-From-Inferior
46+
47+
48+
pp_set = gdb.printing.RegexpCollectionPrettyPrinter("tint")
49+
50+
51+
class Printer(object):
52+
'''Base class for Printers'''
53+
54+
def __init__(self, val):
55+
self.val = val
56+
57+
def template_type(self, index):
58+
'''Returns template type at index'''
59+
return self.val.type.template_argument(index)
60+
61+
62+
class UtilsSlicePrinter(Printer):
63+
'''Printer for tint::utils::Slice<T>'''
64+
65+
def __init__(self, val):
66+
super(UtilsSlicePrinter, self).__init__(val)
67+
self.len = self.val['len']
68+
self.cap = self.val['cap']
69+
self.data = self.val['data']
70+
self.elem_type = self.data.type.target().unqualified()
71+
72+
def length(self):
73+
return self.len
74+
75+
def value_at(self, index):
76+
'''Returns array value at index'''
77+
return (self.data + index).dereference().cast(self.elem_type)
78+
79+
def to_string(self):
80+
return 'length={} capacity={}'.format(self.len, self.cap)
81+
82+
def members(self):
83+
if _DISPLAY_MEMBERS_AS_CHILDREN:
84+
return [
85+
('length', self.len),
86+
('capacity', self.cap),
87+
]
88+
else:
89+
return []
90+
91+
def children(self):
92+
for m in self.members():
93+
yield m
94+
for i in range(self.len):
95+
yield str(i), self.value_at(i)
96+
97+
def display_hint(self):
98+
return 'array'
99+
100+
101+
pp_set.add_printer('UtilsSlicePrinter',
102+
'^tint::utils::Slice<.*>$', UtilsSlicePrinter)
103+
104+
105+
class UtilsVectorPrinter(Printer):
106+
'''Printer for tint::utils::Vector<T, N>'''
107+
108+
def __init__(self, val):
109+
super(UtilsVectorPrinter, self).__init__(val)
110+
self.slice = self.val['impl_']['slice']
111+
self.using_heap = self.slice['cap'] > self.template_type(1)
112+
113+
def slice_printer(self):
114+
return UtilsSlicePrinter(self.slice)
115+
116+
def to_string(self):
117+
return 'heap={} {}'.format(self.using_heap, self.slice)
118+
119+
def members(self):
120+
if _DISPLAY_MEMBERS_AS_CHILDREN:
121+
return [
122+
('heap', self.using_heap),
123+
]
124+
else:
125+
return []
126+
127+
def children(self):
128+
return chain(self.members(), self.slice_printer().children())
129+
130+
def display_hint(self):
131+
return 'array'
132+
133+
134+
pp_set.add_printer(
135+
'UtilsVector', '^tint::utils::Vector<.*>$', UtilsVectorPrinter)
136+
137+
138+
class UtilsVectorRefPrinter(Printer):
139+
'''Printer for tint::utils::VectorRef<T>'''
140+
141+
def __init__(self, val):
142+
super(UtilsVectorRefPrinter, self).__init__(val)
143+
self.slice = self.val['slice_']
144+
self.can_move = self.val['can_move_']
145+
146+
def to_string(self):
147+
return 'can_move={} {}'.format(self.can_move, self.slice)
148+
149+
def members(self):
150+
if _DISPLAY_MEMBERS_AS_CHILDREN:
151+
return [
152+
('can_move', self.can_move),
153+
]
154+
else:
155+
return []
156+
157+
def children(self):
158+
return chain(self.members(), UtilsSlicePrinter(self.slice).children())
159+
160+
def display_hint(self):
161+
return 'array'
162+
163+
164+
pp_set.add_printer(
165+
'UtilsVector', '^tint::utils::VectorRef<.*>$', UtilsVectorRefPrinter)
166+
167+
168+
class UtilsHashsetPrinter(Printer):
169+
'''Printer for Hashset<T, N, HASH, EQUAL>'''
170+
171+
def __init__(self, val):
172+
super(UtilsHashsetPrinter, self).__init__(val)
173+
self.slice = UtilsVectorPrinter(self.val['slots_']).slice_printer()
174+
self.try_read_std_optional_func = self.try_read_std_optional
175+
176+
def to_string(self):
177+
length = 0
178+
for slot in range(0, self.slice.length()):
179+
v = self.slice.value_at(slot)
180+
if v['hash'] != 0:
181+
length += 1
182+
return 'length={}'.format(length)
183+
184+
def children(self):
185+
for slot in range(0, self.slice.length()):
186+
v = self.slice.value_at(slot)
187+
if v['hash'] != 0:
188+
value = v['value']
189+
190+
# value is a std::optional, let's try to extract its value for display
191+
kvp = self.try_read_std_optional_func(slot, value)
192+
if kvp is None:
193+
# If we failed, just output the slot and value as is, which will use
194+
# the default visualizer for each.
195+
kvp = slot, value
196+
197+
yield str(kvp[0]), kvp[1]
198+
199+
def display_hint(self):
200+
return 'array'
201+
202+
def try_read_std_optional(self, slot, value):
203+
try:
204+
# libstdc++
205+
v = value['_M_payload']['_M_payload']['_M_value']
206+
return slot, v
207+
# return str(kvp['key']), kvp['value']
208+
except:
209+
return None
210+
211+
212+
pp_set.add_printer(
213+
'UtilsHashset', '^tint::utils::Hashset<.*>$', UtilsHashsetPrinter)
214+
215+
216+
class UtilsHashmapPrinter(Printer):
217+
'''Printer for Hashmap<K, V, N, HASH, EQUAL>'''
218+
219+
def __init__(self, val):
220+
super(UtilsHashmapPrinter, self).__init__(val)
221+
self.hash_set = UtilsHashsetPrinter(self.val['set_'])
222+
# Replace the lookup function so we can extract the key and value out of the std::optionals in the Hashset
223+
self.hash_set.try_read_std_optional_func = self.try_read_std_optional
224+
225+
def to_string(self):
226+
return self.hash_set.to_string()
227+
228+
def children(self):
229+
return self.hash_set.children()
230+
231+
def display_hint(self):
232+
return 'array'
233+
234+
def try_read_std_optional(self, slot, value):
235+
try:
236+
# libstdc++
237+
kvp = value['_M_payload']['_M_payload']['_M_value']
238+
return str(kvp['key']), kvp['value']
239+
except:
240+
pass
241+
# Failed, fall back on hash_set
242+
return self.hash_set.try_read_std_optional(slot, value)
243+
244+
245+
pp_set.add_printer(
246+
'UtilsHashmap', '^tint::utils::Hashmap<.*>$', UtilsHashmapPrinter)
247+
248+
249+
gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)

0 commit comments

Comments
 (0)