| from __future__ import print_function | |
| import struct | |
| import sys | |
| import gdb.printing | |
| import gdb.types | |
| class Iterator: | |
| def __iter__(self): | |
| return self | |
| if sys.version_info.major == 2: | |
| def next(self): | |
| return self.__next__() | |
| def children(self): | |
| return self | |
| class SmallStringPrinter: | |
| """Print an llvm::SmallString object.""" | |
| def __init__(self, val): | |
| self.val = val | |
| def to_string(self): | |
| data = self.val["BeginX"].cast(gdb.lookup_type("char").pointer()) | |
| length = self.val["Size"] | |
| return data.lazy_string(length=length) | |
| def display_hint(self): | |
| return "string" | |
| class StringRefPrinter: | |
| """Print an llvm::StringRef object.""" | |
| def __init__(self, val): | |
| self.val = val | |
| def to_string(self): | |
| data = self.val["Data"] | |
| length = self.val["Length"] | |
| return data.lazy_string(length=length) | |
| def display_hint(self): | |
| return "string" | |
| class SmallVectorPrinter(Iterator): | |
| """Print an llvm::SmallVector object.""" | |
| def __init__(self, val): | |
| self.val = val | |
| t = val.type.template_argument(0).pointer() | |
| self.begin = val["BeginX"].cast(t) | |
| self.size = val["Size"] | |
| self.i = 0 | |
| def __next__(self): | |
| if self.i == self.size: | |
| raise StopIteration | |
| ret = "[{}]".format(self.i), (self.begin + self.i).dereference() | |
| self.i += 1 | |
| return ret | |
| def to_string(self): | |
| return "llvm::SmallVector of Size {}, Capacity {}".format( | |
| self.size, self.val["Capacity"] | |
| ) | |
| def display_hint(self): | |
| return "array" | |
| class ArrayRefPrinter: | |
| """Print an llvm::ArrayRef object.""" | |
| class _iterator: | |
| def __init__(self, begin, end): | |
| self.cur = begin | |
| self.end = end | |
| self.count = 0 | |
| def __iter__(self): | |
| return self | |
| def __next__(self): | |
| if self.cur == self.end: | |
| raise StopIteration | |
| count = self.count | |
| self.count = self.count + 1 | |
| cur = self.cur | |
| self.cur = self.cur + 1 | |
| return "[%d]" % count, cur.dereference() | |
| if sys.version_info.major == 2: | |
| next = __next__ | |
| def __init__(self, val): | |
| self.val = val | |
| def children(self): | |
| data = self.val["Data"] | |
| return self._iterator(data, data + self.val["Length"]) | |
| def to_string(self): | |
| return "llvm::ArrayRef of length %d" % (self.val["Length"]) | |
| def display_hint(self): | |
| return "array" | |
| class ExpectedPrinter(Iterator): | |
| """Print an llvm::Expected object.""" | |
| def __init__(self, val): | |
| self.val = val | |
| def __next__(self): | |
| val = self.val | |
| if val is None: | |
| raise StopIteration | |
| self.val = None | |
| if val["HasError"]: | |
| return ( | |
| "error", | |
| val["ErrorStorage"] | |
| .address.cast(gdb.lookup_type("llvm::ErrorInfoBase").pointer()) | |
| .dereference(), | |
| ) | |
| return ( | |
| "value", | |
| val["TStorage"] | |
| .address.cast(val.type.template_argument(0).pointer()) | |
| .dereference(), | |
| ) | |
| def to_string(self): | |
| return "llvm::Expected{}".format(" is error" if self.val["HasError"] else "") | |
| class DenseMapPrinter: | |
| "Print a DenseMap" | |
| class _iterator: | |
| def __init__(self, buckets, used, num_buckets): | |
| self.buckets = buckets | |
| self.used = used | |
| self.num_buckets = num_buckets | |
| self.index = 0 | |
| self.advancePastEmptyBuckets() | |
| self.first = True | |
| def __iter__(self): | |
| return self | |
| def isUsed(self, index): | |
| # Occupancy is tracked in a packed 1-bit-per-bucket "used" array of | |
| # uint32_t words, so a bucket is occupied iff its bit is set. | |
| word = self.used[index >> 5] | |
| return (int(word) >> (index & 31)) & 1 | |
| def advancePastEmptyBuckets(self): | |
| while self.index < self.num_buckets and not self.isUsed(self.index): | |
| self.index += 1 | |
| def __next__(self): | |
| if self.index >= self.num_buckets: | |
| raise StopIteration | |
| bucket = (self.buckets + self.index).dereference() | |
| v = bucket["first" if self.first else "second"] | |
| if not self.first: | |
| self.index += 1 | |
| self.advancePastEmptyBuckets() | |
| self.first = True | |
| else: | |
| self.first = False | |
| return "x", v | |
| if sys.version_info.major == 2: | |
| next = __next__ | |
| def __init__(self, val): | |
| self.val = val | |
| def children(self): | |
| t = self.val.type.template_argument(3).pointer() | |
| storage = self.val["Storage"] | |
| buckets = storage["Buckets"].cast(t) | |
| return self._iterator(buckets, storage["Used"], int(storage["NumBuckets"])) | |
| def to_string(self): | |
| return "llvm::DenseMap with %d elements" % (self.val["Storage"]["NumEntries"]) | |
| def display_hint(self): | |
| return "map" | |
| class StringMapPrinter: | |
| "Print a StringMap" | |
| def __init__(self, val): | |
| self.val = val | |
| def children(self): | |
| it = self.val["TheTable"] | |
| end = it + self.val["NumBuckets"] | |
| value_ty = self.val.type.template_argument(0) | |
| entry_base_ty = gdb.lookup_type("llvm::StringMapEntryBase") | |
| while it != end: | |
| it_deref = it.dereference() | |
| if it_deref == 0: | |
| it = it + 1 | |
| continue | |
| entry_ptr = it_deref.cast(entry_base_ty.pointer()) | |
| entry = entry_ptr.dereference() | |
| str_len = entry["keyLength"] | |
| value_ptr = (entry_ptr + 1).cast(value_ty.pointer()) | |
| str_data = (entry_ptr + 1).cast(gdb.lookup_type("uintptr_t")) + max( | |
| value_ty.sizeof, entry_base_ty.alignof | |
| ) | |
| str_data = str_data.cast(gdb.lookup_type("char").const().pointer()) | |
| string_ref = gdb.Value( | |
| struct.pack("PN", int(str_data), int(str_len)), | |
| gdb.lookup_type("llvm::StringRef"), | |
| ) | |
| yield "key", string_ref | |
| value = value_ptr.dereference() | |
| yield "value", value | |
| it = it + 1 | |
| def to_string(self): | |
| return "llvm::StringMap with %d elements" % (self.val["NumItems"]) | |
| def display_hint(self): | |
| return "map" | |
| class TwinePrinter: | |
| "Print a Twine" | |
| def __init__(self, val): | |
| self._val = val | |
| def display_hint(self): | |
| return "string" | |
| def string_from_pretty_printer_lookup(self, val): | |
| """Lookup the default pretty-printer for val and use it. | |
| If no pretty-printer is defined for the type of val, print an error and | |
| return a placeholder string.""" | |
| pp = gdb.default_visualizer(val) | |
| if pp: | |
| s = pp.to_string() | |
| # The pretty-printer may return a LazyString instead of an actual Python | |
| # string. Convert it to a Python string. However, GDB doesn't seem to | |
| # register the LazyString type, so we can't check | |
| # "type(s) == gdb.LazyString". | |
| if "LazyString" in type(s).__name__: | |
| s = s.value().string() | |
| else: | |
| print( | |
| ( | |
| "No pretty printer for {} found. The resulting Twine " | |
| + "representation will be incomplete." | |
| ).format(val.type.name) | |
| ) | |
| s = "(missing {})".format(val.type.name) | |
| return s | |
| def is_twine_kind(self, kind, expected): | |
| if not kind.endswith(expected): | |
| return False | |
| # apparently some GDB versions add the NodeKind:: namespace | |
| # (happens for me on GDB 7.11) | |
| return kind in ( | |
| "llvm::Twine::" + expected, | |
| "llvm::Twine::NodeKind::" + expected, | |
| ) | |
| def string_from_child(self, child, kind): | |
| """Return the string representation of the Twine::Child child.""" | |
| if self.is_twine_kind(kind, "EmptyKind") or self.is_twine_kind( | |
| kind, "NullKind" | |
| ): | |
| return "" | |
| if self.is_twine_kind(kind, "TwineKind"): | |
| return self.string_from_twine_object(child["twine"].dereference()) | |
| if self.is_twine_kind(kind, "CStringKind"): | |
| return child["cString"].string() | |
| if self.is_twine_kind(kind, "StdStringKind"): | |
| val = child["stdString"].dereference() | |
| return self.string_from_pretty_printer_lookup(val) | |
| if self.is_twine_kind(kind, "PtrAndLengthKind"): | |
| val = child["ptrAndLength"] | |
| data = val["ptr"] | |
| length = val["length"] | |
| return data.string(length=length) | |
| if self.is_twine_kind(kind, "CharKind"): | |
| return chr(child["character"]) | |
| if self.is_twine_kind(kind, "DecUIKind"): | |
| return str(child["decUI"]) | |
| if self.is_twine_kind(kind, "DecIKind"): | |
| return str(child["decI"]) | |
| if self.is_twine_kind(kind, "DecULKind"): | |
| return str(child["decUL"].dereference()) | |
| if self.is_twine_kind(kind, "DecLKind"): | |
| return str(child["decL"].dereference()) | |
| if self.is_twine_kind(kind, "DecULLKind"): | |
| return str(child["decULL"].dereference()) | |
| if self.is_twine_kind(kind, "DecLLKind"): | |
| return str(child["decLL"].dereference()) | |
| if self.is_twine_kind(kind, "UHexKind"): | |
| val = child["uHex"].dereference() | |
| return hex(int(val)) | |
| print( | |
| ( | |
| "Unhandled NodeKind {} in Twine pretty-printer. The result will be " | |
| "incomplete." | |
| ).format(kind) | |
| ) | |
| return "(unhandled {})".format(kind) | |
| def string_from_twine_object(self, twine): | |
| """Return the string representation of the Twine object twine.""" | |
| lhs = twine["LHS"] | |
| rhs = twine["RHS"] | |
| lhs_kind = str(twine["LHSKind"]) | |
| rhs_kind = str(twine["RHSKind"]) | |
| lhs_str = self.string_from_child(lhs, lhs_kind) | |
| rhs_str = self.string_from_child(rhs, rhs_kind) | |
| return lhs_str + rhs_str | |
| def to_string(self): | |
| return self.string_from_twine_object(self._val) | |
| def display_hint(self): | |
| return "string" | |
| def get_pointer_int_pair(val): | |
| """Get tuple from llvm::PointerIntPair.""" | |
| info_name = val.type.template_argument(4).strip_typedefs().name | |
| # Note: this throws a gdb.error if the info type is not used (by means of a | |
| # call to getPointer() or similar) in the current translation unit. | |
| enum_type = gdb.lookup_type(info_name + "::MaskAndShiftConstants") | |
| enum_dict = gdb.types.make_enum_dict(enum_type) | |
| ptr_mask = enum_dict[info_name + "::PointerBitMask"] | |
| int_shift = enum_dict[info_name + "::IntShift"] | |
| int_mask = enum_dict[info_name + "::IntMask"] | |
| pair_union = val["Value"] | |
| value_type = pair_union.type.template_argument(0) | |
| value_type_ptr = value_type.pointer() | |
| pair_union = pair_union.address.cast(value_type_ptr).dereference() | |
| pair_union = pair_union.cast(gdb.lookup_type("intptr_t")) | |
| pointer = pair_union & ptr_mask | |
| pointer = pointer.cast(value_type) | |
| value = (pair_union >> int_shift) & int_mask | |
| return (pointer, value) | |
| class PointerIntPairPrinter: | |
| """Print a PointerIntPair.""" | |
| def __init__(self, pointer, value): | |
| self.pointer = pointer | |
| self.value = value | |
| def children(self): | |
| yield ("pointer", self.pointer) | |
| yield ("value", self.value) | |
| def to_string(self): | |
| return "(%s, %s)" % (self.pointer.type, self.value.type) | |
| def make_pointer_int_pair_printer(val): | |
| """Factory for an llvm::PointerIntPair printer.""" | |
| try: | |
| pointer, value = get_pointer_int_pair(val) | |
| except gdb.error: | |
| return None # If PointerIntPair cannot be analyzed, print as raw value. | |
| pointer_type = val.type.template_argument(0) | |
| value_type = val.type.template_argument(2) | |
| return PointerIntPairPrinter(pointer.cast(pointer_type), value.cast(value_type)) | |
| class PointerUnionPrinter: | |
| """Print a PointerUnion.""" | |
| def __init__(self, pointer): | |
| self.pointer = pointer | |
| def children(self): | |
| yield ("pointer", self.pointer) | |
| def to_string(self): | |
| return "Containing %s" % self.pointer.type | |
| def _make_pointer_union_raw_fallback(raw_value, min_low_bits): | |
| """Strip tag bits and return as void* when active type is unknown.""" | |
| pointer = raw_value & ~((1 << min_low_bits) - 1) | |
| void_ptr = gdb.lookup_type("void").pointer() | |
| return PointerUnionPrinter(gdb.Value(pointer).cast(void_ptr)) | |
| def make_pointer_union_printer(val): | |
| """Factory for an llvm::PointerUnion printer.""" | |
| try: | |
| raw_value = int( | |
| val["Val"]["Data"] | |
| .address.cast(gdb.lookup_type("uintptr_t").pointer()) | |
| .dereference() | |
| ) | |
| # Collect template argument types. | |
| # Distinguish truncation (RuntimeError — type resolution failure) from | |
| # normal end-of-args (gdb.error — index out of range). | |
| arg_types = [] | |
| truncated = False | |
| while True: | |
| try: | |
| arg_types.append(val.type.template_argument(len(arg_types))) | |
| except RuntimeError: | |
| truncated = True | |
| break | |
| except gdb.error: | |
| break | |
| if not arg_types: | |
| return None | |
| # Compute tag from type alignments (fixed-width encoding). | |
| num_args = len(arg_types) | |
| min_low_bits = min( | |
| (a.bit_length() - 1 if a > 0 else 0) | |
| for a in (t.target().alignof for t in arg_types) | |
| ) | |
| # If template args are truncated, we can't reliably decode the tag. | |
| # Fall back to showing the raw pointer with tag bits stripped. | |
| if truncated: | |
| return _make_pointer_union_raw_fallback(raw_value, min_low_bits) | |
| tag_bits = (num_args - 1).bit_length() | |
| if tag_bits > min_low_bits: | |
| return _make_pointer_union_raw_fallback(raw_value, min_low_bits) | |
| tag_shift = min_low_bits - tag_bits | |
| tag_mask = (1 << tag_bits) - 1 | |
| active_tag = (raw_value >> tag_shift) & tag_mask | |
| if active_tag >= num_args: | |
| return _make_pointer_union_raw_fallback(raw_value, min_low_bits) | |
| pointer_type = arg_types[active_tag] | |
| align = pointer_type.target().alignof | |
| low_bits = align.bit_length() - 1 if align > 0 else 0 | |
| pointer = raw_value & ~((1 << low_bits) - 1) | |
| except (gdb.error, RuntimeError, IndexError): | |
| return None | |
| return PointerUnionPrinter(gdb.Value(pointer).cast(pointer_type)) | |
| class IlistNodePrinter: | |
| """Print an llvm::ilist_node object.""" | |
| def __init__(self, val): | |
| impl_type = val.type.fields()[0].type | |
| base_type = impl_type.fields()[0].type | |
| derived_type = val.type.template_argument(0) | |
| def get_prev_and_sentinel(base): | |
| # One of Prev and PrevAndSentinel exists. Depending on #defines used to | |
| # compile LLVM, the base_type's template argument is either true of false. | |
| if base_type.template_argument(0): | |
| return get_pointer_int_pair(base["PrevAndSentinel"]) | |
| return base["Prev"], None | |
| # Casts a base_type pointer to the appropriate derived type. | |
| def cast_pointer(pointer): | |
| sentinel = get_prev_and_sentinel(pointer.dereference())[1] | |
| pointer = pointer.cast(impl_type.pointer()) | |
| if sentinel: | |
| return pointer | |
| return pointer.cast(derived_type.pointer()) | |
| # Repeated cast becaue val.type's base_type is ambiguous when using tags. | |
| base = val.cast(impl_type).cast(base_type) | |
| (prev, sentinel) = get_prev_and_sentinel(base) | |
| prev = prev.cast(base_type.pointer()) | |
| self.prev = cast_pointer(prev) | |
| self.next = cast_pointer(val["Next"]) | |
| self.sentinel = sentinel | |
| def children(self): | |
| if self.sentinel: | |
| yield "sentinel", "yes" | |
| yield "prev", self.prev | |
| yield "next", self.next | |
| class IlistPrinter: | |
| """Print an llvm::simple_ilist or llvm::iplist object.""" | |
| def __init__(self, val): | |
| self.node_type = val.type.template_argument(0) | |
| sentinel = val["Sentinel"] | |
| # First field is common base type of sentinel and ilist_node. | |
| base_type = sentinel.type.fields()[0].type | |
| self.sentinel = sentinel.address.cast(base_type.pointer()) | |
| def _pointers(self): | |
| pointer = self.sentinel | |
| while True: | |
| pointer = pointer["Next"].cast(pointer.type) | |
| if pointer == self.sentinel: | |
| return | |
| yield pointer.cast(self.node_type.pointer()) | |
| def children(self): | |
| for k, v in enumerate(self._pointers()): | |
| yield ("[%d]" % k, v.dereference()) | |
| pp = gdb.printing.RegexpCollectionPrettyPrinter("LLVMSupport") | |
| pp.add_printer("llvm::SmallString", "^llvm::SmallString<.*>$", SmallStringPrinter) | |
| pp.add_printer("llvm::StringRef", "^llvm::StringRef$", StringRefPrinter) | |
| pp.add_printer( | |
| "llvm::SmallVectorImpl", "^llvm::SmallVector(Impl)?<.*>$", SmallVectorPrinter | |
| ) | |
| pp.add_printer("llvm::ArrayRef", "^llvm::(Mutable)?ArrayRef<.*>$", ArrayRefPrinter) | |
| pp.add_printer("llvm::Expected", "^llvm::Expected<.*>$", ExpectedPrinter) | |
| pp.add_printer("llvm::DenseMap", "^llvm::DenseMap<.*>$", DenseMapPrinter) | |
| pp.add_printer("llvm::StringMap", "^llvm::StringMap<.*>$", StringMapPrinter) | |
| pp.add_printer("llvm::Twine", "^llvm::Twine$", TwinePrinter) | |
| pp.add_printer( | |
| "llvm::PointerIntPair", "^llvm::PointerIntPair<.*>$", make_pointer_int_pair_printer | |
| ) | |
| pp.add_printer( | |
| "llvm::PointerUnion", "^llvm::PointerUnion<.*>$", make_pointer_union_printer | |
| ) | |
| pp.add_printer("llvm::ilist_node", "^llvm::ilist_node<.*>$", IlistNodePrinter) | |
| pp.add_printer("llvm::iplist", "^llvm::iplist<.*>$", IlistPrinter) | |
| pp.add_printer("llvm::simple_ilist", "^llvm::simple_ilist<.*>$", IlistPrinter) | |
| gdb.printing.register_pretty_printer(gdb.current_objfile(), pp) |