1
Fork 0

Auto merge of #25905 - michaelwoerister:lldb-pp-strings, r=brson

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 PR introduces a common module used by both debuggers.

This PR also implements proper rendering of `String` and `&str` values in LLDB.
This commit is contained in:
bors 2015-06-02 13:07:41 +00:00
commit c800b22e95
6 changed files with 742 additions and 426 deletions

View file

@ -15,7 +15,8 @@
## GDB ## ## GDB ##
DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB=gdb_load_rust_pretty_printers.py \ 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=\ DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB_ABS=\
$(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB), \ $(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_GDB), \
$(CFG_SRC_DIR)src/etc/$(script)) $(CFG_SRC_DIR)src/etc/$(script))
@ -27,7 +28,8 @@ DEBUGGER_BIN_SCRIPTS_GDB_ABS=\
## LLDB ## ## 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=\ DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB_ABS=\
$(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB), \ $(foreach script,$(DEBUGGER_RUSTLIB_ETC_SCRIPTS_LLDB), \
$(CFG_SRC_DIR)src/etc/$(script)) $(CFG_SRC_DIR)src/etc/$(script))

View file

@ -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 <LICENSE-APACHE or
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<std::string::String>' -> 'Vec<std::string::String>'
'&std::option::Option<std::string::String>' -> '&std::option::Option<std::string::String>'
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)

View file

