import lldb
import re
+import debugger_pretty_printers_common as rustpp
-def print_val(val, internal_dict):
- '''Prints the given value with Rust syntax'''
- type_class = val.GetType().GetTypeClass()
+#===============================================================================
+# LLDB Pretty Printing Module for Rust
+#===============================================================================
- if type_class == lldb.eTypeClassStruct:
- return print_struct_val(val, internal_dict)
+class LldbType(rustpp.Type):
- if type_class == lldb.eTypeClassUnion:
- return print_enum_val(val, internal_dict)
+ def __init__(self, ty):
+ super(LldbType, self).__init__()
+ self.ty = ty
+ self.fields = None
- if type_class == lldb.eTypeClassPointer:
- return print_pointer_val(val, internal_dict)
+ def get_unqualified_type_name(self):
+ qualified_name = self.ty.GetName()
- if type_class == lldb.eTypeClassArray:
- return print_fixed_size_vec_val(val, internal_dict)
+ if qualified_name is None:
+ return qualified_name
- return val.GetValue()
+ return rustpp.extract_type_name(qualified_name).replace("&'static ", "&")
+ def get_dwarf_type_kind(self):
+ type_class = self.ty.GetTypeClass()
-#=--------------------------------------------------------------------------------------------------
-# Type-Specialized Printing Functions
-#=--------------------------------------------------------------------------------------------------
+ if type_class == lldb.eTypeClassStruct:
+ return rustpp.DWARF_TYPE_CODE_STRUCT
+
+ if type_class == lldb.eTypeClassUnion:
+ return rustpp.DWARF_TYPE_CODE_UNION
+
+ if type_class == lldb.eTypeClassPointer:
+ return rustpp.DWARF_TYPE_CODE_PTR
+
+ if type_class == lldb.eTypeClassArray:
+ return rustpp.DWARF_TYPE_CODE_ARRAY
+
+ if type_class == lldb.eTypeClassEnumeration:
+ return rustpp.DWARF_TYPE_CODE_ENUM
+
+ return None
+
+ def get_fields(self):
+ assert ((self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT) or
+ (self.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION))
+ if self.fields is None:
+ self.fields = list(self.ty.fields)
+ return self.fields
+
+ def get_wrapped_value(self):
+ return self.ty
+
+
+class LldbValue(rustpp.Value):
+ def __init__(self, lldb_val):
+ ty = lldb_val.type
+ wty = LldbType(ty)
+ super(LldbValue, self).__init__(wty)
+ self.lldb_val = lldb_val
+ self.children = {}
+
+ def get_child_at_index(self, index):
+ child = self.children.get(index)
+ if child is None:
+ lldb_field = self.lldb_val.GetChildAtIndex(index)
+ child = LldbValue(lldb_field)
+ self.children[index] = child
+ return child
+
+ def as_integer(self):
+ return self.lldb_val.GetValueAsUnsigned()
+
+ def get_wrapped_value(self):
+ return self.lldb_val
+
+
+def print_val(lldb_val, internal_dict):
+ val = LldbValue(lldb_val)
+ type_kind = val.type.get_type_kind()
+
+ if (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or
+ type_kind == rustpp.TYPE_KIND_EMPTY):
+ return print_struct_val(val,
+ internal_dict,
+ omit_first_field = False,
+ omit_type_name = False,
+ is_tuple_like = False)
-def print_struct_val(val, internal_dict):
- '''Prints a struct, tuple, or tuple struct value with Rust syntax'''
- assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct
+ if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT:
+ return print_struct_val(val,
+ internal_dict,
+ omit_first_field = True,
+ omit_type_name = False,
+ is_tuple_like = False)
- if is_vec_slice(val):
+ if type_kind == rustpp.TYPE_KIND_SLICE:
return print_vec_slice_val(val, internal_dict)
- elif is_std_vec(val):
+
+ if type_kind == rustpp.TYPE_KIND_STR_SLICE:
+ return print_str_slice_val(val, internal_dict)
+
+ if type_kind == rustpp.TYPE_KIND_STD_VEC:
return print_std_vec_val(val, internal_dict)
- else:
- return print_struct_val_starting_from(0, val, internal_dict)
+ if type_kind == rustpp.TYPE_KIND_STD_STRING:
+ return print_std_string_val(val, internal_dict)
+
+ if type_kind == rustpp.TYPE_KIND_TUPLE:
+ return print_struct_val(val,
+ internal_dict,
+ omit_first_field = False,
+ omit_type_name = True,
+ is_tuple_like = True)
+
+ if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT:
+ return print_struct_val(val,
+ internal_dict,
+ omit_first_field = False,
+ omit_type_name = False,
+ is_tuple_like = True)
+
+ if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT:
+ return val.type.get_unqualified_type_name()
+
+ if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT:
+ return print_struct_val(val,
+ internal_dict,
+ omit_first_field = True,
+ omit_type_name = False,
+ is_tuple_like = True)
+
+ if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM:
+ return print_val(lldb_val.GetChildAtIndex(0), internal_dict)
+
+ if type_kind == rustpp.TYPE_KIND_PTR:
+ return print_pointer_val(val, internal_dict)
-def print_struct_val_starting_from(field_start_index, val, internal_dict):
- '''
- Prints a struct, tuple, or tuple struct value with Rust syntax.
- Ignores any fields before field_start_index.
- '''
- assert val.GetType().GetTypeClass() == lldb.eTypeClassStruct
+ if type_kind == rustpp.TYPE_KIND_FIXED_SIZE_VEC:
+ return print_fixed_size_vec_val(val, internal_dict)
+
+ if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM:
+ # This is a regular enum, extract the discriminant
+ discriminant_val = rustpp.get_discriminant_value_as_integer(val)
+ return print_val(lldb_val.GetChildAtIndex(discriminant_val), internal_dict)
+
+ if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM:
+ encoded_enum_info = rustpp.EncodedEnumInfo(val)
+ if encoded_enum_info.is_null_variant():
+ return encoded_enum_info.get_null_variant_name()
+
+ non_null_val = encoded_enum_info.get_non_null_variant_val()
+ return print_val(non_null_val.get_wrapped_value(), internal_dict)
- t = val.GetType()
- type_name = extract_type_name(t.GetName())
- num_children = val.num_children
+ # No pretty printer has been found
+ return lldb_val.GetValue()
- if (num_children - field_start_index) == 0:
- # The only field of this struct is the enum discriminant
- return type_name
- is_tuple_like = type_is_tuple_like(t)
+#=--------------------------------------------------------------------------------------------------
+# Type-Specialized Printing Functions
+#=--------------------------------------------------------------------------------------------------
+
+def print_struct_val(val, internal_dict, omit_first_field, omit_type_name, is_tuple_like):
+ """
+ Prints a struct, tuple, or tuple struct value with Rust syntax.
+ Ignores any fields before field_start_index.
+ """
+ assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT
+
+ if omit_type_name:
+ type_name = ""
+ else:
+ type_name = val.type.get_unqualified_type_name()
if is_tuple_like:
template = "%(type_name)s(%(body)s)"
template = "%(type_name)s {\n%(body)s\n}"
separator = ", \n"
- if type_name.startswith("("):
- # this is a tuple, so don't print the type name
- type_name = ""
+ fields = val.type.get_fields()
def render_child(child_index):
this = ""
if not is_tuple_like:
- field_name = t.GetFieldAtIndex(child_index).GetName()
+ field_name = fields[child_index].name
this += field_name + ": "
- field_val = val.GetChildAtIndex(child_index)
+ field_val = val.get_child_at_index(child_index)
- if not field_val.IsValid():
- field = t.GetFieldAtIndex(child_index)
+ if not field_val.get_wrapped_value().IsValid():
+ field = fields[child_index]
# LLDB is not good at handling zero-sized values, so we have to help
# it a little
if field.GetType().GetByteSize() == 0:
- return this + extract_type_name(field.GetType().GetName())
+ return this + rustpp.extract_type_name(field.GetType().GetName())
else:
return this + "<invalid value>"
- return this + print_val(field_val, internal_dict)
+ return this + print_val(field_val.get_wrapped_value(), internal_dict)
- body = separator.join([render_child(idx) for idx in range(field_start_index, num_children)])
+ if omit_first_field:
+ field_start_index = 1
+ else:
+ field_start_index = 0
+
+ body = separator.join([render_child(idx) for idx in range(field_start_index, len(fields))])
return template % {"type_name": type_name,
"body": body}
-
-def print_enum_val(val, internal_dict):
- '''Prints an enum value with Rust syntax'''
-
- assert val.GetType().GetTypeClass() == lldb.eTypeClassUnion
-
- if val.num_children == 1:
- # This is either an enum with just one variant, or it is an Option-like
- # enum where the discriminant is encoded in a non-nullable pointer
- # field. We find out which one it is by looking at the member name of
- # the sole union variant. If it starts with "RUST$ENCODED$ENUM$" then
- # we have an Option-like enum.
- first_variant_name = val.GetChildAtIndex(0).GetName()
- if first_variant_name and first_variant_name.startswith("RUST$ENCODED$ENUM$"):
-
- # This is an Option-like enum. The position of the discriminator field is
- # encoded in the name which has the format:
- # RUST$ENCODED$ENUM$<index of discriminator field>$<name of null variant>
- last_separator_index = first_variant_name.rfind("$")
- if last_separator_index == -1:
- return "<invalid enum encoding: %s>" % first_variant_name
-
- start_index = len("RUST$ENCODED$ENUM$")
-
- # Extract indices of the discriminator field
- try:
- disr_field_indices = first_variant_name[start_index:last_separator_index].split("$")
- disr_field_indices = [int(index) for index in disr_field_indices]
- except:
- return "<invalid enum encoding: %s>" % first_variant_name
-
- # Read the discriminant
- disr_val = val.GetChildAtIndex(0)
- for index in disr_field_indices:
- disr_val = disr_val.GetChildAtIndex(index)
-
- # If the discriminant field is a fat pointer we have to consider the
- # first word as the true discriminant
- if disr_val.GetType().GetTypeClass() == lldb.eTypeClassStruct:
- disr_val = disr_val.GetChildAtIndex(0)
-
- if disr_val.GetValueAsUnsigned() == 0:
- # Null case: Print the name of the null-variant
- null_variant_name = first_variant_name[last_separator_index + 1:]
- return null_variant_name
- else:
- # Non-null case: Interpret the data as a value of the non-null variant type
- return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
- else:
- # This is just a regular uni-variant enum without discriminator field
- return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
-
- # If we are here, this is a regular enum with more than one variant
- disr_val = val.GetChildAtIndex(0).GetChildMemberWithName("RUST$ENUM$DISR")
- disr_type = disr_val.GetType()
-
- if disr_type.GetTypeClass() != lldb.eTypeClassEnumeration:
- return "<Invalid enum value encountered: Discriminator is not an enum>"
-
- variant_index = disr_val.GetValueAsUnsigned()
- return print_struct_val_starting_from(1, val.GetChildAtIndex(variant_index), internal_dict)
-
-
def print_pointer_val(val, internal_dict):
- '''Prints a pointer value with Rust syntax'''
- assert val.GetType().IsPointerType()
+ """Prints a pointer value with Rust syntax"""
+ assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
sigil = "&"
- type_name = extract_type_name(val.GetType().GetName())
- if type_name and type_name[0:1] in ["&", "~", "*"]:
+ type_name = val.type.get_unqualified_type_name()
+ if type_name and type_name[0:1] in ["&", "*"]:
sigil = type_name[0:1]
- return sigil + hex(val.GetValueAsUnsigned()) #print_val(val.Dereference(), internal_dict)
+ return sigil + hex(val.as_integer())
def print_fixed_size_vec_val(val, internal_dict):
- assert val.GetType().GetTypeClass() == lldb.eTypeClassArray
+ assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ARRAY
+ lldb_val = val.get_wrapped_value()
output = "["
- for i in range(val.num_children):
- output += print_val(val.GetChildAtIndex(i), internal_dict)
- if i != val.num_children - 1:
+ for i in range(lldb_val.num_children):
+ output += print_val(lldb_val.GetChildAtIndex(i), internal_dict)
+ if i != lldb_val.num_children - 1:
output += ", "
output += "]"
def print_vec_slice_val(val, internal_dict):
- length = val.GetChildAtIndex(1).GetValueAsUnsigned()
-
- data_ptr_val = val.GetChildAtIndex(0)
- data_ptr_type = data_ptr_val.GetType()
-
- return "&[%s]" % print_array_of_values(val.GetName(),
- data_ptr_val,
+ (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val)
+ return "&[%s]" % print_array_of_values(val.get_wrapped_value().GetName(),
+ data_ptr,
length,
internal_dict)
def print_std_vec_val(val, internal_dict):
- length = val.GetChildAtIndex(1).GetValueAsUnsigned()
-
- # Vec<> -> Unique<> -> NonZero<> -> *T
- data_ptr_val = val.GetChildAtIndex(0).GetChildAtIndex(0).GetChildAtIndex(0)
- data_ptr_type = data_ptr_val.GetType()
-
- return "vec![%s]" % print_array_of_values(val.GetName(),
- data_ptr_val,
+ (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(val)
+ return "vec![%s]" % print_array_of_values(val.get_wrapped_value().GetName(),
+ data_ptr,
length,
internal_dict)
+def print_str_slice_val(val, internal_dict):
+ (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(val)
+ return read_utf8_string(data_ptr, length)
+
+def print_std_string_val(val, internal_dict):
+ vec = val.get_child_at_index(0)
+ (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec)
+ return read_utf8_string(data_ptr, length)
+
#=--------------------------------------------------------------------------------------------------
# Helper Functions
#=--------------------------------------------------------------------------------------------------
-unqualified_type_markers = frozenset(["(", "[", "&", "*"])
-
-
-def extract_type_name(qualified_type_name):
- '''Extracts the type name from a fully qualified path'''
- if qualified_type_name[0] in unqualified_type_markers:
- return qualified_type_name
-
- end_of_search = qualified_type_name.find("<")
- if end_of_search < 0:
- end_of_search = len(qualified_type_name)
-
- index = qualified_type_name.rfind("::", 0, end_of_search)
- if index < 0:
- return qualified_type_name
- else:
- return qualified_type_name[index + 2:]
-
-
-def type_is_tuple_like(ty):
- '''Returns true of this is a type with field names (struct, struct-like enum variant)'''
- for field in ty.fields:
- if field.GetName() == "RUST$ENUM$DISR":
- # Ignore the enum discriminant field if there is one.
- continue
- if (field.GetName() is None) or (re.match(r"__\d+$", field.GetName()) is None):
- return False
- return True
-
-
-def is_vec_slice(val):
- ty = val.GetType()
- if ty.GetTypeClass() != lldb.eTypeClassStruct:
- return False
-
- if ty.GetNumberOfFields() != 2:
- return False
-
- if ty.GetFieldAtIndex(0).GetName() != "data_ptr":
- return False
-
- if ty.GetFieldAtIndex(1).GetName() != "length":
- return False
-
- type_name = extract_type_name(ty.GetName()).replace("&'static", "&").replace(" ", "")
- return type_name.startswith("&[") and type_name.endswith("]")
-
-def is_std_vec(val):
- ty = val.GetType()
- if ty.GetTypeClass() != lldb.eTypeClassStruct:
- return False
-
- if ty.GetNumberOfFields() != 3:
- return False
-
- if ty.GetFieldAtIndex(0).GetName() != "ptr":
- return False
-
- if ty.GetFieldAtIndex(1).GetName() != "len":
- return False
-
- if ty.GetFieldAtIndex(2).GetName() != "cap":
- return False
-
- return ty.GetName().startswith("collections::vec::Vec<")
-
-
def print_array_of_values(array_name, data_ptr_val, length, internal_dict):
- '''Prints a contigous memory range, interpreting it as values of the
- pointee-type of data_ptr_val.'''
+ """Prints a contigous memory range, interpreting it as values of the
+ pointee-type of data_ptr_val."""
- data_ptr_type = data_ptr_val.GetType()
- assert data_ptr_type.IsPointerType()
+ data_ptr_type = data_ptr_val.type
+ assert data_ptr_type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
- element_type = data_ptr_type.GetPointeeType()
+ element_type = data_ptr_type.get_wrapped_value().GetPointeeType()
element_type_size = element_type.GetByteSize()
- start_address = data_ptr_val.GetValueAsUnsigned()
+ start_address = data_ptr_val.as_integer()
+ raw_value = data_ptr_val.get_wrapped_value()
def render_element(i):
address = start_address + i * element_type_size
- element_val = data_ptr_val.CreateValueFromAddress(array_name + ("[%s]" % i),
- address,
- element_type)
+ element_val = raw_value.CreateValueFromAddress(array_name + ("[%s]" % i),
+ address,
+ element_type)
return print_val(element_val, internal_dict)
return ', '.join([render_element(i) for i in range(length)])
+
+
+def read_utf8_string(ptr_val, byte_count):
+ error = lldb.SBError()
+ process = ptr_val.get_wrapped_value().GetProcess()
+ data = process.ReadMemory(ptr_val.as_integer(), byte_count, error)
+ if error.Success():
+ return '"%s"' % data.decode(encoding='UTF-8')
+ else:
+ return '<error: %s>' % error.GetCString()