diff --git a/mk/debuggers.mk b/mk/debuggers.mk index e6a2174f84a82..aa7b62e13b86c 100644 --- a/mk/debuggers.mk +++ b/mk/debuggers.mk @@ -15,7 +15,8 @@ ## GDB ## DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB=gdb_load_rust_pretty_printers.py \ - gdb_rust_pretty_printing.py + gdb_rust_pretty_printing.py \ + debugger_pretty_printers_common.py DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB_ABS=\ $(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB), \ $(CFG_SRC_DIR)src/etc/$(script)) @@ -27,7 +28,8 @@ DEBUGGER_BIN_SCRIPTS_GDB_ABS=\ ## LLDB ## -DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB=lldb_rust_formatters.py +DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB=lldb_rust_formatters.py \ + debugger_pretty_printers_common.py DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB_ABS=\ $(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB), \ $(CFG_SRC_DIR)src/etc/$(script)) diff --git a/src/etc/debugger_pretty_printers_common.py b/src/etc/debugger_pretty_printers_common.py new file mode 100644 index 0000000000000..6e667b37a9c5c --- /dev/null +++ b/src/etc/debugger_pretty_printers_common.py @@ -0,0 +1,328 @@ +# Copyright 2015 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +""" +This module provides an abstraction layer over common Rust pretty printing +functionality needed by both GDB and LLDB. +""" + +import re + +# Type codes that indicate the kind of type as it appears in DWARF debug +# information. This code alone is not sufficient to determine the Rust type. +# For example structs, tuples, fat pointers, or enum variants will all have +# DWARF_TYPE_CODE_STRUCT. +DWARF_TYPE_CODE_STRUCT = 1 +DWARF_TYPE_CODE_UNION = 2 +DWARF_TYPE_CODE_PTR = 3 +DWARF_TYPE_CODE_ARRAY = 4 +DWARF_TYPE_CODE_ENUM = 5 + +# These constants specify the most specific kind of type that could be +# determined for a given value. +TYPE_KIND_UNKNOWN = -1 +TYPE_KIND_EMPTY = 0 +TYPE_KIND_SLICE = 1 +TYPE_KIND_REGULAR_STRUCT = 2 +TYPE_KIND_TUPLE = 3 +TYPE_KIND_TUPLE_STRUCT = 4 +TYPE_KIND_CSTYLE_VARIANT = 5 +TYPE_KIND_TUPLE_VARIANT = 6 +TYPE_KIND_STRUCT_VARIANT = 7 +TYPE_KIND_STR_SLICE = 8 +TYPE_KIND_STD_VEC = 9 +TYPE_KIND_STD_STRING = 10 +TYPE_KIND_REGULAR_ENUM = 11 +TYPE_KIND_COMPRESSED_ENUM = 12 +TYPE_KIND_SINGLETON_ENUM = 13 +TYPE_KIND_CSTYLE_ENUM = 14 +TYPE_KIND_PTR = 15 +TYPE_KIND_FIXED_SIZE_VEC = 16 + +ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$" +ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR" + +# Slice related constants +SLICE_FIELD_NAME_DATA_PTR = "data_ptr" +SLICE_FIELD_NAME_LENGTH = "length" +SLICE_FIELD_NAMES = [SLICE_FIELD_NAME_DATA_PTR, SLICE_FIELD_NAME_LENGTH] + +# std::Vec<> related constants +STD_VEC_FIELD_NAME_DATA_PTR = "ptr" +STD_VEC_FIELD_NAME_LENGTH = "len" +STD_VEC_FIELD_NAME_CAPACITY = "cap" +STD_VEC_FIELD_NAMES = [STD_VEC_FIELD_NAME_DATA_PTR, + STD_VEC_FIELD_NAME_LENGTH, + STD_VEC_FIELD_NAME_CAPACITY] + +# std::String related constants +STD_STRING_FIELD_NAMES = ["vec"] + + +class Type(object): + """ + This class provides a common interface for type-oriented operations. + Sub-classes are supposed to wrap a debugger-specific type-object and + provide implementations for the abstract methods in this class. + """ + + def __init__(self): + self.__type_kind = None + + def get_unqualified_type_name(self): + """ + Implementations of this method should return the unqualified name of the + type-object they are wrapping. Some examples: + + 'int' -> 'int' + 'std::vec::Vec' -> 'Vec' + '&std::option::Option' -> '&std::option::Option' + + As you can see, type arguments stay fully qualified. + """ + raise NotImplementedError("Override this method") + + def get_dwarf_type_kind(self): + """ + Implementations of this method should return the correct + DWARF_TYPE_CODE_* value for the wrapped type-object. + """ + raise NotImplementedError("Override this method") + + def get_fields(self): + """ + Implementations of this method should return a list of field-objects of + this type. For Rust-enums (i.e. with DWARF_TYPE_CODE_UNION) these field- + objects represent the variants of the enum. Field-objects must have a + `name` attribute that gives their name as specified in DWARF. + """ + assert ((self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT) or + (self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION)) + raise NotImplementedError("Override this method") + + def get_wrapped_value(self): + """ + Returns the debugger-specific type-object wrapped by this object. This + is sometimes needed for doing things like pointer-arithmetic in GDB. + """ + raise NotImplementedError("Override this method") + + def get_type_kind(self): + """This method returns the TYPE_KIND_* value for this type-object.""" + if self.__type_kind is None: + dwarf_type_code = self.get_dwarf_type_kind() + + if dwarf_type_code == DWARF_TYPE_CODE_STRUCT: + self.__type_kind = self.__classify_struct() + elif dwarf_type_code == DWARF_TYPE_CODE_UNION: + self.__type_kind = self.__classify_union() + elif dwarf_type_code == DWARF_TYPE_CODE_PTR: + self.__type_kind = TYPE_KIND_PTR + elif dwarf_type_code == DWARF_TYPE_CODE_ARRAY: + self.__type_kind = TYPE_KIND_FIXED_SIZE_VEC + else: + self.__type_kind = TYPE_KIND_UNKNOWN + return self.__type_kind + + def __classify_struct(self): + assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT + + unqualified_type_name = self.get_unqualified_type_name() + + # STR SLICE + if unqualified_type_name == "&str": + return TYPE_KIND_STR_SLICE + + # REGULAR SLICE + if (unqualified_type_name.startswith("&[") and + unqualified_type_name.endswith("]") and + self.__conforms_to_field_layout(SLICE_FIELD_NAMES)): + return TYPE_KIND_SLICE + + fields = self.get_fields() + field_count = len(fields) + + # EMPTY STRUCT + if field_count == 0: + return TYPE_KIND_EMPTY + + # STD VEC + if (unqualified_type_name.startswith("Vec<") and + self.__conforms_to_field_layout(STD_VEC_FIELD_NAMES)): + return TYPE_KIND_STD_VEC + + # STD STRING + if (unqualified_type_name.startswith("String") and + self.__conforms_to_field_layout(STD_STRING_FIELD_NAMES)): + return TYPE_KIND_STD_STRING + + # ENUM VARIANTS + if fields[0].name == ENUM_DISR_FIELD_NAME: + if field_count == 1: + return TYPE_KIND_CSTYLE_VARIANT + elif self.__all_fields_conform_to_tuple_field_naming(1): + return TYPE_KIND_TUPLE_VARIANT + else: + return TYPE_KIND_STRUCT_VARIANT + + # TUPLE + if self.__all_fields_conform_to_tuple_field_naming(0): + if unqualified_type_name.startswith("("): + return TYPE_KIND_TUPLE + else: + return TYPE_KIND_TUPLE_STRUCT + + # REGULAR STRUCT + return TYPE_KIND_REGULAR_STRUCT + + + def __classify_union(self): + assert self.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION + + union_members = self.get_fields() + union_member_count = len(union_members) + if union_member_count == 0: + return TYPE_KIND_EMPTY + elif union_member_count == 1: + first_variant_name = union_members[0].name + if first_variant_name is None: + return TYPE_KIND_SINGLETON_ENUM + else: + assert first_variant_name.startswith(ENCODED_ENUM_PREFIX) + return TYPE_KIND_COMPRESSED_ENUM + else: + return TYPE_KIND_REGULAR_ENUM + + + def __conforms_to_field_layout(self, expected_fields): + actual_fields = self.get_fields() + actual_field_count = len(actual_fields) + + if actual_field_count != len(expected_fields): + return False + + for i in range(0, actual_field_count): + if actual_fields[i].name != expected_fields[i]: + return False + + return True + + def __all_fields_conform_to_tuple_field_naming(self, start_index): + fields = self.get_fields() + field_count = len(fields) + + for i in range(start_index, field_count): + field_name = fields[i].name + if (field_name is None) or (re.match(r"__\d+$", field_name) is None): + return False + return True + + +class Value(object): + """ + This class provides a common interface for value-oriented operations. + Sub-classes are supposed to wrap a debugger-specific value-object and + provide implementations for the abstract methods in this class. + """ + def __init__(self, ty): + self.type = ty + + def get_child_at_index(self, index): + """Returns the value of the field, array element or variant at the given index""" + raise NotImplementedError("Override this method") + + def as_integer(self): + """ + Try to convert the wrapped value into a Python integer. This should + always succeed for values that are pointers or actual integers. + """ + raise NotImplementedError("Override this method") + + def get_wrapped_value(self): + """ + Returns the debugger-specific value-object wrapped by this object. This + is sometimes needed for doing things like pointer-arithmetic in GDB. + """ + raise NotImplementedError("Override this method") + + +class EncodedEnumInfo(object): + """ + This class provides facilities for handling enum values with compressed + encoding where a non-null field in one variant doubles as the discriminant. + """ + + def __init__(self, enum_val): + assert enum_val.type.get_type_kind() == TYPE_KIND_COMPRESSED_ENUM + variant_name = enum_val.type.get_fields()[0].name + last_separator_index = variant_name.rfind("$") + start_index = len(ENCODED_ENUM_PREFIX) + indices_substring = variant_name[start_index:last_separator_index].split("$") + self.__enum_val = enum_val + self.__disr_field_indices = [int(index) for index in indices_substring] + self.__null_variant_name = variant_name[last_separator_index + 1:] + + def is_null_variant(self): + ty = self.__enum_val.type + sole_variant_val = self.__enum_val.get_child_at_index(0) + discriminant_val = sole_variant_val + for disr_field_index in self.__disr_field_indices: + discriminant_val = discriminant_val.get_child_at_index(disr_field_index) + + # If the discriminant field is a fat pointer we have to consider the + # first word as the true discriminant + if discriminant_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_STRUCT: + discriminant_val = discriminant_val.get_child_at_index(0) + + return discriminant_val.as_integer() == 0 + + def get_non_null_variant_val(self): + return self.__enum_val.get_child_at_index(0) + + def get_null_variant_name(self): + return self.__null_variant_name + + +def get_discriminant_value_as_integer(enum_val): + assert enum_val.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_UNION + # we can take any variant here because the discriminant has to be the same + # for all of them. + variant_val = enum_val.get_child_at_index(0) + disr_val = variant_val.get_child_at_index(0) + return disr_val.as_integer() + + +def extract_length_ptr_and_cap_from_std_vec(vec_val): + assert vec_val.type.get_type_kind() == TYPE_KIND_STD_VEC + length_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_LENGTH) + ptr_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_DATA_PTR) + cap_field_index = STD_VEC_FIELD_NAMES.index(STD_VEC_FIELD_NAME_CAPACITY) + + length = vec_val.get_child_at_index(length_field_index).as_integer() + vec_ptr_val = vec_val.get_child_at_index(ptr_field_index) + capacity = vec_val.get_child_at_index(cap_field_index).as_integer() + + unique_ptr_val = vec_ptr_val.get_child_at_index(0) + data_ptr = unique_ptr_val.get_child_at_index(0) + assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR + return (length, data_ptr, capacity) + +def extract_length_and_ptr_from_slice(slice_val): + assert (slice_val.type.get_type_kind() == TYPE_KIND_SLICE or + slice_val.type.get_type_kind() == TYPE_KIND_STR_SLICE) + + length_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_LENGTH) + ptr_field_index = SLICE_FIELD_NAMES.index(SLICE_FIELD_NAME_DATA_PTR) + + length = slice_val.get_child_at_index(length_field_index).as_integer() + data_ptr = slice_val.get_child_at_index(ptr_field_index) + + assert data_ptr.type.get_dwarf_type_kind() == DWARF_TYPE_CODE_PTR + return (length, data_ptr) diff --git a/src/etc/gdb_rust_pretty_printing.py b/src/etc/gdb_rust_pretty_printing.py index fc4d15a5168db..f93f3490215d1 100755 --- a/src/etc/gdb_rust_pretty_printing.py +++ b/src/etc/gdb_rust_pretty_printing.py @@ -10,246 +10,265 @@ import gdb import re +import debugger_pretty_printers_common as rustpp #=============================================================================== # GDB Pretty Printing Module for Rust #=============================================================================== +class GdbType(rustpp.Type): -def register_printers(objfile): - "Registers Rust pretty printers for the given objfile" - objfile.pretty_printers.append(rust_pretty_printer_lookup_function) + def __init__(self, ty): + super(GdbType, self).__init__() + self.ty = ty + self.fields = None + def get_unqualified_type_name(self): + tag = self.ty.tag -def rust_pretty_printer_lookup_function(val): - "Returns the correct Rust pretty printer for the given value if there is one" - type_code = val.type.code + if tag is None: + return tag - if type_code == gdb.TYPE_CODE_STRUCT: - struct_kind = classify_struct(val.type) + return tag.replace("&'static ", "&") - if struct_kind == STRUCT_KIND_SLICE: - return RustSlicePrinter(val) + def get_dwarf_type_kind(self): + if self.ty.code == gdb.TYPE_CODE_STRUCT: + return rustpp.DWARF_TYPE_CODE_STRUCT - if struct_kind == STRUCT_KIND_STR_SLICE: - return RustStringSlicePrinter(val) + if self.ty.code == gdb.TYPE_CODE_UNION: + return rustpp.DWARF_TYPE_CODE_UNION - if struct_kind == STRUCT_KIND_STD_VEC: - return RustStdVecPrinter(val) + if self.ty.code == gdb.TYPE_CODE_PTR: + return rustpp.DWARF_TYPE_CODE_PTR - if struct_kind == STRUCT_KIND_STD_STRING: - return RustStdStringPrinter(val) + if self.ty.code == gdb.TYPE_CODE_ENUM: + return rustpp.DWARF_TYPE_CODE_ENUM - if struct_kind == STRUCT_KIND_TUPLE: - return RustTuplePrinter(val) + 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 - if struct_kind == STRUCT_KIND_TUPLE_STRUCT: - return RustTupleStructPrinter(val, False) + def get_wrapped_value(self): + return self.ty - if struct_kind == STRUCT_KIND_CSTYLE_VARIANT: - return RustCStyleEnumPrinter(val[get_field_at_index(val, 0)]) - if struct_kind == STRUCT_KIND_TUPLE_VARIANT: - return RustTupleStructPrinter(val, True) +class GdbValue(rustpp.Value): + def __init__(self, gdb_val): + super(GdbValue, self).__init__(GdbType(gdb_val.type)) + self.gdb_val = gdb_val + self.children = {} - if struct_kind == STRUCT_KIND_STRUCT_VARIANT: - return RustStructPrinter(val, True) + def get_child_at_index(self, index): + child = self.children.get(index) + if child is None: + gdb_field = get_field_at_index(self.gdb_val, index) + child = GdbValue(self.gdb_val[gdb_field]) + self.children[index] = child + return child - return RustStructPrinter(val, False) + def as_integer(self): + return int(self.gdb_val) - # Enum handling - if type_code == gdb.TYPE_CODE_UNION: - enum_members = list(val.type.fields()) - enum_member_count = len(enum_members) + def get_wrapped_value(self): + return self.gdb_val - if enum_member_count == 0: - return RustStructPrinter(val, False) - if enum_member_count == 1: - first_variant_name = enum_members[0].name - if first_variant_name is None: - # This is a singleton enum - return rust_pretty_printer_lookup_function(val[enum_members[0]]) - else: - assert first_variant_name.startswith("RUST$ENCODED$ENUM$") - # This is a space-optimized enum. - # This means this enum has only two states, and Rust uses one - # of the fields somewhere in the struct to determine which of - # the two states it's in. The location of the field is encoded - # in the name as something like - # RUST$ENCODED$ENUM$(num$)*name_of_zero_state - last_separator_index = first_variant_name.rfind("$") - start_index = len("RUST$ENCODED$ENUM$") - disr_field_indices = first_variant_name[start_index:last_separator_index].split("$") - disr_field_indices = [int(index) for index in disr_field_indices] - - sole_variant_val = val[enum_members[0]] - discriminant = sole_variant_val - for disr_field_index in disr_field_indices: - disr_field = get_field_at_index(discriminant, disr_field_index) - discriminant = discriminant[disr_field] - - # If the discriminant field is a fat pointer we have to consider the - # first word as the true discriminant - if discriminant.type.code == gdb.TYPE_CODE_STRUCT: - discriminant = discriminant[get_field_at_index(discriminant, 0)] - - if discriminant == 0: - null_variant_name = first_variant_name[last_separator_index + 1:] - return IdentityPrinter(null_variant_name) - - return rust_pretty_printer_lookup_function(sole_variant_val) +def register_printers(objfile): + """Registers Rust pretty printers for the given objfile""" + objfile.pretty_printers.append(rust_pretty_printer_lookup_function) - # This is a regular enum, extract the discriminant - discriminant_name, discriminant_val = extract_discriminant_value(val) - return rust_pretty_printer_lookup_function(val[enum_members[discriminant_val]]) - # No pretty printer has been found - return None +def rust_pretty_printer_lookup_function(gdb_val): + """ + Returns the correct Rust pretty printer for the given value + if there is one + """ -#=------------------------------------------------------------------------------ -# Pretty Printer Classes -#=------------------------------------------------------------------------------ + val = GdbValue(gdb_val) + type_kind = val.type.get_type_kind() + if (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or + type_kind == rustpp.TYPE_KIND_EMPTY): + return RustStructPrinter(val, + omit_first_field = False, + omit_type_name = False, + is_tuple_like = False) -class RustStructPrinter: - def __init__(self, val, hide_first_field): - self.val = val - self.hide_first_field = hide_first_field + if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT: + return RustStructPrinter(val, + omit_first_field = True, + omit_type_name = False, + is_tuple_like = False) - def to_string(self): - return self.val.type.tag + if type_kind == rustpp.TYPE_KIND_SLICE: + return RustSlicePrinter(val) - def children(self): - cs = [] - for field in self.val.type.fields(): - field_name = field.name - # Normally the field name is used as a key to access the field - # value, because that's also supported in older versions of GDB... - field_key = field_name - if field_name is None: - field_name = "" - # ... but for fields without a name (as in tuples), we have to - # fall back to the newer method of using the field object - # directly as key. In older versions of GDB, this will just - # fail. - field_key = field - name_value_tuple = (field_name, self.val[field_key]) - cs.append(name_value_tuple) - - if self.hide_first_field: - cs = cs[1:] + if type_kind == rustpp.TYPE_KIND_STR_SLICE: + return RustStringSlicePrinter(val) - return cs + if type_kind == rustpp.TYPE_KIND_STD_VEC: + return RustStdVecPrinter(val) + if type_kind == rustpp.TYPE_KIND_STD_STRING: + return RustStdStringPrinter(val) -class RustTuplePrinter: - def __init__(self, val): - self.val = val + if type_kind == rustpp.TYPE_KIND_TUPLE: + return RustStructPrinter(val, + omit_first_field = False, + omit_type_name = True, + is_tuple_like = True) - def to_string(self): - return None + if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT: + return RustStructPrinter(val, + omit_first_field = False, + omit_type_name = False, + is_tuple_like = True) - def children(self): - cs = [] - for field in self.val.type.fields(): - cs.append(("", self.val[field])) + if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT: + return RustCStyleVariantPrinter(val.get_child_at_index(0)) - return cs + if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT: + return RustStructPrinter(val, + omit_first_field = True, + omit_type_name = False, + is_tuple_like = True) - def display_hint(self): - return "array" + if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM: + variant = get_field_at_index(gdb_val, 0) + return rust_pretty_printer_lookup_function(gdb_val[variant]) + 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) + variant = get_field_at_index(gdb_val, discriminant_val) + return rust_pretty_printer_lookup_function(gdb_val[variant]) + + if type_kind == rustpp.TYPE_KIND_COMPRESSED_ENUM: + encoded_enum_info = rustpp.EncodedEnumInfo(val) + if encoded_enum_info.is_null_variant(): + return IdentityPrinter(encoded_enum_info.get_null_variant_name()) -class RustTupleStructPrinter: - def __init__(self, val, hide_first_field): - self.val = val - self.hide_first_field = hide_first_field + non_null_val = encoded_enum_info.get_non_null_variant_val() + return rust_pretty_printer_lookup_function(non_null_val.get_wrapped_value()) + + # No pretty printer has been found + return None + + +#=------------------------------------------------------------------------------ +# Pretty Printer Classes +#=------------------------------------------------------------------------------ +class RustStructPrinter: + def __init__(self, val, omit_first_field, omit_type_name, is_tuple_like): + self.__val = val + self.__omit_first_field = omit_first_field + self.__omit_type_name = omit_type_name + self.__is_tuple_like = is_tuple_like def to_string(self): - return self.val.type.tag + if self.__omit_type_name: + return None + return self.__val.type.get_unqualified_type_name() def children(self): cs = [] - for field in self.val.type.fields(): - cs.append(("", self.val[field])) + wrapped_value = self.__val.get_wrapped_value() + + for field in self.__val.type.get_fields(): + field_value = wrapped_value[field.name] + if self.__is_tuple_like: + cs.append(("", field_value)) + else: + cs.append((field.name, field_value)) - if self.hide_first_field: + if self.__omit_first_field: cs = cs[1:] return cs def display_hint(self): - return "array" + if self.__is_tuple_like: + return "array" + else: + return "" + class RustSlicePrinter: def __init__(self, val): - self.val = val + self.__val = val def display_hint(self): return "array" def to_string(self): - length = int(self.val["length"]) - return self.val.type.tag + ("(len: %i)" % length) + (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) + return (self.__val.type.get_unqualified_type_name() + + ("(len: %i)" % length)) def children(self): cs = [] - length = int(self.val["length"]) - data_ptr = self.val["data_ptr"] - assert data_ptr.type.code == gdb.TYPE_CODE_PTR - pointee_type = data_ptr.type.target() + (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) + assert data_ptr.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR + raw_ptr = data_ptr.get_wrapped_value() for index in range(0, length): - cs.append((str(index), (data_ptr + index).dereference())) + cs.append((str(index), (raw_ptr + index).dereference())) return cs + class RustStringSlicePrinter: def __init__(self, val): - self.val = val + self.__val = val def to_string(self): - slice_byte_len = self.val["length"] - return '"%s"' % self.val["data_ptr"].string(encoding="utf-8", length=slice_byte_len) + (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val) + raw_ptr = data_ptr.get_wrapped_value() + return '"%s"' % raw_ptr.string(encoding="utf-8", length=length) + class RustStdVecPrinter: def __init__(self, val): - self.val = val + self.__val = val def display_hint(self): return "array" def to_string(self): - length = int(self.val["len"]) - cap = int(self.val["cap"]) - return self.val.type.tag + ("(len: %i, cap: %i)" % (length, cap)) + (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val) + return (self.__val.type.get_unqualified_type_name() + + ("(len: %i, cap: %i)" % (length, cap))) def children(self): cs = [] - (length, data_ptr) = extract_length_and_data_ptr_from_std_vec(self.val) - pointee_type = data_ptr.type.target() - + (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val) + gdb_ptr = data_ptr.get_wrapped_value() for index in range(0, length): - cs.append((str(index), (data_ptr + index).dereference())) + cs.append((str(index), (gdb_ptr + index).dereference())) return cs + class RustStdStringPrinter: def __init__(self, val): - self.val = val + self.__val = val def to_string(self): - (length, data_ptr) = extract_length_and_data_ptr_from_std_vec(self.val["vec"]) - return '"%s"' % data_ptr.string(encoding="utf-8", length=length) + vec = self.__val.get_child_at_index(0) + (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(vec) + return '"%s"' % data_ptr.get_wrapped_value().string(encoding="utf-8", + length=length) -class RustCStyleEnumPrinter: +class RustCStyleVariantPrinter: def __init__(self, val): - assert val.type.code == gdb.TYPE_CODE_ENUM - self.val = val + assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ENUM + self.__val = val def to_string(self): - return str(self.val) + return str(self.__val.get_wrapped_value()) class IdentityPrinter: @@ -259,91 +278,11 @@ def __init__(self, string): def to_string(self): return self.string -STRUCT_KIND_REGULAR_STRUCT = 0 -STRUCT_KIND_TUPLE_STRUCT = 1 -STRUCT_KIND_TUPLE = 2 -STRUCT_KIND_TUPLE_VARIANT = 3 -STRUCT_KIND_STRUCT_VARIANT = 4 -STRUCT_KIND_CSTYLE_VARIANT = 5 -STRUCT_KIND_SLICE = 6 -STRUCT_KIND_STR_SLICE = 7 -STRUCT_KIND_STD_VEC = 8 -STRUCT_KIND_STD_STRING = 9 - - -def classify_struct(type): - # print("\nclassify_struct: tag=%s\n" % type.tag) - if type.tag == "&str": - return STRUCT_KIND_STR_SLICE - - if type.tag.startswith("&[") and type.tag.endswith("]"): - return STRUCT_KIND_SLICE - - fields = list(type.fields()) - field_count = len(fields) - - if field_count == 0: - return STRUCT_KIND_REGULAR_STRUCT - - if (field_count == 3 and - fields[0].name == "ptr" and - fields[1].name == "len" and - fields[2].name == "cap" and - type.tag.startswith("Vec<")): - return STRUCT_KIND_STD_VEC - - if (field_count == 1 and - fields[0].name == "vec" and - type.tag == "String"): - return STRUCT_KIND_STD_STRING - - if fields[0].name == "RUST$ENUM$DISR": - if field_count == 1: - return STRUCT_KIND_CSTYLE_VARIANT - elif all_fields_conform_to_tuple_field_naming(fields, 1): - return STRUCT_KIND_TUPLE_VARIANT - else: - return STRUCT_KIND_STRUCT_VARIANT - - if all_fields_conform_to_tuple_field_naming(fields, 0): - if type.tag.startswith("("): - return STRUCT_KIND_TUPLE - else: - return STRUCT_KIND_TUPLE_STRUCT - return STRUCT_KIND_REGULAR_STRUCT - - -def extract_discriminant_value(enum_val): - assert enum_val.type.code == gdb.TYPE_CODE_UNION - for variant_descriptor in enum_val.type.fields(): - variant_val = enum_val[variant_descriptor] - for field in variant_val.type.fields(): - return (field.name, int(variant_val[field])) - - -def first_field(val): - for field in val.type.fields(): - return field - -def get_field_at_index(val, index): +def get_field_at_index(gdb_val, index): i = 0 - for field in val.type.fields(): + for field in gdb_val.type.fields(): if i == index: return field i += 1 return None - -def all_fields_conform_to_tuple_field_naming(fields, start_index): - for i in range(start_index, len(fields)): - if (fields[i].name is None) or (re.match(r"__\d+$", fields[i].name) is None): - return False - return True - -def extract_length_and_data_ptr_from_std_vec(vec_val): - length = int(vec_val["len"]) - vec_ptr_val = vec_val["ptr"] - unique_ptr_val = vec_ptr_val[first_field(vec_ptr_val)] - data_ptr = unique_ptr_val[first_field(unique_ptr_val)] - assert data_ptr.type.code == gdb.TYPE_CODE_PTR - return (length, data_ptr) diff --git a/src/etc/lldb_rust_formatters.py b/src/etc/lldb_rust_formatters.py index 2aaa158875817..c22a60abf3f76 100644 --- a/src/etc/lldb_rust_formatters.py +++ b/src/etc/lldb_rust_formatters.py @@ -10,58 +10,177 @@ 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 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() -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_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) - if is_vec_slice(val): + 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) - 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) + + 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() -def print_struct_val_starting_from(field_start_index, val, internal_dict): + 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.GetType().GetTypeClass() == lldb.eTypeClassStruct - - t = val.GetType() - type_name = extract_type_name(t.GetName()) - num_children = val.num_children - - if (num_children - field_start_index) == 0: - # The only field of this struct is the enum discriminant - return type_name + assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT - is_tuple_like = type_is_tuple_like(t) + 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)" @@ -70,20 +189,18 @@ def print_struct_val_starting_from(field_start_index, val, internal_dict): 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: @@ -91,95 +208,38 @@ def render_child(child_index): else: return this + "" - return this + print_val(field_val, internal_dict) + 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, num_children)]) + 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$$ - last_separator_index = first_variant_name.rfind("$") - if last_separator_index == -1: - return "" % 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 "" % 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 "" - - 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() + 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 += "]" @@ -187,39 +247,38 @@ def print_fixed_size_vec_val(val, internal_dict): 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(["(", "[", "&", "*"]) - +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: + if qualified_type_name[0] in UNQUALIFIED_TYPE_MARKERS: return qualified_type_name end_of_search = qualified_type_name.find("<") @@ -232,72 +291,34 @@ def extract_type_name(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.''' - 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.GetCString() diff --git a/src/test/debuginfo/option-like-enum.rs b/src/test/debuginfo/option-like-enum.rs index 65a83088d8051..fba21d9a553ba 100644 --- a/src/test/debuginfo/option-like-enum.rs +++ b/src/test/debuginfo/option-like-enum.rs @@ -67,7 +67,7 @@ // lldb-check:[...]$5 = Void // lldb-command:print some_str -// lldb-check:[...]$6 = Some(&str { data_ptr: [...], length: 3 }) +// lldb-check:[...]$6 = Some("abc") // lldb-command:print none_str // lldb-check:[...]$7 = None diff --git a/src/test/debuginfo/gdb-pretty-std.rs b/src/test/debuginfo/pretty-std.rs similarity index 68% rename from src/test/debuginfo/gdb-pretty-std.rs rename to src/test/debuginfo/pretty-std.rs index c42f7f19839fd..576621737e6b6 100644 --- a/src/test/debuginfo/gdb-pretty-std.rs +++ b/src/test/debuginfo/pretty-std.rs @@ -10,10 +10,12 @@ // ignore-windows failing on win32 bot // ignore-freebsd: gdb package too new -// ignore-lldb // ignore-android: FIXME(#10381) // compile-flags:-g // min-gdb-version 7.7 +// min-lldb-version: 310 + +// === GDB TESTS =================================================================================== // gdb-command: run @@ -35,6 +37,30 @@ // gdb-command: print none // gdb-check:$6 = None + +// === LLDB TESTS ================================================================================== + +// lldb-command: run + +// lldb-command: print slice +// lldb-check:[...]$0 = &[0, 1, 2, 3] + +// lldb-command: print vec +// lldb-check:[...]$1 = vec![4, 5, 6, 7] + +// lldb-command: print str_slice +// lldb-check:[...]$2 = "IAMA string slice!" + +// lldb-command: print string +// lldb-check:[...]$3 = "IAMA string!" + +// lldb-command: print some +// lldb-check:[...]$4 = Some(8) + +// lldb-command: print none +// lldb-check:[...]$5 = None + + #![allow(unused_variables)] fn main() {