Commit | Line | Data |
---|---|---|
7b51bc51 | 1 | # Pretty-printer utilities. |
88b9d363 | 2 | # Copyright (C) 2010-2022 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 | ||
19 | import gdb | |
20 | import gdb.types | |
21 | import re | |
9a27f2c6 | 22 | import sys |
7b51bc51 | 23 | |
9a27f2c6 PK |
24 | if sys.version_info[0] > 2: |
25 | # Python 3 removed basestring and long | |
26 | basestring = str | |
27 | long = int | |
7b51bc51 | 28 | |
13123da8 | 29 | |
7b51bc51 DE |
30 | class PrettyPrinter(object): |
31 | """A basic pretty-printer. | |
32 | ||
33 | Attributes: | |
34 | name: A unique string among all printers for the context in which | |
690a4937 DE |
35 | it is defined (objfile, progspace, or global(gdb)), and should |
36 | meaningfully describe what can be pretty-printed. | |
37 | E.g., "StringPiece" or "protobufs". | |
7b51bc51 | 38 | subprinters: An iterable object with each element having a `name' |
690a4937 DE |
39 | attribute, and, potentially, "enabled" attribute. |
40 | Or this is None if there are no subprinters. | |
7b51bc51 DE |
41 | enabled: A boolean indicating if the printer is enabled. |
42 | ||
43 | Subprinters are for situations where "one" pretty-printer is actually a | |
44 | collection of several printers. E.g., The libstdc++ pretty-printer has | |
45 | a pretty-printer for each of several different types, based on regexps. | |
46 | """ | |
47 | ||
48 | # While one might want to push subprinters into the subclass, it's | |
49 | # present here to formalize such support to simplify | |
50 | # commands/pretty_printers.py. | |
51 | ||
52 | def __init__(self, name, subprinters=None): | |
53 | self.name = name | |
54 | self.subprinters = subprinters | |
55 | self.enabled = True | |
56 | ||
57 | def __call__(self, val): | |
58 | # The subclass must define this. | |
59 | raise NotImplementedError("PrettyPrinter __call__") | |
60 | ||
61 | ||
62 | class SubPrettyPrinter(object): | |
63 | """Baseclass for sub-pretty-printers. | |
64 | ||
65 | Sub-pretty-printers needn't use this, but it formalizes what's needed. | |
66 | ||
67 | Attributes: | |
68 | name: The name of the subprinter. | |
69 | enabled: A boolean indicating if the subprinter is enabled. | |
70 | """ | |
71 | ||
72 | def __init__(self, name): | |
73 | self.name = name | |
74 | self.enabled = True | |
75 | ||
76 | ||
1fa57852 | 77 | def register_pretty_printer(obj, printer, replace=False): |
7b51bc51 DE |
78 | """Register pretty-printer PRINTER with OBJ. |
79 | ||
80 | The printer is added to the front of the search list, thus one can override | |
690a4937 DE |
81 | an existing printer if one needs to. Use a different name when overriding |
82 | an existing printer, otherwise an exception will be raised; multiple | |
83 | printers with the same name are disallowed. | |
7b51bc51 DE |
84 | |
85 | Arguments: | |
86 | obj: Either an objfile, progspace, or None (in which case the printer | |
690a4937 | 87 | is registered globally). |
7b51bc51 | 88 | printer: Either a function of one argument (old way) or any object |
690a4937 | 89 | which has attributes: name, enabled, __call__. |
1fa57852 DE |
90 | replace: If True replace any existing copy of the printer. |
91 | Otherwise if the printer already exists raise an exception. | |
7b51bc51 DE |
92 | |
93 | Returns: | |
94 | Nothing. | |
95 | ||
96 | Raises: | |
97 | TypeError: A problem with the type of the printer. | |
4e04c971 | 98 | ValueError: The printer's name contains a semicolon ";". |
6d64e6d4 | 99 | RuntimeError: A printer with the same name is already registered. |
7b51bc51 DE |
100 | |
101 | If the caller wants the printer to be listable and disableable, it must | |
102 | follow the PrettyPrinter API. This applies to the old way (functions) too. | |
103 | If printer is an object, __call__ is a method of two arguments: | |
104 | self, and the value to be pretty-printed. See PrettyPrinter. | |
105 | """ | |
106 | ||
107 | # Watch for both __name__ and name. | |
108 | # Functions get the former for free, but we don't want to use an | |
109 | # attribute named __foo__ for pretty-printers-as-objects. | |
110 | # If printer has both, we use `name'. | |
111 | if not hasattr(printer, "__name__") and not hasattr(printer, "name"): | |
112 | raise TypeError("printer missing attribute: name") | |
113 | if hasattr(printer, "name") and not hasattr(printer, "enabled"): | |
13123da8 | 114 | raise TypeError("printer missing attribute: enabled") |
7b51bc51 DE |
115 | if not hasattr(printer, "__call__"): |
116 | raise TypeError("printer missing attribute: __call__") | |
117 | ||
34f5f757 | 118 | if hasattr(printer, "name"): |
13123da8 | 119 | name = printer.name |
34f5f757 | 120 | else: |
13123da8 | 121 | name = printer.__name__ |
34f5f757 | 122 | if obj is None or obj is gdb: |
7b51bc51 DE |
123 | if gdb.parameter("verbose"): |
124 | gdb.write("Registering global %s pretty-printer ...\n" % name) | |
125 | obj = gdb | |
126 | else: | |
127 | if gdb.parameter("verbose"): | |
13123da8 SM |
128 | gdb.write( |
129 | "Registering %s pretty-printer for %s ...\n" % (name, obj.filename) | |
130 | ) | |
7b51bc51 | 131 | |
34f5f757 DE |
132 | # Printers implemented as functions are old-style. In order to not risk |
133 | # breaking anything we do not check __name__ here. | |
7b51bc51 DE |
134 | if hasattr(printer, "name"): |
135 | if not isinstance(printer.name, basestring): | |
136 | raise TypeError("printer name is not a string") | |
4e04c971 DE |
137 | # If printer provides a name, make sure it doesn't contain ";". |
138 | # Semicolon is used by the info/enable/disable pretty-printer commands | |
7b51bc51 | 139 | # to delimit subprinters. |
4e04c971 DE |
140 | if printer.name.find(";") >= 0: |
141 | raise ValueError("semicolon ';' in printer name") | |
7b51bc51 DE |
142 | # Also make sure the name is unique. |
143 | # Alas, we can't do the same for functions and __name__, they could | |
144 | # all have a canonical name like "lookup_function". | |
145 | # PERF: gdb records printers in a list, making this inefficient. | |
1fa57852 DE |
146 | i = 0 |
147 | for p in obj.pretty_printers: | |
148 | if hasattr(p, "name") and p.name == printer.name: | |
149 | if replace: | |
150 | del obj.pretty_printers[i] | |
151 | break | |
152 | else: | |
13123da8 SM |
153 | raise RuntimeError( |
154 | "pretty-printer already registered: %s" % printer.name | |
155 | ) | |
1fa57852 | 156 | i = i + 1 |
7b51bc51 DE |
157 | |
158 | obj.pretty_printers.insert(0, printer) | |
159 | ||
160 | ||
161 | class RegexpCollectionPrettyPrinter(PrettyPrinter): | |
162 | """Class for implementing a collection of regular-expression based pretty-printers. | |
163 | ||
164 | Intended usage: | |
165 | ||
166 | pretty_printer = RegexpCollectionPrettyPrinter("my_library") | |
167 | pretty_printer.add_printer("myclass1", "^myclass1$", MyClass1Printer) | |
168 | ... | |
169 | pretty_printer.add_printer("myclassN", "^myclassN$", MyClassNPrinter) | |
170 | register_pretty_printer(obj, pretty_printer) | |
171 | """ | |
172 | ||
173 | class RegexpSubprinter(SubPrettyPrinter): | |
174 | def __init__(self, name, regexp, gen_printer): | |
175 | super(RegexpCollectionPrettyPrinter.RegexpSubprinter, self).__init__(name) | |
176 | self.regexp = regexp | |
177 | self.gen_printer = gen_printer | |
178 | self.compiled_re = re.compile(regexp) | |
179 | ||
180 | def __init__(self, name): | |
181 | super(RegexpCollectionPrettyPrinter, self).__init__(name, []) | |
182 | ||
183 | def add_printer(self, name, regexp, gen_printer): | |
184 | """Add a printer to the list. | |
185 | ||
186 | The printer is added to the end of the list. | |
187 | ||
188 | Arguments: | |
189 | name: The name of the subprinter. | |
190 | regexp: The regular expression, as a string. | |
191 | gen_printer: A function/method that given a value returns an | |
690a4937 | 192 | object to pretty-print it. |
7b51bc51 DE |
193 | |
194 | Returns: | |
195 | Nothing. | |
196 | """ | |
197 | ||
198 | # NOTE: A previous version made the name of each printer the regexp. | |
199 | # That makes it awkward to pass to the enable/disable commands (it's | |
200 | # cumbersome to make a regexp of a regexp). So now the name is a | |
201 | # separate parameter. | |
202 | ||
13123da8 | 203 | self.subprinters.append(self.RegexpSubprinter(name, regexp, gen_printer)) |
7b51bc51 DE |
204 | |
205 | def __call__(self, val): | |
206 | """Lookup the pretty-printer for the provided value.""" | |
207 | ||
208 | # Get the type name. | |
209 | typename = gdb.types.get_basic_type(val.type).tag | |
1b588015 JB |
210 | if not typename: |
211 | typename = val.type.name | |
7b51bc51 DE |
212 | if not typename: |
213 | return None | |
214 | ||
215 | # Iterate over table of type regexps to determine | |
216 | # if a printer is registered for that type. | |
217 | # Return an instantiation of the printer if found. | |
218 | for printer in self.subprinters: | |
219 | if printer.enabled and printer.compiled_re.search(typename): | |
220 | return printer.gen_printer(val) | |
221 | ||
222 | # Cannot find a pretty printer. Return None. | |
223 | return None | |
cafec441 | 224 | |
13123da8 | 225 | |
cafec441 TT |
226 | # A helper class for printing enum types. This class is instantiated |
227 | # with a list of enumerators to print a particular Value. | |
228 | class _EnumInstance: | |
229 | def __init__(self, enumerators, val): | |
230 | self.enumerators = enumerators | |
231 | self.val = val | |
232 | ||
233 | def to_string(self): | |
234 | flag_list = [] | |
235 | v = long(self.val) | |
236 | any_found = False | |
237 | for (e_name, e_value) in self.enumerators: | |
238 | if v & e_value != 0: | |
239 | flag_list.append(e_name) | |
240 | v = v & ~e_value | |
241 | any_found = True | |
242 | if not any_found or v != 0: | |
243 | # Leftover value. | |
13123da8 | 244 | flag_list.append("<unknown: 0x%x>" % v) |
10e3ed90 | 245 | return "0x%x [%s]" % (int(self.val), " | ".join(flag_list)) |
cafec441 | 246 | |
13123da8 | 247 | |
cafec441 TT |
248 | class FlagEnumerationPrinter(PrettyPrinter): |
249 | """A pretty-printer which can be used to print a flag-style enumeration. | |
250 | A flag-style enumeration is one where the enumerators are or'd | |
251 | together to create values. The new printer will print these | |
252 | symbolically using '|' notation. The printer must be registered | |
253 | manually. This printer is most useful when an enum is flag-like, | |
254 | but has some overlap. GDB's built-in printing will not handle | |
255 | this case, but this printer will attempt to.""" | |
256 | ||
257 | def __init__(self, enum_type): | |
258 | super(FlagEnumerationPrinter, self).__init__(enum_type) | |
259 | self.initialized = False | |
260 | ||
261 | def __call__(self, val): | |
262 | if not self.initialized: | |
263 | self.initialized = True | |
264 | flags = gdb.lookup_type(self.name) | |
265 | self.enumerators = [] | |
266 | for field in flags.fields(): | |
14e75d8e | 267 | self.enumerators.append((field.name, field.enumval)) |
cafec441 TT |
268 | # Sorting the enumerators by value usually does the right |
269 | # thing. | |
13123da8 | 270 | self.enumerators.sort(key=lambda x: x[1]) |
cafec441 TT |
271 | |
272 | if self.enabled: | |
273 | return _EnumInstance(self.enumerators, val) | |
274 | else: | |
275 | return None | |
6979730b DE |
276 | |
277 | ||
278 | # Builtin pretty-printers. | |
279 | # The set is defined as empty, and files in printing/*.py add their printers | |
280 | # to this with add_builtin_pretty_printer. | |
281 | ||
282 | _builtin_pretty_printers = RegexpCollectionPrettyPrinter("builtin") | |
283 | ||
284 | register_pretty_printer(None, _builtin_pretty_printers) | |
285 | ||
286 | # Add a builtin pretty-printer. | |
287 | ||
13123da8 | 288 | |
6979730b DE |
289 | def add_builtin_pretty_printer(name, regexp, printer): |
290 | _builtin_pretty_printers.add_printer(name, regexp, printer) |