mirror of
https://github.com/rust-lang/rust
synced 2024-11-02 11:53:40 +00:00
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.
This commit is contained in:
parent
474c6e0ae4
commit
d136714e04
6 changed files with 742 additions and 426 deletions
|
@ -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))
|
||||
|
|
328
src/etc/debugger_pretty_printers_common.py
Normal file
328
src/etc/debugger_pretty_printers_common.py
Normal 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)
|
|
@ -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 __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):
|
||||
"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)
|
||||
|
||||
|
||||
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
|
||||
def rust_pretty_printer_lookup_function(gdb_val):
|
||||
"""
|
||||
Returns the correct Rust pretty printer for the given value
|
||||
if there is one
|
||||
"""
|
||||
|
||||
if type_code == gdb.TYPE_CODE_STRUCT:
|
||||
struct_kind = classify_struct(val.type)
|
||||
val = GdbValue(gdb_val)
|
||||
type_kind = val.type.get_type_kind()
|
||||
|
||||
if struct_kind == STRUCT_KIND_SLICE:
|
||||
return RustSlicePrinter(val)
|
||||
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)
|
||||
|
||||
if struct_kind == STRUCT_KIND_STR_SLICE:
|
||||
return RustStringSlicePrinter(val)
|
||||
if type_kind == rustpp.TYPE_KIND_STRUCT_VARIANT:
|
||||
return RustStructPrinter(val,
|
||||
omit_first_field = True,
|
||||
omit_type_name = False,
|
||||
is_tuple_like = False)
|
||||
|
||||
if struct_kind == STRUCT_KIND_STD_VEC:
|
||||
return RustStdVecPrinter(val)
|
||||
if type_kind == rustpp.TYPE_KIND_SLICE:
|
||||
return RustSlicePrinter(val)
|
||||
|
||||
if struct_kind == STRUCT_KIND_STD_STRING:
|
||||
return RustStdStringPrinter(val)
|
||||
if type_kind == rustpp.TYPE_KIND_STR_SLICE:
|
||||
return RustStringSlicePrinter(val)
|
||||
|
||||
if struct_kind == STRUCT_KIND_TUPLE:
|
||||
return RustTuplePrinter(val)
|
||||
if type_kind == rustpp.TYPE_KIND_STD_VEC:
|
||||
return RustStdVecPrinter(val)
|
||||
|
||||
if struct_kind == STRUCT_KIND_TUPLE_STRUCT:
|
||||
return RustTupleStructPrinter(val, False)
|
||||
if type_kind == rustpp.TYPE_KIND_STD_STRING:
|
||||
return RustStdStringPrinter(val)
|
||||
|
||||
if struct_kind == STRUCT_KIND_CSTYLE_VARIANT:
|
||||
return RustCStyleEnumPrinter(val[get_field_at_index(val, 0)])
|
||||
if type_kind == rustpp.TYPE_KIND_TUPLE:
|
||||
return RustStructPrinter(val,
|
||||
omit_first_field = False,
|
||||
omit_type_name = True,
|
||||
is_tuple_like = True)
|
||||
|
||||
if struct_kind == STRUCT_KIND_TUPLE_VARIANT:
|
||||
return RustTupleStructPrinter(val, True)
|
||||
if type_kind == rustpp.TYPE_KIND_TUPLE_STRUCT:
|
||||
return RustStructPrinter(val,
|
||||
omit_first_field = False,
|
||||
omit_type_name = False,
|
||||
is_tuple_like = True)
|
||||
|
||||
if struct_kind == STRUCT_KIND_STRUCT_VARIANT:
|
||||
return RustStructPrinter(val, True)
|
||||
if type_kind == rustpp.TYPE_KIND_CSTYLE_VARIANT:
|
||||
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_code == gdb.TYPE_CODE_UNION:
|
||||
enum_members = list(val.type.fields())
|
||||
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_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_name, discriminant_val = extract_discriminant_value(val)
|
||||
return rust_pretty_printer_lookup_function(val[enum_members[discriminant_val]])
|
||||
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())
|
||||
|
||||
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, hide_first_field):
|
||||
self.val = val
|
||||
self.hide_first_field = hide_first_field
|
||||
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():
|
||||
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)
|
||||
wrapped_value = self.__val.get_wrapped_value()
|
||||
|
||||
if self.hide_first_field:
|
||||
cs = cs[1:]
|
||||
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))
|
||||
|
||||
return cs
|
||||
|
||||
|
||||
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:
|
||||
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 @@ class IdentityPrinter:
|
|||
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)
|
||||
|
|
|
@ -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:
|
||||
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)
|
||||
|
||||
if type_class == lldb.eTypeClassArray:
|
||||
if type_kind == rustpp.TYPE_KIND_FIXED_SIZE_VEC:
|
||||
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
|
||||
#=--------------------------------------------------------------------------------------------------
|
||||
|
||||
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 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):
|
||||
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
|
||||
assert val.type.get_dwarf_type_kind() == rustpp.DWARF_TYPE_CODE_STRUCT
|
||||
|
||||
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
|
||||
|
||||
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 print_struct_val_starting_from(field_start_index, val, internal_dict):
|
|||
else:
|
||||
return this + "<invalid value>"
|
||||
|
||||
return this + print_val(field_val, internal_dict)
|
||||
return this + print_val(field_val.get_wrapped_value(), internal_dict)
|
||||
|
||||
body = separator.join([render_child(idx) for idx in range(field_start_index, num_children)])
|
||||
if omit_first_field:
|
||||
field_start_index = 1
|
||||
else:
|
||||
field_start_index = 0
|
||||
|
||||
body = separator.join([render_child(idx) for idx in range(field_start_index, len(fields))])
|
||||
|
||||
return template % {"type_name": type_name,
|
||||
"body": body}
|
||||
|
||||
|
||||
def print_enum_val(val, internal_dict):
|
||||
'''Prints an enum value with Rust syntax'''
|
||||
|
||||
assert val.GetType().GetTypeClass() == lldb.eTypeClassUnion
|
||||
|
||||
if val.num_children == 1:
|
||||
# This is either an enum with just one variant, or it is an Option-like
|
||||
# enum where the discriminant is encoded in a non-nullable pointer
|
||||
# field. We find out which one it is by looking at the member name of
|
||||
# the sole union variant. If it starts with "RUST$ENCODED$ENUM$" then
|
||||
# we have an Option-like enum.
|
||||
first_variant_name = val.GetChildAtIndex(0).GetName()
|
||||
if first_variant_name and first_variant_name.startswith("RUST$ENCODED$ENUM$"):
|
||||
|
||||
# This is an Option-like enum. The position of the discriminator field is
|
||||
# encoded in the name which has the format:
|
||||
# RUST$ENCODED$ENUM$<index of discriminator field>$<name of null variant>
|
||||
last_separator_index = first_variant_name.rfind("$")
|
||||
if last_separator_index == -1:
|
||||
return "<invalid enum encoding: %s>" % first_variant_name
|
||||
|
||||
start_index = len("RUST$ENCODED$ENUM$")
|
||||
|
||||
# Extract indices of the discriminator field
|
||||
try:
|
||||
disr_field_indices = first_variant_name[start_index:last_separator_index].split("$")
|
||||
disr_field_indices = [int(index) for index in disr_field_indices]
|
||||
except:
|
||||
return "<invalid enum encoding: %s>" % first_variant_name
|
||||
|
||||
# Read the discriminant
|
||||
disr_val = val.GetChildAtIndex(0)
|
||||
for index in disr_field_indices:
|
||||
disr_val = disr_val.GetChildAtIndex(index)
|
||||
|
||||
# If the discriminant field is a fat pointer we have to consider the
|
||||
# first word as the true discriminant
|
||||
if disr_val.GetType().GetTypeClass() == lldb.eTypeClassStruct:
|
||||
disr_val = disr_val.GetChildAtIndex(0)
|
||||
|
||||
if disr_val.GetValueAsUnsigned() == 0:
|
||||
# Null case: Print the name of the null-variant
|
||||
null_variant_name = first_variant_name[last_separator_index + 1:]
|
||||
return null_variant_name
|
||||
else:
|
||||
# Non-null case: Interpret the data as a value of the non-null variant type
|
||||
return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
|
||||
else:
|
||||
# This is just a regular uni-variant enum without discriminator field
|
||||
return print_struct_val_starting_from(0, val.GetChildAtIndex(0), internal_dict)
|
||||
|
||||
# If we are here, this is a regular enum with more than one variant
|
||||
disr_val = val.GetChildAtIndex(0).GetChildMemberWithName("RUST$ENUM$DISR")
|
||||
disr_type = disr_val.GetType()
|
||||
|
||||
if disr_type.GetTypeClass() != lldb.eTypeClassEnumeration:
|
||||
return "<Invalid enum value encountered: Discriminator is not an enum>"
|
||||
|
||||
variant_index = disr_val.GetValueAsUnsigned()
|
||||
return print_struct_val_starting_from(1, val.GetChildAtIndex(variant_index), internal_dict)
|
||||
|
||||
|
||||
def print_pointer_val(val, internal_dict):
|
||||
'''Prints a pointer value with Rust syntax'''
|
||||
assert val.GetType().IsPointerType()
|
||||
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: %s>' % error.GetCString()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
Loading…
Reference in a new issue