123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- # Copyright (C) 2009-2022 Free Software Foundation, Inc.
- #
- # This file is part of GDB.
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- import gdb
- import os.path
- class TypeFlag:
- """A class that allows us to store a flag name, its short name,
- and its value.
- In the GDB sources, struct type has a component called instance_flags
- in which the value is the addition of various flags. These flags are
- defined by the enumerates type_instance_flag_value. This class helps us
- recreate a list with all these flags that is easy to manipulate and sort.
- Because all flag names start with TYPE_INSTANCE_FLAG_, a short_name
- attribute is provided that strips this prefix.
- ATTRIBUTES
- name: The enumeration name (eg: "TYPE_INSTANCE_FLAG_CONST").
- value: The associated value.
- short_name: The enumeration name, with the suffix stripped.
- """
- def __init__(self, name, value):
- self.name = name
- self.value = value
- self.short_name = name.replace("TYPE_INSTANCE_FLAG_", "")
- def __lt__(self, other):
- """Sort by value order."""
- return self.value < other.value
- # A list of all existing TYPE_INSTANCE_FLAGS_* enumerations,
- # stored as TypeFlags objects. Lazy-initialized.
- TYPE_FLAGS = None
- class TypeFlagsPrinter:
- """A class that prints a decoded form of an instance_flags value.
- This class uses a global named TYPE_FLAGS, which is a list of
- all defined TypeFlag values. Using a global allows us to compute
- this list only once.
- This class relies on a couple of enumeration types being defined.
- If not, then printing of the instance_flag is going to be degraded,
- but it's not a fatal error.
- """
- def __init__(self, val):
- self.val = val
- def __str__(self):
- global TYPE_FLAGS
- if TYPE_FLAGS is None:
- self.init_TYPE_FLAGS()
- if not self.val:
- return "0"
- if TYPE_FLAGS:
- flag_list = [
- flag.short_name for flag in TYPE_FLAGS if self.val & flag.value
- ]
- else:
- flag_list = ["???"]
- return "0x%x [%s]" % (self.val, "|".join(flag_list))
- def init_TYPE_FLAGS(self):
- """Initialize the TYPE_FLAGS global as a list of TypeFlag objects.
- This operation requires the search of a couple of enumeration types.
- If not found, a warning is printed on stdout, and TYPE_FLAGS is
- set to the empty list.
- The resulting list is sorted by increasing value, to facilitate
- printing of the list of flags used in an instance_flags value.
- """
- global TYPE_FLAGS
- TYPE_FLAGS = []
- try:
- iflags = gdb.lookup_type("enum type_instance_flag_value")
- except:
- print("Warning: Cannot find enum type_instance_flag_value type.")
- print(" `struct type' pretty-printer will be degraded")
- return
- TYPE_FLAGS = [TypeFlag(field.name, field.enumval) for field in iflags.fields()]
- TYPE_FLAGS.sort()
- class StructTypePrettyPrinter:
- """Pretty-print an object of type struct type"""
- def __init__(self, val):
- self.val = val
- def to_string(self):
- fields = []
- fields.append("pointer_type = %s" % self.val["pointer_type"])
- fields.append("reference_type = %s" % self.val["reference_type"])
- fields.append("chain = %s" % self.val["reference_type"])
- fields.append(
- "instance_flags = %s" % TypeFlagsPrinter(self.val["m_instance_flags"])
- )
- fields.append("length = %d" % self.val["length"])
- fields.append("main_type = %s" % self.val["main_type"])
- return "\n{" + ",\n ".join(fields) + "}"
- class StructMainTypePrettyPrinter:
- """Pretty-print an objet of type main_type"""
- def __init__(self, val):
- self.val = val
- def flags_to_string(self):
- """struct main_type contains a series of components that
- are one-bit ints whose name start with "flag_". For instance:
- flag_unsigned, flag_stub, etc. In essence, these components are
- really boolean flags, and this method prints a short synthetic
- version of the value of all these flags. For instance, if
- flag_unsigned and flag_static are the only components set to 1,
- this function will return "unsigned|static".
- """
- fields = [
- field.name.replace("flag_", "")
- for field in self.val.type.fields()
- if field.name.startswith("flag_") and self.val[field.name]
- ]
- return "|".join(fields)
- def owner_to_string(self):
- """Return an image of component "owner"."""
- if self.val["m_flag_objfile_owned"] != 0:
- return "%s (objfile)" % self.val["m_owner"]["objfile"]
- else:
- return "%s (gdbarch)" % self.val["m_owner"]["gdbarch"]
- def struct_field_location_img(self, field_val):
- """Return an image of the loc component inside the given field
- gdb.Value.
- """
- loc_val = field_val["m_loc"]
- loc_kind = str(field_val["m_loc_kind"])
- if loc_kind == "FIELD_LOC_KIND_BITPOS":
- return "bitpos = %d" % loc_val["bitpos"]
- elif loc_kind == "FIELD_LOC_KIND_ENUMVAL":
- return "enumval = %d" % loc_val["enumval"]
- elif loc_kind == "FIELD_LOC_KIND_PHYSADDR":
- return "physaddr = 0x%x" % loc_val["physaddr"]
- elif loc_kind == "FIELD_LOC_KIND_PHYSNAME":
- return "physname = %s" % loc_val["physname"]
- elif loc_kind == "FIELD_LOC_KIND_DWARF_BLOCK":
- return "dwarf_block = %s" % loc_val["dwarf_block"]
- else:
- return "m_loc = ??? (unsupported m_loc_kind value)"
- def struct_field_img(self, fieldno):
- """Return an image of the main_type field number FIELDNO."""
- f = self.val["flds_bnds"]["fields"][fieldno]
- label = "flds_bnds.fields[%d]:" % fieldno
- if f["artificial"]:
- label += " (artificial)"
- fields = []
- fields.append("m_name = %s" % f["m_name"])
- fields.append("m_type = %s" % f["m_type"])
- fields.append("m_loc_kind = %s" % f["m_loc_kind"])
- fields.append("bitsize = %d" % f["bitsize"])
- fields.append(self.struct_field_location_img(f))
- return label + "\n" + " {" + ",\n ".join(fields) + "}"
- def bound_img(self, bound_name):
- """Return an image of the given main_type's bound."""
- bounds = self.val["flds_bnds"]["bounds"].dereference()
- b = bounds[bound_name]
- bnd_kind = str(b["m_kind"])
- if bnd_kind == "PROP_CONST":
- return str(b["m_data"]["const_val"])
- elif bnd_kind == "PROP_UNDEFINED":
- return "(undefined)"
- else:
- info = [bnd_kind]
- if bound_name == "high" and bounds["flag_upper_bound_is_count"]:
- info.append("upper_bound_is_count")
- return "{} ({})".format(str(b["m_data"]["baton"]), ",".join(info))
- def bounds_img(self):
- """Return an image of the main_type bounds."""
- b = self.val["flds_bnds"]["bounds"].dereference()
- low = self.bound_img("low")
- high = self.bound_img("high")
- img = "flds_bnds.bounds = {%s, %s}" % (low, high)
- if b["flag_bound_evaluated"]:
- img += " [evaluated]"
- return img
- def type_specific_img(self):
- """Return a string image of the main_type type_specific union.
- Only the relevant component of that union is printed (based on
- the value of the type_specific_kind field.
- """
- type_specific_kind = str(self.val["type_specific_field"])
- type_specific = self.val["type_specific"]
- if type_specific_kind == "TYPE_SPECIFIC_NONE":
- img = "type_specific_field = %s" % type_specific_kind
- elif type_specific_kind == "TYPE_SPECIFIC_CPLUS_STUFF":
- img = "cplus_stuff = %s" % type_specific["cplus_stuff"]
- elif type_specific_kind == "TYPE_SPECIFIC_GNAT_STUFF":
- img = (
- "gnat_stuff = {descriptive_type = %s}"
- % type_specific["gnat_stuff"]["descriptive_type"]
- )
- elif type_specific_kind == "TYPE_SPECIFIC_FLOATFORMAT":
- img = "floatformat[0..1] = %s" % type_specific["floatformat"]
- elif type_specific_kind == "TYPE_SPECIFIC_FUNC":
- img = (
- "calling_convention = %d"
- % type_specific["func_stuff"]["calling_convention"]
- )
- # tail_call_list is not printed.
- elif type_specific_kind == "TYPE_SPECIFIC_SELF_TYPE":
- img = "self_type = %s" % type_specific["self_type"]
- elif type_specific_kind == "TYPE_SPECIFIC_FIXED_POINT":
- # The scaling factor is an opaque structure, so we cannot
- # decode its value from Python (not without insider knowledge).
- img = (
- "scaling_factor: <opaque> (call __gmpz_dump with "
- " _mp_num and _mp_den fields if needed)"
- )
- elif type_specific_kind == "TYPE_SPECIFIC_INT":
- img = "int_stuff = { bit_size = %d, bit_offset = %d }" % (
- type_specific["int_stuff"]["bit_size"],
- type_specific["int_stuff"]["bit_offset"],
- )
- else:
- img = (
- "type_specific = ??? (unknown type_specific_kind: %s)"
- % type_specific_kind
- )
- return img
- def to_string(self):
- """Return a pretty-printed image of our main_type."""
- fields = []
- fields.append("name = %s" % self.val["name"])
- fields.append("code = %s" % self.val["code"])
- fields.append("flags = [%s]" % self.flags_to_string())
- fields.append("owner = %s" % self.owner_to_string())
- fields.append("target_type = %s" % self.val["target_type"])
- if self.val["nfields"] > 0:
- for fieldno in range(self.val["nfields"]):
- fields.append(self.struct_field_img(fieldno))
- if self.val["code"] == gdb.TYPE_CODE_RANGE:
- fields.append(self.bounds_img())
- fields.append(self.type_specific_img())
- return "\n{" + ",\n ".join(fields) + "}"
- class CoreAddrPrettyPrinter:
- """Print CORE_ADDR values as hex."""
- def __init__(self, val):
- self._val = val
- def to_string(self):
- return hex(int(self._val))
- class IntrusiveListPrinter:
- """Print a struct intrusive_list."""
- def __init__(self, val):
- self._val = val
- # Type of linked items.
- self._item_type = self._val.type.template_argument(0)
- self._node_ptr_type = gdb.lookup_type(
- "intrusive_list_node<{}>".format(self._item_type.tag)
- ).pointer()
- # Type of value -> node converter.
- self._conv_type = self._val.type.template_argument(1)
- if self._uses_member_node():
- # The second template argument of intrusive_member_node is a member
- # pointer value. Its value is the offset of the node member in the
- # enclosing type.
- member_node_ptr = self._conv_type.template_argument(1)
- member_node_ptr = member_node_ptr.cast(gdb.lookup_type("int"))
- self._member_node_offset = int(member_node_ptr)
- # This is only needed in _as_node_ptr if using a member node. Look it
- # up here so we only do it once.
- self._char_ptr_type = gdb.lookup_type("char").pointer()
- def display_hint(self):
- return "array"
- def _uses_member_node(self):
- """Return True if the list items use a node as a member, False if
- they use a node as a base class.
- """
- if self._conv_type.name.startswith("intrusive_member_node<"):
- return True
- elif self._conv_type.name.startswith("intrusive_base_node<"):
- return False
- else:
- raise RuntimeError(
- "Unexpected intrusive_list value -> node converter type: {}".format(
- self._conv_type.name
- )
- )
- def to_string(self):
- s = "intrusive list of {}".format(self._item_type)
- if self._uses_member_node():
- node_member = self._conv_type.template_argument(1)
- s += ", linked through {}".format(node_member)
- return s
- def _as_node_ptr(self, elem_ptr):
- """Given ELEM_PTR, a pointer to a list element, return a pointer to the
- corresponding intrusive_list_node.
- """
- assert elem_ptr.type.code == gdb.TYPE_CODE_PTR
- if self._uses_member_node():
- # Node as a member: add the member node offset from to the element's
- # address to get the member node's address.
- elem_char_ptr = elem_ptr.cast(self._char_ptr_type)
- node_char_ptr = elem_char_ptr + self._member_node_offset
- return node_char_ptr.cast(self._node_ptr_type)
- else:
- # Node as a base: just casting from node pointer to item pointer
- # will adjust the pointer value.
- return elem_ptr.cast(self._node_ptr_type)
- def _children_generator(self):
- """Generator that yields one tuple per list item."""
- elem_ptr = self._val["m_front"]
- idx = 0
- while elem_ptr != 0:
- yield (str(idx), elem_ptr.dereference())
- node_ptr = self._as_node_ptr(elem_ptr)
- elem_ptr = node_ptr["next"]
- idx += 1
- def children(self):
- return self._children_generator()
- def type_lookup_function(val):
- """A routine that returns the correct pretty printer for VAL
- if appropriate. Returns None otherwise.
- """
- tag = val.type.tag
- name = val.type.name
- if tag == "type":
- return StructTypePrettyPrinter(val)
- elif tag == "main_type":
- return StructMainTypePrettyPrinter(val)
- elif name == "CORE_ADDR":
- return CoreAddrPrettyPrinter(val)
- elif tag is not None and tag.startswith("intrusive_list<"):
- return IntrusiveListPrinter(val)
- return None
- def register_pretty_printer(objfile):
- """A routine to register a pretty-printer against the given OBJFILE."""
- objfile.pretty_printers.append(type_lookup_function)
- if __name__ == "__main__":
- if gdb.current_objfile() is not None:
- # This is the case where this script is being "auto-loaded"
- # for a given objfile. Register the pretty-printer for that
- # objfile.
- register_pretty_printer(gdb.current_objfile())
- else:
- # We need to locate the objfile corresponding to the GDB
- # executable, and register the pretty-printer for that objfile.
- # FIXME: The condition used to match the objfile is too simplistic
- # and will not work on Windows.
- for objfile in gdb.objfiles():
- if os.path.basename(objfile.filename) == "gdb":
- objfile.pretty_printers.append(type_lookup_function)
|