mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
64b62b62ba
Instead of using the type llvm::StringMapEntry<{stringified_value_type}> use only the base class llvm::StringMapEntryBase and calculate the offsets of the member variables manually. The approach with stringifying the name of the value type is pretty fragile as it can easily break with local and dependent types. Differential Revision: https://reviews.llvm.org/D94431
494 lines
15 KiB
Python
494 lines
15 KiB
Python
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
|
|
|
|
def escape_bytes(val, l):
|
|
return '"' + val.string(encoding='Latin-1', length=l).encode('unicode_escape').decode() + '"'
|
|
|
|
class SmallStringPrinter:
|
|
"""Print an llvm::SmallString object."""
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
begin = self.val['BeginX']
|
|
return escape_bytes(begin.cast(gdb.lookup_type('char').pointer()), self.val['Size'])
|
|
|
|
class StringRefPrinter:
|
|
"""Print an llvm::StringRef object."""
|
|
|
|
def __init__(self, val):
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return escape_bytes(self.val['Data'], self.val['Length'])
|
|
|
|
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']['value'])
|
|
|
|
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().address.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, 'StringRefKind'):
|
|
val = child['stringRef'].dereference()
|
|
pp = StringRefPrinter(val)
|
|
return pp.to_string()
|
|
|
|
if self.is_twine_kind(kind, 'SmallStringKind'):
|
|
val = child['smallString'].dereference()
|
|
pp = SmallStringPrinter(val)
|
|
return pp.to_string()
|
|
|
|
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_str = ''
|
|
rhs_str = ''
|
|
|
|
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 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']
|
|
pointer = (pair_union & ptr_mask)
|
|
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 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)
|