]>
Commit | Line | Data |
---|---|---|
7b51bc51 | 1 | # Pretty-printer utilities. |
d01e8234 | 2 | # Copyright (C) 2010-2025 Free Software Foundation, Inc. |
7b51bc51 DE |
3 | |
4 | # This program is free software; you can redistribute it and/or modify | |
5 | # it under the terms of the GNU General Public License as published by | |
6 | # the Free Software Foundation; either version 3 of the License, or | |
7 | # (at your option) any later version. | |
8 | # | |
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. | |
13 | # | |
14 | # You should have received a copy of the GNU General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | """Utilities for working with pretty-printers.""" | |
18 | ||
708cedb7 | 19 | import itertools |
7b51bc51 DE |
20 | import re |
21 | ||
c2cf30e7 TT |
22 | import gdb |
23 | import gdb.types | |
24 | ||
13123da8 | 25 | |
7b51bc51 DE |
26 | class PrettyPrinter(object): |
27 | """A basic pretty-printer. | |
28 | ||
29 | Attributes: | |
30 | name: A unique string among all printers for the context in which | |
690a4937 DE |
31 | it is defined (objfile, progspace, or global(gdb)), and should |
32 | meaningfully describe what can be pretty-printed. | |
33 | E.g., "StringPiece" or "protobufs". | |
7b51bc51 | 34 | subprinters: An iterable object with each element having a `name' |
690a4937 DE |
35 | attribute, and, potentially, "enabled" attribute. |
36 | Or this is None if there are no subprinters. | |
7b51bc51 DE |
37 | enabled: A boolean indicating if the printer is enabled. |
38 | ||
39 | Subprinters are for situations where "one" pretty-printer is actually a | |
40 | collection of several printers. E.g., The libstdc++ pretty-printer has | |
41 | a pretty-printer for each of several different types, based on regexps. | |
42 | """ | |
43 | ||
44 | # While one might want to push subprinters into the subclass, it's | |
45 | # present here to formalize such support to simplify | |
46 | # commands/pretty_printers.py. | |
47 | ||
48 | def __init__(self, name, subprinters=None): | |
49 | self.name = name | |
50 | self.subprinters = subprinters | |
51 | self.enabled = True | |
52 | ||
53 | def __call__(self, val): | |
54 | # The subclass must define this. | |
55 | raise NotImplementedError("PrettyPrinter __call__") | |
56 | ||
57 | ||
58 | class SubPrettyPrinter(object): | |
59 | """Baseclass for sub-pretty-printers. | |
60 | ||
61 | Sub-pretty-printers needn't use this, but it formalizes what's needed. | |
62 | ||
63 | Attributes: | |
64 | name: The name of the subprinter. | |
65 | enabled: A boolean indicating if the subprinter is enabled. | |
66 | """ | |
67 | ||
68 | def __init__(self, name): | |
69 | self.name = name | |
70 | self.enabled = True | |
71 | ||
72 | ||
1fa57852 | 73 | def register_pretty_printer(obj, printer, replace=False): |
7b51bc51 DE |
74 | """Register pretty-printer PRINTER with OBJ. |
75 | ||
76 | The printer is added to the front of the search list, thus one can override | |
690a4937 DE |
77 | an existing printer if one needs to. Use a different name when overriding |
78 | an existing printer, otherwise an exception will be raised; multiple | |
79 | printers with the same name are disallowed. | |
7b51bc51 DE |
80 | |
81 | Arguments: | |
82 | obj: Either an objfile, progspace, or None (in which case the printer | |
690a4937 | 83 | is registered globally). |
7b51bc51 | 84 | printer: Either a function of one argument (old way) or any object |
690a4937 | 85 | which has attributes: name, enabled, __call__. |
1fa57852 DE |
86 | replace: If True replace any existing copy of the printer. |
87 | Otherwise if the printer already exists raise an exception. | |
7b51bc51 DE |
88 | |
89 | Returns: | |
90 | Nothing. | |
91 | ||
92 | Raises: | |
93 | TypeError: A problem with the type of the printer. | |
4e04c971 | 94 | ValueError: The printer's name contains a semicolon ";". |
6d64e6d4 | 95 | RuntimeError: A printer with the same name is already registered. |
7b51bc51 DE |
96 | |
97 | If the caller wants the printer to be listable and disableable, it must | |
98 | follow the PrettyPrinter API. This applies to the old way (functions) too. | |
99 | If printer is an object, __call__ is a method of two arguments: | |
100 | self, and the value to be pretty-printed. See PrettyPrinter. | |
101 | """ | |
102 | ||
103 | # Watch for both __name__ and name. | |
104 | # Functions get the former for free, but we don't want to use an | |
105 | # attribute named __foo__ for pretty-printers-as-objects. | |
106 | # If printer has both, we use `name'. | |
107 | if not hasattr(printer, "__name__") and not hasattr(printer, "name"): | |
108 | raise TypeError("printer missing attribute: name") | |
109 | if hasattr(printer, "name") and not hasattr(printer, "enabled"): | |
13123da8 | 110 | raise TypeError("printer missing attribute: enabled") |
7b51bc51 DE |
111 | if not hasattr(printer, "__call__"): |
112 | raise TypeError("printer missing attribute: __call__") | |
113 | ||
34f5f757 | 114 | if hasattr(printer, "name"): |
13123da8 | 115 | name = printer.name |
34f5f757 | 116 | else: |
13123da8 | 117 | name = printer.__name__ |
34f5f757 | 118 | if obj is None or obj is gdb: |
7b51bc51 DE |
119 | if gdb.parameter("verbose"): |
120 | gdb.write("Registering global %s pretty-printer ...\n" % name) | |
121 | obj = gdb | |
122 | else: | |
123 | if gdb.parameter("verbose"): | |
13123da8 SM |
124 | gdb.write( |
125 | "Registering %s pretty-printer for %s ...\n" % (name, obj.filename) | |
126 | ) | |
7b51bc51 | 127 | |
34f5f757 DE |
128 | # Printers implemented as functions are old-style. In order to not risk |
129 | # breaking anything we do not check __name__ here. | |
7b51bc51 | 130 | if hasattr(printer, "name"): |
edae3fd6 | 131 | if not isinstance(printer.name, str): |
7b51bc51 | 132 | raise TypeError("printer name is not a string") |
4e04c971 DE |
133 | # If printer provides a name, make sure it doesn't contain ";". |
134 | # Semicolon is used by the info/enable/disable pretty-printer commands | |
7b51bc51 | 135 | # to delimit subprinters. |
4e04c971 DE |
136 | if printer.name.find(";") >= 0: |
137 | raise ValueError("semicolon ';' in printer name") | |
7b51bc51 DE |
138 | # Also make sure the name is unique. |
139 | # Alas, we can't do the same for functions and __name__, they could | |
140 | # all have a canonical name like "lookup_function". | |
141 | # PERF: gdb records printers in a list, making this inefficient. | |
1fa57852 DE |
142 | i = 0 |
143 | for p in obj.pretty_printers: | |
144 | if hasattr(p, "name") and p.name == printer.name: | |
145 | if replace: | |
146 | del obj.pretty_printers[i] | |
147 | break | |
148 | else: | |
13123da8 SM |
149 | raise RuntimeError( |
150 | "pretty-printer already registered: %s" % printer.name | |
151 | ) | |
1fa57852 | 152 | i = i + 1 |
7b51bc51 DE |
153 | |
154 | obj.pretty_printers.insert(0, printer) | |
155 | ||
156 | ||
157 | class RegexpCollectionPrettyPrinter(PrettyPrinter): | |
158 | """Class for implementing a collection of regular-expression based pretty-printers. | |
159 | ||
160 | Intended usage: | |
161 | ||
162 | pretty_printer = RegexpCollectionPrettyPrinter("my_library") | |
163 | pretty_printer.add_printer("myclass1", "^myclass1$", MyClass1Printer) | |
164 | ... | |
165 | pretty_printer.add_printer("myclassN", "^myclassN$", MyClassNPrinter) | |
166 | register_pretty_printer(obj, pretty_printer) | |
167 | """ | |
168 | ||
169 | class RegexpSubprinter(SubPrettyPrinter): | |
170 | def __init__(self, name, regexp, gen_printer): | |
171 | super(RegexpCollectionPrettyPrinter.RegexpSubprinter, self).__init__(name) | |
172 | self.regexp = regexp | |
173 | self.gen_printer = gen_printer | |
174 | self.compiled_re = re.compile(regexp) | |
175 | ||
176 | def __init__(self, name): | |
177 | super(RegexpCollectionPrettyPrinter, self).__init__(name, []) | |
178 | ||
179 | def add_printer(self, name, regexp, gen_printer): | |
180 | """Add a printer to the list. | |
181 | ||
182 | The printer is added to the end of the list. | |
183 | ||
184 | Arguments: | |
185 | name: The name of the subprinter. | |
186 | regexp: The regular expression, as a string. | |
187 | gen_printer: A function/method that given a value returns an | |
690a4937 | 188 | object to pretty-print it. |
7b51bc51 DE |
189 | |
190 | Returns: | |
191 | Nothing. | |
192 | """ | |
193 | ||
194 | # NOTE: A previous version made the name of each printer the regexp. | |
195 | # That makes it awkward to pass to the enable/disable commands (it's | |
196 | # cumbersome to make a regexp of a regexp). So now the name is a | |
197 | # separate parameter. | |
198 | ||
13123da8 | 199 | self.subprinters.append(self.RegexpSubprinter(name, regexp, gen_printer)) |
7b51bc51 DE |
200 | |
201 | def __call__(self, val): | |
202 | """Lookup the pretty-printer for the provided value.""" | |
203 | ||
204 | # Get the type name. | |
205 | typename = gdb.types.get_basic_type(val.type).tag | |
1b588015 JB |
206 | if not typename: |
207 | typename = val.type.name | |
7b51bc51 DE |
208 | if not typename: |
209 | return None | |
210 | ||
211 | # Iterate over table of type regexps to determine | |
212 | # if a printer is registered for that type. | |
213 | # Return an instantiation of the printer if found. | |
214 | for printer in self.subprinters: | |
215 | if printer.enabled and printer.compiled_re.search(typename): | |
216 | return printer.gen_printer(val) | |
217 | ||
218 | # Cannot find a pretty printer. Return None. | |
219 | return None | |
cafec441 | 220 | |
13123da8 | 221 | |
cafec441 TT |
222 | # A helper class for printing enum types. This class is instantiated |
223 | # with a list of enumerators to print a particular Value. | |
fb282576 | 224 | class _EnumInstance(gdb.ValuePrinter): |
cafec441 | 225 | def __init__(self, enumerators, val): |
fb282576 TT |
226 | self.__enumerators = enumerators |
227 | self.__val = val | |
cafec441 TT |
228 | |
229 | def to_string(self): | |
230 | flag_list = [] | |
fb282576 | 231 | v = int(self.__val) |
cafec441 | 232 | any_found = False |
fb282576 | 233 | for e_name, e_value in self.__enumerators: |
cafec441 TT |
234 | if v & e_value != 0: |
235 | flag_list.append(e_name) | |
236 | v = v & ~e_value | |
237 | any_found = True | |
238 | if not any_found or v != 0: | |
239 | # Leftover value. | |
13123da8 | 240 | flag_list.append("<unknown: 0x%x>" % v) |
fb282576 | 241 | return "0x%x [%s]" % (int(self.__val), " | ".join(flag_list)) |
cafec441 | 242 | |
13123da8 | 243 | |
cafec441 TT |
244 | class FlagEnumerationPrinter(PrettyPrinter): |
245 | """A pretty-printer which can be used to print a flag-style enumeration. | |
246 | A flag-style enumeration is one where the enumerators are or'd | |
247 | together to create values. The new printer will print these | |
248 | symbolically using '|' notation. The printer must be registered | |
249 | manually. This printer is most useful when an enum is flag-like, | |
250 | but has some overlap. GDB's built-in printing will not handle | |
251 | this case, but this printer will attempt to.""" | |
252 | ||
253 | def __init__(self, enum_type): | |
254 | super(FlagEnumerationPrinter, self).__init__(enum_type) | |
255 | self.initialized = False | |
256 | ||
257 | def __call__(self, val): | |
258 | if not self.initialized: | |
259 | self.initialized = True | |
260 | flags = gdb.lookup_type(self.name) | |
261 | self.enumerators = [] | |
262 | for field in flags.fields(): | |
14e75d8e | 263 | self.enumerators.append((field.name, field.enumval)) |
cafec441 TT |
264 | # Sorting the enumerators by value usually does the right |
265 | # thing. | |
13123da8 | 266 | self.enumerators.sort(key=lambda x: x[1]) |
cafec441 TT |
267 | |
268 | if self.enabled: | |
269 | return _EnumInstance(self.enumerators, val) | |
270 | else: | |
271 | return None | |
6979730b DE |
272 | |
273 | ||
fb282576 | 274 | class NoOpScalarPrinter(gdb.ValuePrinter): |
8900a92e | 275 | """A no-op pretty printer that wraps a scalar value.""" |
06e8a3a9 | 276 | |
8900a92e | 277 | def __init__(self, value): |
fb282576 | 278 | self.__value = value |
8900a92e TT |
279 | |
280 | def to_string(self): | |
fb282576 | 281 | return self.__value.format_string(raw=True) |
8900a92e TT |
282 | |
283 | ||
4c2d19e3 TT |
284 | class NoOpStringPrinter(gdb.ValuePrinter): |
285 | """A no-op pretty printer that wraps a string value.""" | |
286 | ||
287 | def __init__(self, ty, value): | |
288 | self.__ty = ty | |
289 | self.__value = value | |
290 | ||
291 | def to_string(self): | |
292 | # We need some special cases here. | |
293 | # | |
294 | # * If the gdb.Value was created from a Python string, it will | |
295 | # be a non-lazy array -- but will have address 0 and so the | |
296 | # contents will be lost on conversion to lazy string. | |
297 | # (Weirdly, the .address attribute will not be 0 though.) | |
298 | # Since conversion to lazy string is to avoid fetching too | |
299 | # much data, and since the array is already non-lazy, just | |
300 | # return it. | |
301 | # | |
302 | # * To avoid weird printing for a C "string" that is just a | |
303 | # NULL pointer, special case this as well. | |
304 | # | |
305 | # * Lazy strings only understand arrays and pointers; other | |
306 | # string-like objects (like a Rust &str) should simply be | |
307 | # returned. | |
308 | code = self.__ty.code | |
309 | if code == gdb.TYPE_CODE_ARRAY and not self.__value.is_lazy: | |
310 | return self.__value | |
311 | elif code == gdb.TYPE_CODE_PTR and self.__value == 0: | |
312 | return self.__value | |
313 | elif code != gdb.TYPE_CODE_PTR and code != gdb.TYPE_CODE_ARRAY: | |
314 | return self.__value | |
315 | else: | |
316 | return self.__value.lazy_string() | |
317 | ||
318 | def display_hint(self): | |
319 | return "string" | |
320 | ||
321 | ||
fb282576 | 322 | class NoOpPointerReferencePrinter(gdb.ValuePrinter): |
a56e5dce TT |
323 | """A no-op pretty printer that wraps a pointer or reference.""" |
324 | ||
325 | def __init__(self, value): | |
fb282576 | 326 | self.__value = value |
a56e5dce TT |
327 | |
328 | def to_string(self): | |
fb282576 | 329 | return self.__value.format_string(deref_refs=False) |
a56e5dce | 330 | |
f35baff3 TT |
331 | def num_children(self): |
332 | return 1 | |
333 | ||
334 | def child(self, i): | |
335 | return "value", self.__value.referenced_value() | |
336 | ||
a56e5dce | 337 | def children(self): |
fb282576 | 338 | yield "value", self.__value.referenced_value() |
a56e5dce TT |
339 | |
340 | ||
fb282576 | 341 | class NoOpArrayPrinter(gdb.ValuePrinter): |
8900a92e | 342 | """A no-op pretty printer that wraps an array value.""" |
06e8a3a9 | 343 | |
c38bda51 | 344 | def __init__(self, ty, value): |
fb282576 | 345 | self.__value = value |
c38bda51 | 346 | (low, high) = ty.range() |
708cedb7 TT |
347 | # In Ada, an array can have an index type that is a |
348 | # non-contiguous enum. In this case the indexing must be done | |
349 | # by using the indices into the enum type, not the underlying | |
350 | # integer values. | |
351 | range_type = ty.fields()[0].type | |
352 | if range_type.target().code == gdb.TYPE_CODE_ENUM: | |
353 | e_values = range_type.target().fields() | |
354 | # Drop any values before LOW. | |
355 | e_values = itertools.dropwhile(lambda x: x.enumval < low, e_values) | |
356 | # Drop any values after HIGH. | |
357 | e_values = itertools.takewhile(lambda x: x.enumval <= high, e_values) | |
358 | low = 0 | |
359 | high = len(list(e_values)) - 1 | |
fb282576 TT |
360 | self.__low = low |
361 | self.__high = high | |
8900a92e TT |
362 | |
363 | def to_string(self): | |
364 | return "" | |
365 | ||
366 | def display_hint(self): | |
367 | return "array" | |
368 | ||
f35baff3 TT |
369 | def num_children(self): |
370 | return self.__high - self.__low + 1 | |
371 | ||
372 | def child(self, i): | |
373 | return (self.__low + i, self.__value[self.__low + i]) | |
374 | ||
8900a92e | 375 | def children(self): |
fb282576 TT |
376 | for i in range(self.__low, self.__high + 1): |
377 | yield (i, self.__value[i]) | |
8900a92e TT |
378 | |
379 | ||
fb282576 | 380 | class NoOpStructPrinter(gdb.ValuePrinter): |
8900a92e | 381 | """A no-op pretty printer that wraps a struct or union value.""" |
06e8a3a9 | 382 | |
c38bda51 | 383 | def __init__(self, ty, value): |
fb282576 TT |
384 | self.__ty = ty |
385 | self.__value = value | |
8900a92e TT |
386 | |
387 | def to_string(self): | |
388 | return "" | |
389 | ||
390 | def children(self): | |
fb282576 | 391 | for field in self.__ty.fields(): |
4a1b9a4b | 392 | if hasattr(field, "bitpos") and field.name is not None: |
fb282576 | 393 | yield (field.name, self.__value[field]) |
8900a92e TT |
394 | |
395 | ||
396 | def make_visualizer(value): | |
397 | """Given a gdb.Value, wrap it in a pretty-printer. | |
398 | ||
399 | If a pretty-printer is found by the usual means, it is returned. | |
400 | Otherwise, VALUE will be wrapped in a no-op visualizer.""" | |
401 | ||
402 | result = gdb.default_visualizer(value) | |
403 | if result is not None: | |
404 | # Found a pretty-printer. | |
405 | pass | |
8900a92e | 406 | else: |
c38bda51 | 407 | ty = value.type.strip_typedefs() |
708cedb7 | 408 | if ty.is_string_like: |
4c2d19e3 | 409 | result = NoOpStringPrinter(ty, value) |
708cedb7 | 410 | elif ty.code == gdb.TYPE_CODE_ARRAY: |
54e05387 | 411 | result = NoOpArrayPrinter(ty, value) |
708cedb7 TT |
412 | elif ty.is_array_like: |
413 | value = value.to_array() | |
414 | ty = value.type.strip_typedefs() | |
54e05387 | 415 | result = NoOpArrayPrinter(ty, value) |
c38bda51 | 416 | elif ty.code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION): |
54e05387 | 417 | result = NoOpStructPrinter(ty, value) |
4abf53c9 TT |
418 | elif ty.code in ( |
419 | gdb.TYPE_CODE_PTR, | |
420 | gdb.TYPE_CODE_REF, | |
421 | gdb.TYPE_CODE_RVALUE_REF, | |
422 | ): | |
a56e5dce | 423 | result = NoOpPointerReferencePrinter(value) |
c38bda51 | 424 | else: |
54e05387 | 425 | result = NoOpScalarPrinter(value) |
8900a92e TT |
426 | return result |
427 | ||
428 | ||
6979730b DE |
429 | # Builtin pretty-printers. |
430 | # The set is defined as empty, and files in printing/*.py add their printers | |
431 | # to this with add_builtin_pretty_printer. | |
432 | ||
433 | _builtin_pretty_printers = RegexpCollectionPrettyPrinter("builtin") | |
434 | ||
435 | register_pretty_printer(None, _builtin_pretty_printers) | |
436 | ||
437 | # Add a builtin pretty-printer. | |
438 | ||
13123da8 | 439 | |
6979730b DE |
440 | def add_builtin_pretty_printer(name, regexp, printer): |
441 | _builtin_pretty_printers.add_printer(name, regexp, printer) |