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)
|