import lldb import re import debugger_pretty_printers_common as rustpp #=============================================================================== # LLDB Pretty Printing Module for Rust #=============================================================================== class LldbType(rustpp.Type): def __init__(self, ty): super(LldbType, self).__init__() self.ty = ty self.fields = None def get_unqualified_type_name(self): qualified_name = self.ty.GetName() if qualified_name is None: return qualified_name return rustpp.extract_type_name(qualified_name).replace("&'static ", "&") def get_dwarf_type_kind(self): type_class = self.ty.GetTypeClass() 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_REGULAR_UNION 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) 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 type_kind == rustpp.TYPE_KIND_SLICE: return print_vec_slice_val(val, internal_dict) 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) 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) 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) # No pretty printer has been found return lldb_val.GetValue() #=-------------------------------------------------------------------------------------------------- # 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 or val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_UNION) 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)" separator = ", " else: template = "%(type_name)s {\n%(body)s\n}" separator = ", \n" fields = val.type.get_fields() def render_child(child_index): this = "" if not is_tuple_like: field_name = fields[child_index].name this += field_name + ": " field_val = val.get_child_at_index(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 + rustpp.extract_type_name(field.GetType().GetName()) else: return this + "" return this + print_val(field_val.get_wrapped_value(), internal_dict) 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_pointer_val(val, internal_dict): """Prints a pointer value with Rust syntax""" assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR sigil = "&" 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.as_integer()) def print_fixed_size_vec_val(val, internal_dict): assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ARRAY lldb_val = val.get_wrapped_value() output = "[" 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 += "]" return output def print_vec_slice_val(val, internal_dict): (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, 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 #=-------------------------------------------------------------------------------------------------- def print_array_of_values(array_name, data_ptr_val, length, internal_dict): """Prints a contiguous memory range, interpreting it as values of the pointee-type of data_ptr_val.""" 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.get_wrapped_value().GetPointeeType() element_type_size = element_type.GetByteSize() 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 = 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): if byte_count == 0: return '""' 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.GetCString()