d136714e04
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.
329 lines
12 KiB
Python
329 lines
12 KiB
Python
# 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)
|