| 1 | # Copyright (C) 2009-2020 Free Software Foundation, Inc. |
| 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 | |
| 21 | |
| 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 |
| 27 | in which the value is the addition of various flags. These flags are |
| 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. |
| 32 | |
| 33 | ATTRIBUTES |
| 34 | name: The enumeration name (eg: "TYPE_INSTANCE_FLAG_CONST"). |
| 35 | value: The associated value. |
| 36 | short_name: The enumeration name, with the suffix stripped. |
| 37 | """ |
| 38 | |
| 39 | def __init__(self, name, value): |
| 40 | self.name = name |
| 41 | self.value = value |
| 42 | self.short_name = name.replace("TYPE_INSTANCE_FLAG_", '') |
| 43 | |
| 44 | def __lt__(self, other): |
| 45 | """Sort by value order.""" |
| 46 | return self.value < other.value |
| 47 | |
| 48 | |
| 49 | # A list of all existing TYPE_INSTANCE_FLAGS_* enumerations, |
| 50 | # stored as TypeFlags objects. Lazy-initialized. |
| 51 | TYPE_FLAGS = None |
| 52 | |
| 53 | |
| 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 | """ |
| 65 | |
| 66 | def __init__(self, val): |
| 67 | self.val = val |
| 68 | |
| 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: |
| 76 | flag_list = [flag.short_name for flag in TYPE_FLAGS |
| 77 | if self.val & flag.value] |
| 78 | else: |
| 79 | flag_list = ["???"] |
| 80 | return "0x%x [%s]" % (self.val, "|".join(flag_list)) |
| 81 | |
| 82 | def init_TYPE_FLAGS(self): |
| 83 | """Initialize the TYPE_FLAGS global as a list of TypeFlag objects. |
| 84 | This operation requires the search of a couple of enumeration types. |
| 85 | If not found, a warning is printed on stdout, and TYPE_FLAGS is |
| 86 | set to the empty list. |
| 87 | |
| 88 | The resulting list is sorted by increasing value, to facilitate |
| 89 | printing of the list of flags used in an instance_flags value. |
| 90 | """ |
| 91 | global TYPE_FLAGS |
| 92 | TYPE_FLAGS = [] |
| 93 | try: |
| 94 | iflags = gdb.lookup_type("enum type_instance_flag_value") |
| 95 | except: |
| 96 | print("Warning: Cannot find enum type_instance_flag_value type.") |
| 97 | print(" `struct type' pretty-printer will be degraded") |
| 98 | return |
| 99 | TYPE_FLAGS = [TypeFlag(field.name, field.enumval) |
| 100 | for field in iflags.fields()] |
| 101 | TYPE_FLAGS.sort() |
| 102 | |
| 103 | |
| 104 | class StructTypePrettyPrinter: |
| 105 | """Pretty-print an object of type struct type""" |
| 106 | |
| 107 | def __init__(self, val): |
| 108 | self.val = val |
| 109 | |
| 110 | def to_string(self): |
| 111 | fields = [] |
| 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("instance_flags = %s" |
| 116 | % TypeFlagsPrinter(self.val['instance_flags'])) |
| 117 | fields.append("length = %d" % self.val['length']) |
| 118 | fields.append("main_type = %s" % self.val['main_type']) |
| 119 | return "\n{" + ",\n ".join(fields) + "}" |
| 120 | |
| 121 | |
| 122 | class StructMainTypePrettyPrinter: |
| 123 | """Pretty-print an objet of type main_type""" |
| 124 | |
| 125 | def __init__(self, val): |
| 126 | self.val = val |
| 127 | |
| 128 | def flags_to_string(self): |
| 129 | """struct main_type contains a series of components that |
| 130 | are one-bit ints whose name start with "flag_". For instance: |
| 131 | flag_unsigned, flag_stub, etc. In essence, these components are |
| 132 | really boolean flags, and this method prints a short synthetic |
| 133 | version of the value of all these flags. For instance, if |
| 134 | flag_unsigned and flag_static are the only components set to 1, |
| 135 | this function will return "unsigned|static". |
| 136 | """ |
| 137 | fields = [field.name.replace("flag_", "") |
| 138 | for field in self.val.type.fields() |
| 139 | if field.name.startswith("flag_") and self.val[field.name]] |
| 140 | return "|".join(fields) |
| 141 | |
| 142 | def owner_to_string(self): |
| 143 | """Return an image of component "owner". |
| 144 | """ |
| 145 | if self.val['flag_objfile_owned'] != 0: |
| 146 | return "%s (objfile)" % self.val['owner']['objfile'] |
| 147 | else: |
| 148 | return "%s (gdbarch)" % self.val['owner']['gdbarch'] |
| 149 | |
| 150 | def struct_field_location_img(self, field_val): |
| 151 | """Return an image of the loc component inside the given field |
| 152 | gdb.Value. |
| 153 | """ |
| 154 | loc_val = field_val['loc'] |
| 155 | loc_kind = str(field_val['loc_kind']) |
| 156 | if loc_kind == "FIELD_LOC_KIND_BITPOS": |
| 157 | return 'bitpos = %d' % loc_val['bitpos'] |
| 158 | elif loc_kind == "FIELD_LOC_KIND_ENUMVAL": |
| 159 | return 'enumval = %d' % loc_val['enumval'] |
| 160 | elif loc_kind == "FIELD_LOC_KIND_PHYSADDR": |
| 161 | return 'physaddr = 0x%x' % loc_val['physaddr'] |
| 162 | elif loc_kind == "FIELD_LOC_KIND_PHYSNAME": |
| 163 | return 'physname = %s' % loc_val['physname'] |
| 164 | elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK": |
| 165 | return 'dwarf_block = %s' % loc_val['dwarf_block'] |
| 166 | else: |
| 167 | return 'loc = ??? (unsupported loc_kind value)' |
| 168 | |
| 169 | def struct_field_img(self, fieldno): |
| 170 | """Return an image of the main_type field number FIELDNO. |
| 171 | """ |
| 172 | f = self.val['flds_bnds']['fields'][fieldno] |
| 173 | label = "flds_bnds.fields[%d]:" % fieldno |
| 174 | if f['artificial']: |
| 175 | label += " (artificial)" |
| 176 | fields = [] |
| 177 | fields.append("name = %s" % f['name']) |
| 178 | fields.append("type = %s" % f['type']) |
| 179 | fields.append("loc_kind = %s" % f['loc_kind']) |
| 180 | fields.append("bitsize = %d" % f['bitsize']) |
| 181 | fields.append(self.struct_field_location_img(f)) |
| 182 | return label + "\n" + " {" + ",\n ".join(fields) + "}" |
| 183 | |
| 184 | def bound_img(self, bound_name): |
| 185 | """Return an image of the given main_type's bound.""" |
| 186 | b = self.val['flds_bnds']['bounds'].dereference()[bound_name] |
| 187 | bnd_kind = str(b['kind']) |
| 188 | if bnd_kind == 'PROP_CONST': |
| 189 | return str(b['data']['const_val']) |
| 190 | elif bnd_kind == 'PROP_UNDEFINED': |
| 191 | return '(undefined)' |
| 192 | else: |
| 193 | info = [bnd_kind] |
| 194 | if bound_name == 'high' and b['flag_upper_bound_is_count']: |
| 195 | info.append('upper_bound_is_count') |
| 196 | return '{} ({})'.format(str(b['data']['baton']), ','.join(info)) |
| 197 | |
| 198 | def bounds_img(self): |
| 199 | """Return an image of the main_type bounds. |
| 200 | """ |
| 201 | b = self.val['flds_bnds']['bounds'].dereference() |
| 202 | low = self.bound_img('low') |
| 203 | high = self.bound_img('high') |
| 204 | |
| 205 | img = "flds_bnds.bounds = {%s, %s}" % (low, high) |
| 206 | if b['flag_bound_evaluated']: |
| 207 | img += ' [evaluated]' |
| 208 | return img |
| 209 | |
| 210 | def type_specific_img(self): |
| 211 | """Return a string image of the main_type type_specific union. |
| 212 | Only the relevant component of that union is printed (based on |
| 213 | the value of the type_specific_kind field. |
| 214 | """ |
| 215 | type_specific_kind = str(self.val['type_specific_field']) |
| 216 | type_specific = self.val['type_specific'] |
| 217 | if type_specific_kind == "TYPE_SPECIFIC_NONE": |
| 218 | img = 'type_specific_field = %s' % type_specific_kind |
| 219 | elif type_specific_kind == "TYPE_SPECIFIC_CPLUS_STUFF": |
| 220 | img = "cplus_stuff = %s" % type_specific['cplus_stuff'] |
| 221 | elif type_specific_kind == "TYPE_SPECIFIC_GNAT_STUFF": |
| 222 | img = ("gnat_stuff = {descriptive_type = %s}" |
| 223 | % type_specific['gnat_stuff']['descriptive_type']) |
| 224 | elif type_specific_kind == "TYPE_SPECIFIC_FLOATFORMAT": |
| 225 | img = "floatformat[0..1] = %s" % type_specific['floatformat'] |
| 226 | elif type_specific_kind == "TYPE_SPECIFIC_FUNC": |
| 227 | img = ("calling_convention = %d" |
| 228 | % type_specific['func_stuff']['calling_convention']) |
| 229 | # tail_call_list is not printed. |
| 230 | elif type_specific_kind == "TYPE_SPECIFIC_SELF_TYPE": |
| 231 | img = "self_type = %s" % type_specific['self_type'] |
| 232 | else: |
| 233 | img = ("type_specific = ??? (unknown type_secific_kind: %s)" |
| 234 | % type_specific_kind) |
| 235 | return img |
| 236 | |
| 237 | def to_string(self): |
| 238 | """Return a pretty-printed image of our main_type. |
| 239 | """ |
| 240 | fields = [] |
| 241 | fields.append("name = %s" % self.val['name']) |
| 242 | fields.append("code = %s" % self.val['code']) |
| 243 | fields.append("flags = [%s]" % self.flags_to_string()) |
| 244 | fields.append("owner = %s" % self.owner_to_string()) |
| 245 | fields.append("target_type = %s" % self.val['target_type']) |
| 246 | if self.val['nfields'] > 0: |
| 247 | for fieldno in range(self.val['nfields']): |
| 248 | fields.append(self.struct_field_img(fieldno)) |
| 249 | if self.val['code'] == gdb.TYPE_CODE_RANGE: |
| 250 | fields.append(self.bounds_img()) |
| 251 | fields.append(self.type_specific_img()) |
| 252 | |
| 253 | return "\n{" + ",\n ".join(fields) + "}" |
| 254 | |
| 255 | |
| 256 | class CoreAddrPrettyPrinter: |
| 257 | """Print CORE_ADDR values as hex.""" |
| 258 | |
| 259 | def __init__(self, val): |
| 260 | self._val = val |
| 261 | |
| 262 | def to_string(self): |
| 263 | return hex(int(self._val)) |
| 264 | |
| 265 | |
| 266 | def type_lookup_function(val): |
| 267 | """A routine that returns the correct pretty printer for VAL |
| 268 | if appropriate. Returns None otherwise. |
| 269 | """ |
| 270 | if val.type.tag == "type": |
| 271 | return StructTypePrettyPrinter(val) |
| 272 | elif val.type.tag == "main_type": |
| 273 | return StructMainTypePrettyPrinter(val) |
| 274 | elif val.type.name == 'CORE_ADDR': |
| 275 | return CoreAddrPrettyPrinter(val) |
| 276 | return None |
| 277 | |
| 278 | |
| 279 | def register_pretty_printer(objfile): |
| 280 | """A routine to register a pretty-printer against the given OBJFILE. |
| 281 | """ |
| 282 | objfile.pretty_printers.append(type_lookup_function) |
| 283 | |
| 284 | |
| 285 | if __name__ == "__main__": |
| 286 | if gdb.current_objfile() is not None: |
| 287 | # This is the case where this script is being "auto-loaded" |
| 288 | # for a given objfile. Register the pretty-printer for that |
| 289 | # objfile. |
| 290 | register_pretty_printer(gdb.current_objfile()) |
| 291 | else: |
| 292 | # We need to locate the objfile corresponding to the GDB |
| 293 | # executable, and register the pretty-printer for that objfile. |
| 294 | # FIXME: The condition used to match the objfile is too simplistic |
| 295 | # and will not work on Windows. |
| 296 | for objfile in gdb.objfiles(): |
| 297 | if os.path.basename(objfile.filename) == "gdb": |
| 298 | objfile.pretty_printers.append(type_lookup_function) |