@ -10,246 +10,265 @@
import gdb import gdb
import re import re
import debugger_pretty_printers_common as rustpp
#=============================================================================== #===============================================================================
# GDB Pretty Printing Module for Rust # GDB Pretty Printing Module for Rust
#=============================================================================== #===============================================================================
class GdbType(rustpp.Type):
def __init__(self, ty):
super(GdbType, self).__init__()
self.ty = ty
self.fields = None
def get_unqualified_type_name(self):
tag = self.ty.tag
if tag is None:
return tag
return tag.replace("&'static ", "&")
def get_dwarf_type_kind(self):
if self.ty.code == gdb.TYPE_CODE_STRUCT:
return rustpp.DWARF_TYPE_CODE_STRUCT
if self.ty.code == gdb.TYPE_CODE_UNION:
return rustpp.DWARF_TYPE_CODE_UNION
if self.ty.code == gdb.TYPE_CODE_PTR:
return rustpp.DWARF_TYPE_CODE_PTR
if self.ty.code == gdb.TYPE_CODE_ENUM:
return rustpp.DWARF_TYPE_CODE_ENUM
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 GdbValue(rustpp.Value):
def __init__(self, gdb_val):
super(GdbValue, self).__init__(GdbType(gdb_val.type))
self.gdb_val = gdb_val
self.children = {}
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
def as_integer(self):
return int(self.gdb_val)
def get_wrapped_value(self):
return self.gdb_val
def register_printers(objfile): def register_printers(objfile):
"Registers Rust pretty printers for the given objfile" """Registers Rust pretty printers for the given objfile"""
objfile.pretty_printers.append(rust_pretty_printer_lookup_function) objfile.pretty_printers.append(rust_pretty_printer_lookup_function)
def rust_pretty_printer_lookup_function(val): def rust_pretty_printer_lookup_function(gdb_val):
"Returns the correct Rust pretty printer for the given value if there is one" """
type_code = val.type.code Returns the correct Rust pretty printer for the given value
if there is one
"""
if type_code == gdb.TYPE_CODE_STRUCT: val = GdbValue(gdb_val)
struct_kind = classify_struct(val.type) type_kind = val.type.get_type_kind()
if struct_kind == STRUCT_KIND_SLICE: if (type_kind == rustpp.TYPE_KIND_REGULAR_STRUCT or
return RustSlicePrinter(val) type_kind == rustpp.TYPE_KIND_EMPTY):
return RustStructPrinter(val,
omit_first_field = False,
omit_type_name = False,
is_tuple_like = False)
if struct_kind == STRUCT_KIND_STR_SLICE: if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT:
return RustStringSlicePrinter(val) return RustStructPrinter(val,
omit_first_field = True,
omit_type_name = False,
is_tuple_like = False)
if struct_kind == STRUCT_KIND_STD_VEC: if type_kind == rustpp.TYPE_KIND_SLICE:
return RustStdVecPrinter(val) return RustSlicePrinter(val)
if struct_kind == STRUCT_KIND_STD_STRING: if type_kind == rustpp.TYPE_KIND_STR_SLICE:
return RustStdStringPrinter(val) return RustStringSlicePrinter(val)
if struct_kind == STRUCT_KIND_TUPLE: if type_kind == rustpp.TYPE_KIND_STD_VEC:
return RustTuplePrinter(val) return RustStdVecPrinter(val)
if struct_kind == STRUCT_KIND_TUPLE_STRUCT: if type_kind == rustpp.TYPE_KIND_STD_STRING:
return RustTupleStructPrinter(val, False) return RustStdStringPrinter(val)
if struct_kind == STRUCT_KIND_CSTYLE_VARIANT: if type_kind == rustpp.TYPE_KIND_TUPLE:
return RustCStyleEnumPrinter(val[get_field_at_index(val, 0)]) return RustStructPrinter(val,
omit_first_field = False,
omit_type_name = True,
is_tuple_like = True)
if struct_kind == STRUCT_KIND_TUPLE_VARIANT: if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT:
return RustTupleStructPrinter(val, True) return RustStructPrinter(val,
omit_first_field = False,
omit_type_name = False,
is_tuple_like = True)
if struct_kind == STRUCT_KIND_STRUCT_VARIANT: if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT:
return RustStructPrinter(val, True) return RustCStyleVariantPrinter(val.get_child_at_index(0))
return RustStructPrinter(val, False) if type_kind == rustpp.TYPE_KIND_TUPLE_VARIANT:
return RustStructPrinter(val,
omit_first_field = True,
omit_type_name = False,
is_tuple_like = True)
# Enum handling if type_kind == rustpp.TYPE_KIND_SINGLETON_ENUM:
if type_code == gdb.TYPE_CODE_UNION: variant = get_field_at_index(gdb_val, 0)
enum_members = list(val.type.fields()) return rust_pretty_printer_lookup_function(gdb_val[variant])
enum_member_count = len(enum_members)
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)
if type_kind == rustpp.TYPE_KIND_REGULAR_ENUM:
# This is a regular enum, extract the discriminant # This is a regular enum, extract the discriminant
discriminant_name, discriminant_val = extract_discriminant_value(val) discriminant_val = rustpp.get_discriminant_value_as_integer(val)
return rust_pretty_printer_lookup_function(val[enum_members[discriminant_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())
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 # No pretty printer has been found
return None return None
#=------------------------------------------------------------------------------ #=------------------------------------------------------------------------------
# Pretty Printer Classes # Pretty Printer Classes
#=------------------------------------------------------------------------------ #=------------------------------------------------------------------------------
class RustStructPrinter: class RustStructPrinter:
def __init__(self, val, hide_first_field): def __init__(self, val, omit_first_field, omit_type_name, is_tuple_like):
self.val = val self.__val = val
self.hide_first_field = hide_first_field 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): 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): def children(self):
cs = [] cs = []
for field in self.val.type.fields(): wrapped_value = self.__val.get_wrapped_value()
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: for field in self.__val.type.get_fields():
cs = cs[1:] field_value = wrapped_value[field.name]
if self.__is_tuple_like:
cs.append(("", field_value))
else:
cs.append((field.name, field_value))
return cs if self.__omit_first_field:
class RustTuplePrinter:
def __init__(self, val):
self.val = val
def to_string(self):
return None
def children(self):
cs = []
for field in self.val.type.fields():
cs.append(("", self.val[field]))
return cs
def display_hint(self):
return "array"
class RustTupleStructPrinter:
def __init__(self, val, hide_first_field):
self.val = val
self.hide_first_field = hide_first_field
def to_string(self):
return self.val.type.tag
def children(self):
cs = []
for field in self.val.type.fields():
cs.append(("", self.val[field]))
if self.hide_first_field:
cs = cs[1:] cs = cs[1:]
return cs return cs
def display_hint(self): def display_hint(self):
return "array" if self.__is_tuple_like:
return "array"
else:
return ""
class RustSlicePrinter: class RustSlicePrinter:
def __init__(self, val): def __init__(self, val):
self.val = val self.__val = val
def display_hint(self): def display_hint(self):
return "array" return "array"
def to_string(self): def to_string(self):
length = int(self.val["length"]) (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
return self.val.type.tag + ("(len: %i)" % length) return (self.__val.type.get_unqualified_type_name() +
("(len: %i)" % length))
def children(self): def children(self):
cs = [] cs = []
length = int(self.val["length"]) (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
data_ptr = self.val["data_ptr"] assert data_ptr.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_PTR
assert data_ptr.type.code == gdb.TYPE_CODE_PTR raw_ptr = data_ptr.get_wrapped_value()
pointee_type = data_ptr.type.target()
for index in range(0, length): for index in range(0, length):
cs.append((str(index), (data_ptr + index).dereference())) cs.append((str(index), (raw_ptr + index).dereference()))
return cs return cs
class RustStringSlicePrinter: class RustStringSlicePrinter:
def __init__(self, val): def __init__(self, val):
self.val = val self.__val = val
def to_string(self): def to_string(self):
slice_byte_len = self.val["length"] (length, data_ptr) = rustpp.extract_length_and_ptr_from_slice(self.__val)
return '"%s"' % self.val["data_ptr"].string(encoding="utf-8", length=slice_byte_len) raw_ptr = data_ptr.get_wrapped_value()
return '"%s"' % raw_ptr.string(encoding="utf-8", length=length)
class RustStdVecPrinter: class RustStdVecPrinter:
def __init__(self, val): def __init__(self, val):
self.val = val self.__val = val
def display_hint(self): def display_hint(self):
return "array" return "array"
def to_string(self): def to_string(self):
length = int(self.val["len"]) (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
cap = int(self.val["cap"]) return (self.__val.type.get_unqualified_type_name() +
return self.val.type.tag + ("(len: %i, cap: %i)" % (length, cap)) ("(len: %i, cap: %i)" % (length, cap)))
def children(self): def children(self):
cs = [] cs = []
(length, data_ptr) = extract_length_and_data_ptr_from_std_vec(self.val) (length, data_ptr, cap) = rustpp.extract_length_ptr_and_cap_from_std_vec(self.__val)
pointee_type = data_ptr.type.target() gdb_ptr = data_ptr.get_wrapped_value()
for index in range(0, length): for index in range(0, length):
cs.append((str(index), (data_ptr + index).dereference())) cs.append((str(index), (gdb_ptr + index).dereference()))
return cs return cs
class RustStdStringPrinter: class RustStdStringPrinter:
def __init__(self, val): def __init__(self, val):
self.val = val self.__val = val
def to_string(self): def to_string(self):
(length, data_ptr) = extract_length_and_data_ptr_from_std_vec(self.val["vec"]) vec = self.__val.get_child_at_index(0)
return '"%s"' % data_ptr.string(encoding="utf-8", length=length) (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): def __init__(self, val):
assert val.type.code == gdb.TYPE_CODE_ENUM assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_ENUM
self.val = val self.__val = val
def to_string(self): def to_string(self):
return str(self.val) return str(self.__val.get_wrapped_value())
class IdentityPrinter: class IdentityPrinter:
@ -259,91 +278,11 @@ class IdentityPrinter:
def to_string(self): def to_string(self):
return self.string 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 get_field_at_index(gdb_val, index):
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):
i = 0 i = 0
for field in val.type.fields(): for field in gdb_val.type.fields():
if i == index: if i == index:
return field return field
i += 1 i += 1
return None 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)

View file

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

View file

@ -67,7 +67,7 @@
// lldb-check:[...]$5 = Void // lldb-check:[...]$5 = Void
// lldb-command:print some_str // 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-command:print none_str
// lldb-check:[...]$7 = None // lldb-check:[...]$7 = None

View file

@ -10,10 +10,12 @@
// ignore-windows failing on win32 bot // ignore-windows failing on win32 bot
// ignore-freebsd: gdb package too new // ignore-freebsd: gdb package too new
// ignore-lldb
// ignore-android: FIXME(#10381) // ignore-android: FIXME(#10381)
// compile-flags:-g // compile-flags:-g
// min-gdb-version 7.7 // min-gdb-version 7.7
// min-lldb-version: 310
// === GDB TESTS ===================================================================================
// gdb-command: run // gdb-command: run
@ -35,6 +37,30 @@
// gdb-command: print none // gdb-command: print none
// gdb-check:$6 = 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)] #![allow(unused_variables)]
fn main() { fn main() {