]>
Commit | Line | Data |
---|---|---|
213516ef | 1 | # Copyright (C) 2009-2023 Free Software Foundation, Inc. |
737a160e JB |
2 | # |
3 | # This file is part of GDB. | |
4 | # | |
5 | # This program is free software; you can redistribute it and/or modify | |
6 | # it under the terms of the GNU General Public License as published by | |
7 | # the Free Software Foundation; either version 3 of the License, or | |
8 | # (at your option) any later version. | |
9 | # | |
10 | # This program is distributed in the hope that it will be useful, | |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | # GNU General Public License for more details. | |
14 | # | |
15 | # You should have received a copy of the GNU General Public License | |
16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | ||
18 | import gdb | |
19 | import os.path | |
20 | ||
e76f78a0 | 21 | |
737a160e JB |
22 | class TypeFlag: |
23 | """A class that allows us to store a flag name, its short name, | |
24 | and its value. | |
25 | ||
26 | In the GDB sources, struct type has a component called instance_flags | |
2fe842e5 | 27 | in which the value is the addition of various flags. These flags are |
a9ff5f12 UW |
28 | defined by the enumerates type_instance_flag_value. This class helps us |
29 | recreate a list with all these flags that is easy to manipulate and sort. | |
30 | Because all flag names start with TYPE_INSTANCE_FLAG_, a short_name | |
31 | attribute is provided that strips this prefix. | |
737a160e JB |
32 | |
33 | ATTRIBUTES | |
a9ff5f12 | 34 | name: The enumeration name (eg: "TYPE_INSTANCE_FLAG_CONST"). |
737a160e JB |
35 | value: The associated value. |
36 | short_name: The enumeration name, with the suffix stripped. | |
37 | """ | |
e76f78a0 | 38 | |
737a160e JB |
39 | def __init__(self, name, value): |
40 | self.name = name | |
41 | self.value = value | |
91e159e9 | 42 | self.short_name = name.replace("TYPE_INSTANCE_FLAG_", "") |
68ad5fb9 SM |
43 | |
44 | def __lt__(self, other): | |
737a160e | 45 | """Sort by value order.""" |
68ad5fb9 | 46 | return self.value < other.value |
e76f78a0 | 47 | |
737a160e | 48 | |
a9ff5f12 UW |
49 | # A list of all existing TYPE_INSTANCE_FLAGS_* enumerations, |
50 | # stored as TypeFlags objects. Lazy-initialized. | |
737a160e JB |
51 | TYPE_FLAGS = None |
52 | ||
e76f78a0 | 53 | |
737a160e JB |
54 | class TypeFlagsPrinter: |
55 | """A class that prints a decoded form of an instance_flags value. | |
56 | ||
57 | This class uses a global named TYPE_FLAGS, which is a list of | |
58 | all defined TypeFlag values. Using a global allows us to compute | |
59 | this list only once. | |
60 | ||
61 | This class relies on a couple of enumeration types being defined. | |
62 | If not, then printing of the instance_flag is going to be degraded, | |
63 | but it's not a fatal error. | |
64 | """ | |
e76f78a0 | 65 | |
737a160e JB |
66 | def __init__(self, val): |
67 | self.val = val | |
e76f78a0 | 68 | |
737a160e JB |
69 | def __str__(self): |
70 | global TYPE_FLAGS | |
71 | if TYPE_FLAGS is None: | |
72 | self.init_TYPE_FLAGS() | |
73 | if not self.val: | |
74 | return "0" | |
75 | if TYPE_FLAGS: | |
91e159e9 SM |
76 | flag_list = [ |
77 | flag.short_name for flag in TYPE_FLAGS if self.val & flag.value | |
78 | ] | |
737a160e JB |
79 | else: |
80 | flag_list = ["???"] | |
81 | return "0x%x [%s]" % (self.val, "|".join(flag_list)) | |
e76f78a0 | 82 | |
737a160e JB |
83 | def init_TYPE_FLAGS(self): |
84 | """Initialize the TYPE_FLAGS global as a list of TypeFlag objects. | |
85 | This operation requires the search of a couple of enumeration types. | |
86 | If not found, a warning is printed on stdout, and TYPE_FLAGS is | |
87 | set to the empty list. | |
88 | ||
89 | The resulting list is sorted by increasing value, to facilitate | |
90 | printing of the list of flags used in an instance_flags value. | |
91 | """ | |
92 | global TYPE_FLAGS | |
93 | TYPE_FLAGS = [] | |
737a160e JB |
94 | try: |
95 | iflags = gdb.lookup_type("enum type_instance_flag_value") | |
96 | except: | |
ac46107c JK |
97 | print("Warning: Cannot find enum type_instance_flag_value type.") |
98 | print(" `struct type' pretty-printer will be degraded") | |
737a160e | 99 | return |
91e159e9 | 100 | TYPE_FLAGS = [TypeFlag(field.name, field.enumval) for field in iflags.fields()] |
737a160e JB |
101 | TYPE_FLAGS.sort() |
102 | ||
e76f78a0 | 103 | |
737a160e JB |
104 | class StructTypePrettyPrinter: |
105 | """Pretty-print an object of type struct type""" | |
e76f78a0 | 106 | |
737a160e JB |
107 | def __init__(self, val): |
108 | self.val = val | |
e76f78a0 | 109 | |
737a160e JB |
110 | def to_string(self): |
111 | fields = [] | |
91e159e9 SM |
112 | fields.append("pointer_type = %s" % self.val["pointer_type"]) |
113 | fields.append("reference_type = %s" % self.val["reference_type"]) | |
114 | fields.append("chain = %s" % self.val["reference_type"]) | |
115 | fields.append( | |
116 | "instance_flags = %s" % TypeFlagsPrinter(self.val["m_instance_flags"]) | |
117 | ) | |
be40200b | 118 | fields.append("length = %d" % self.val["m_length"]) |
91e159e9 | 119 | fields.append("main_type = %s" % self.val["main_type"]) |
737a160e JB |
120 | return "\n{" + ",\n ".join(fields) + "}" |
121 | ||
e76f78a0 | 122 | |
737a160e JB |
123 | class StructMainTypePrettyPrinter: |
124 | """Pretty-print an objet of type main_type""" | |
e76f78a0 | 125 | |
737a160e JB |
126 | def __init__(self, val): |
127 | self.val = val | |
e76f78a0 | 128 | |
737a160e JB |
129 | def flags_to_string(self): |
130 | """struct main_type contains a series of components that | |
131 | are one-bit ints whose name start with "flag_". For instance: | |
132 | flag_unsigned, flag_stub, etc. In essence, these components are | |
133 | really boolean flags, and this method prints a short synthetic | |
134 | version of the value of all these flags. For instance, if | |
135 | flag_unsigned and flag_static are the only components set to 1, | |
136 | this function will return "unsigned|static". | |
137 | """ | |
91e159e9 SM |
138 | fields = [ |
139 | field.name.replace("flag_", "") | |
140 | for field in self.val.type.fields() | |
141 | if field.name.startswith("flag_") and self.val[field.name] | |
142 | ] | |
737a160e | 143 | return "|".join(fields) |
e76f78a0 | 144 | |
737a160e | 145 | def owner_to_string(self): |
91e159e9 SM |
146 | """Return an image of component "owner".""" |
147 | if self.val["m_flag_objfile_owned"] != 0: | |
148 | return "%s (objfile)" % self.val["m_owner"]["objfile"] | |
737a160e | 149 | else: |
91e159e9 | 150 | return "%s (gdbarch)" % self.val["m_owner"]["gdbarch"] |
e76f78a0 | 151 | |
737a160e JB |
152 | def struct_field_location_img(self, field_val): |
153 | """Return an image of the loc component inside the given field | |
154 | gdb.Value. | |
155 | """ | |
7eb1526a AB |
156 | loc_val = field_val["m_loc"] |
157 | loc_kind = str(field_val["m_loc_kind"]) | |
737a160e | 158 | if loc_kind == "FIELD_LOC_KIND_BITPOS": |
91e159e9 | 159 | return "bitpos = %d" % loc_val["bitpos"] |
14e75d8e | 160 | elif loc_kind == "FIELD_LOC_KIND_ENUMVAL": |
91e159e9 | 161 | return "enumval = %d" % loc_val["enumval"] |
737a160e | 162 | elif loc_kind == "FIELD_LOC_KIND_PHYSADDR": |
91e159e9 | 163 | return "physaddr = 0x%x" % loc_val["physaddr"] |
737a160e | 164 | elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": |
91e159e9 | 165 | return "physname = %s" % loc_val["physname"] |
8e3b41a9 | 166 | elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": |
91e159e9 | 167 | return "dwarf_block = %s" % loc_val["dwarf_block"] |
737a160e | 168 | else: |
7eb1526a | 169 | return "m_loc = ??? (unsupported m_loc_kind value)" |
e76f78a0 | 170 | |
737a160e | 171 | def struct_field_img(self, fieldno): |
91e159e9 SM |
172 | """Return an image of the main_type field number FIELDNO.""" |
173 | f = self.val["flds_bnds"]["fields"][fieldno] | |
f4859d94 | 174 | label = "flds_bnds.fields[%d]:" % fieldno |
91e159e9 | 175 | if f["artificial"]: |
737a160e JB |
176 | label += " (artificial)" |
177 | fields = [] | |
7eb1526a AB |
178 | fields.append("m_name = %s" % f["m_name"]) |
179 | fields.append("m_type = %s" % f["m_type"]) | |
180 | fields.append("m_loc_kind = %s" % f["m_loc_kind"]) | |
91e159e9 | 181 | fields.append("bitsize = %d" % f["bitsize"]) |
737a160e JB |
182 | fields.append(self.struct_field_location_img(f)) |
183 | return label + "\n" + " {" + ",\n ".join(fields) + "}" | |
e76f78a0 | 184 | |
7f5331a8 JB |
185 | def bound_img(self, bound_name): |
186 | """Return an image of the given main_type's bound.""" | |
91e159e9 | 187 | bounds = self.val["flds_bnds"]["bounds"].dereference() |
b66b4e65 | 188 | b = bounds[bound_name] |
91e159e9 SM |
189 | bnd_kind = str(b["m_kind"]) |
190 | if bnd_kind == "PROP_CONST": | |
191 | return str(b["m_data"]["const_val"]) | |
192 | elif bnd_kind == "PROP_UNDEFINED": | |
193 | return "(undefined)" | |
7f5331a8 JB |
194 | else: |
195 | info = [bnd_kind] | |
91e159e9 SM |
196 | if bound_name == "high" and bounds["flag_upper_bound_is_count"]: |
197 | info.append("upper_bound_is_count") | |
198 | return "{} ({})".format(str(b["m_data"]["baton"]), ",".join(info)) | |
7f5331a8 | 199 | |
737a160e | 200 | def bounds_img(self): |
91e159e9 SM |
201 | """Return an image of the main_type bounds.""" |
202 | b = self.val["flds_bnds"]["bounds"].dereference() | |
203 | low = self.bound_img("low") | |
204 | high = self.bound_img("high") | |
7f5331a8 JB |
205 | |
206 | img = "flds_bnds.bounds = {%s, %s}" % (low, high) | |
91e159e9 SM |
207 | if b["flag_bound_evaluated"]: |
208 | img += " [evaluated]" | |
7f5331a8 | 209 | return img |
e76f78a0 | 210 | |
c389c3dc JB |
211 | def type_specific_img(self): |
212 | """Return a string image of the main_type type_specific union. | |
213 | Only the relevant component of that union is printed (based on | |
214 | the value of the type_specific_kind field. | |
215 | """ | |
91e159e9 SM |
216 | type_specific_kind = str(self.val["type_specific_field"]) |
217 | type_specific = self.val["type_specific"] | |
c389c3dc | 218 | if type_specific_kind == "TYPE_SPECIFIC_NONE": |
91e159e9 | 219 | img = "type_specific_field = %s" % type_specific_kind |
c389c3dc | 220 | elif type_specific_kind == "TYPE_SPECIFIC_CPLUS_STUFF": |
91e159e9 | 221 | img = "cplus_stuff = %s" % type_specific["cplus_stuff"] |
c389c3dc | 222 | elif type_specific_kind == "TYPE_SPECIFIC_GNAT_STUFF": |
91e159e9 SM |
223 | img = ( |
224 | "gnat_stuff = {descriptive_type = %s}" | |
225 | % type_specific["gnat_stuff"]["descriptive_type"] | |
226 | ) | |
c389c3dc | 227 | elif type_specific_kind == "TYPE_SPECIFIC_FLOATFORMAT": |
91e159e9 | 228 | img = "floatformat[0..1] = %s" % type_specific["floatformat"] |
b6cdc2c1 | 229 | elif type_specific_kind == "TYPE_SPECIFIC_FUNC": |
91e159e9 SM |
230 | img = ( |
231 | "calling_convention = %d" | |
232 | % type_specific["func_stuff"]["calling_convention"] | |
233 | ) | |
b6cdc2c1 | 234 | # tail_call_list is not printed. |
09e2d7c7 | 235 | elif type_specific_kind == "TYPE_SPECIFIC_SELF_TYPE": |
91e159e9 | 236 | img = "self_type = %s" % type_specific["self_type"] |
09584414 JB |
237 | elif type_specific_kind == "TYPE_SPECIFIC_FIXED_POINT": |
238 | # The scaling factor is an opaque structure, so we cannot | |
239 | # decode its value from Python (not without insider knowledge). | |
91e159e9 SM |
240 | img = ( |
241 | "scaling_factor: <opaque> (call __gmpz_dump with " | |
242 | " _mp_num and _mp_den fields if needed)" | |
243 | ) | |
7eb1526a | 244 | elif type_specific_kind == "TYPE_SPECIFIC_INT": |
a547eaf1 SM |
245 | img = "int_stuff = { bit_size = %d, bit_offset = %d }" % ( |
246 | type_specific["int_stuff"]["bit_size"], | |
247 | type_specific["int_stuff"]["bit_offset"], | |
248 | ) | |
c389c3dc | 249 | else: |
91e159e9 | 250 | img = ( |
7eb1526a | 251 | "type_specific = ??? (unknown type_specific_kind: %s)" |
91e159e9 SM |
252 | % type_specific_kind |
253 | ) | |
c389c3dc JB |
254 | return img |
255 | ||
737a160e | 256 | def to_string(self): |
91e159e9 | 257 | """Return a pretty-printed image of our main_type.""" |
737a160e | 258 | fields = [] |
91e159e9 SM |
259 | fields.append("name = %s" % self.val["name"]) |
260 | fields.append("code = %s" % self.val["code"]) | |
737a160e JB |
261 | fields.append("flags = [%s]" % self.flags_to_string()) |
262 | fields.append("owner = %s" % self.owner_to_string()) | |
be40200b | 263 | fields.append("target_type = %s" % self.val["m_target_type"]) |
91e159e9 SM |
264 | if self.val["nfields"] > 0: |
265 | for fieldno in range(self.val["nfields"]): | |
737a160e | 266 | fields.append(self.struct_field_img(fieldno)) |
91e159e9 | 267 | if self.val["code"] == gdb.TYPE_CODE_RANGE: |
737a160e | 268 | fields.append(self.bounds_img()) |
c389c3dc JB |
269 | fields.append(self.type_specific_img()) |
270 | ||
737a160e JB |
271 | return "\n{" + ",\n ".join(fields) + "}" |
272 | ||
9a14af7b SM |
273 | |
274 | class CoreAddrPrettyPrinter: | |
275 | """Print CORE_ADDR values as hex.""" | |
276 | ||
277 | def __init__(self, val): | |
278 | self._val = val | |
279 | ||
280 | def to_string(self): | |
281 | return hex(int(self._val)) | |
282 | ||
283 | ||
bf809310 PA |
284 | class IntrusiveListPrinter: |
285 | """Print a struct intrusive_list.""" | |
286 | ||
287 | def __init__(self, val): | |
288 | self._val = val | |
289 | ||
290 | # Type of linked items. | |
291 | self._item_type = self._val.type.template_argument(0) | |
292 | self._node_ptr_type = gdb.lookup_type( | |
293 | "intrusive_list_node<{}>".format(self._item_type.tag) | |
294 | ).pointer() | |
295 | ||
296 | # Type of value -> node converter. | |
297 | self._conv_type = self._val.type.template_argument(1) | |
298 | ||
299 | if self._uses_member_node(): | |
300 | # The second template argument of intrusive_member_node is a member | |
301 | # pointer value. Its value is the offset of the node member in the | |
302 | # enclosing type. | |
303 | member_node_ptr = self._conv_type.template_argument(1) | |
304 | member_node_ptr = member_node_ptr.cast(gdb.lookup_type("int")) | |
305 | self._member_node_offset = int(member_node_ptr) | |
306 | ||
307 | # This is only needed in _as_node_ptr if using a member node. Look it | |
308 | # up here so we only do it once. | |
309 | self._char_ptr_type = gdb.lookup_type("char").pointer() | |
310 | ||
311 | def display_hint(self): | |
312 | return "array" | |
313 | ||
314 | def _uses_member_node(self): | |
315 | """Return True if the list items use a node as a member, False if | |
316 | they use a node as a base class. | |
317 | """ | |
318 | ||
319 | if self._conv_type.name.startswith("intrusive_member_node<"): | |
320 | return True | |
321 | elif self._conv_type.name.startswith("intrusive_base_node<"): | |
322 | return False | |
323 | else: | |
324 | raise RuntimeError( | |
325 | "Unexpected intrusive_list value -> node converter type: {}".format( | |
326 | self._conv_type.name | |
327 | ) | |
328 | ) | |
329 | ||
330 | def to_string(self): | |
331 | s = "intrusive list of {}".format(self._item_type) | |
332 | ||
333 | if self._uses_member_node(): | |
334 | node_member = self._conv_type.template_argument(1) | |
335 | s += ", linked through {}".format(node_member) | |
336 | ||
337 | return s | |
338 | ||
339 | def _as_node_ptr(self, elem_ptr): | |
340 | """Given ELEM_PTR, a pointer to a list element, return a pointer to the | |
341 | corresponding intrusive_list_node. | |
342 | """ | |
343 | ||
344 | assert elem_ptr.type.code == gdb.TYPE_CODE_PTR | |
345 | ||
346 | if self._uses_member_node(): | |
347 | # Node as a member: add the member node offset from to the element's | |
348 | # address to get the member node's address. | |
349 | elem_char_ptr = elem_ptr.cast(self._char_ptr_type) | |
350 | node_char_ptr = elem_char_ptr + self._member_node_offset | |
351 | return node_char_ptr.cast(self._node_ptr_type) | |
352 | else: | |
353 | # Node as a base: just casting from node pointer to item pointer | |
354 | # will adjust the pointer value. | |
355 | return elem_ptr.cast(self._node_ptr_type) | |
356 | ||
357 | def _children_generator(self): | |
358 | """Generator that yields one tuple per list item.""" | |
359 | ||
360 | elem_ptr = self._val["m_front"] | |
361 | idx = 0 | |
362 | while elem_ptr != 0: | |
363 | yield (str(idx), elem_ptr.dereference()) | |
364 | node_ptr = self._as_node_ptr(elem_ptr) | |
365 | elem_ptr = node_ptr["next"] | |
366 | idx += 1 | |
367 | ||
368 | def children(self): | |
369 | return self._children_generator() | |
370 | ||
371 | ||
737a160e JB |
372 | def type_lookup_function(val): |
373 | """A routine that returns the correct pretty printer for VAL | |
374 | if appropriate. Returns None otherwise. | |
375 | """ | |
bf809310 PA |
376 | tag = val.type.tag |
377 | name = val.type.name | |
378 | if tag == "type": | |
737a160e | 379 | return StructTypePrettyPrinter(val) |
bf809310 | 380 | elif tag == "main_type": |
737a160e | 381 | return StructMainTypePrettyPrinter(val) |
bf809310 | 382 | elif name == "CORE_ADDR": |
9a14af7b | 383 | return CoreAddrPrettyPrinter(val) |
bf809310 PA |
384 | elif tag is not None and tag.startswith("intrusive_list<"): |
385 | return IntrusiveListPrinter(val) | |
737a160e JB |
386 | return None |
387 | ||
e76f78a0 | 388 | |
737a160e | 389 | def register_pretty_printer(objfile): |
91e159e9 | 390 | """A routine to register a pretty-printer against the given OBJFILE.""" |
737a160e JB |
391 | objfile.pretty_printers.append(type_lookup_function) |
392 | ||
e76f78a0 | 393 | |
737a160e JB |
394 | if __name__ == "__main__": |
395 | if gdb.current_objfile() is not None: | |
396 | # This is the case where this script is being "auto-loaded" | |
397 | # for a given objfile. Register the pretty-printer for that | |
398 | # objfile. | |
399 | register_pretty_printer(gdb.current_objfile()) | |
400 | else: | |
401 | # We need to locate the objfile corresponding to the GDB | |
402 | # executable, and register the pretty-printer for that objfile. | |
403 | # FIXME: The condition used to match the objfile is too simplistic | |
404 | # and will not work on Windows. | |
405 | for objfile in gdb.objfiles(): | |
406 | if os.path.basename(objfile.filename) == "gdb": | |
407 | objfile.pretty_printers.append(type_lookup_function) |