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 OptionalPrinter(Iterator):
"""Print an llvm::Optional object."""
def __init__(self, val):
self.val = val
def __next__(self):
val = self.val
if val is None:
raise StopIteration
self.val = None
if not val["Storage"]["hasVal"]:
raise StopIteration
return ("value", val["Storage"]["val"])
def to_string(self):
return "llvm::Optional{}".format(
"" if self.val["Storage"]["hasVal"] else " is not initialized"
)
class DenseMapPrinter:
"Print a DenseMap"
class _iterator:
def __init__(self, key_info_t, begin, end):
self.key_info_t = key_info_t
self.cur = begin
self.end = end
self.advancePastEmptyBuckets()
self.first = True
def __iter__(self):
return self
def advancePastEmptyBuckets(self):
# disabled until the comments below can be addressed
# keeping as notes/posterity/hints for future contributors
return
n = self.key_info_t.name
is_equal = gdb.parse_and_eval(n + "::isEqual")
empty = gdb.parse_and_eval(n + "::getEmptyKey()")
tombstone = gdb.parse_and_eval(n + "::getTombstoneKey()")
# the following is invalid, GDB fails with:
# Python Exception <class 'gdb.error'> Attempt to take address of value
# not located in memory.
# because isEqual took parameter (for the unsigned long key I was testing)
# by const ref, and GDB
# It's also not entirely general - we should be accessing the "getFirst()"
# member function, not the 'first' member variable, but I've yet to figure
# out how to find/call member functions (especially (const) overloaded
# ones) on a gdb.Value.
while self.cur != self.end and (
is_equal(self.cur.dereference()["first"], empty)
or is_equal(self.cur.dereference()["first"], tombstone)
):
self.cur = self.cur + 1
def __next__(self):
if self.cur == self.end:
raise StopIteration
cur = self.cur
v = cur.dereference()["first" if self.first else "second"]
if not self.first:
self.cur = self.cur + 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()
begin = self.val["Buckets"].cast(t)
end = (begin + self.val["NumBuckets"]).cast(t)
return self._iterator(self.val.type.template_argument(2), begin, end)
def to_string(self):
return "llvm::DenseMap with %d elements" % (self.val["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")
tombstone = gdb.parse_and_eval("llvm::StringMapImpl::TombstoneIntVal")
while it != end:
it_deref = it.dereference()
if it_deref == 0 or it_deref == tombstone:
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_printer(val):
"""Factory for an llvm::PointerUnion printer."""
try:
pointer, value = get_pointer_int_pair(val["Val"])
except gdb.error:
return None # If PointerIntPair cannot be analyzed, print as raw value.
pointer_type = val.type.template_argument(int(value))
return PointerUnionPrinter(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::Optional", "^llvm::Optional<.*>$", OptionalPrinter)
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)