From d136714e04fa244fb45a8459cb717dc682208697 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Sat, 9 May 2015 16:48:36 +0200 Subject: [PATCH] debuginfo: Create common debugger pretty printer module. GDB and LLDB pretty printers have some common functionality and also access some common information, such as the layout of standard library types. So far, this information has been duplicated in the two pretty printing python modules. This commit introduces a common module used by both debuggers. --- mk/debuggers.mk | 6 +- src/etc/debugger_pretty_printers_common.py | 328 ++++++++++++++ src/etc/gdb_rust_pretty_printing.py | 399 ++++++++---------- src/etc/lldb_rust_formatters.py | 395 +++++++++-------- src/test/debuginfo/option-like-enum.rs | 2 +- .../{gdb-pretty-std.rs => pretty-std.rs} | 28 +- 6 files changed, 737 insertions(+), 421 deletions(-) create mode 100644 src/etc/debugger_pretty_printers_common.py rename src/test/debuginfo/{gdb-pretty-std.rs => pretty-std.rs} (68%) 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() {