[torque] Generate postmortem data about bitfields

This change updates GetObjectProperties to list all of the bitfields
within a class field, if that class field's type is a bitfield struct.
The representation of bitfields in the GetObjectProperties response is
very similar to the representation of struct fields, but with two extra
bytes of data specifying the shift and size of the bitfield.

Bug: v8:9376
Change-Id: I40a22169f3d01652a7f2db8cface43c2a1e30cfe
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1960835
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#65610}
This commit is contained in:
Seth Brenith 2019-12-26 12:48:13 -08:00 committed by Commit Bot
parent ab723525b2
commit ae8eb6c290
4 changed files with 141 additions and 37 deletions

View File

@ -31,14 +31,79 @@ enum TypeStorage {
kUncompressed,
};
// An iterator for use in ValueTypeFieldsRange.
class ValueTypeFieldIterator {
public:
ValueTypeFieldIterator(const Type* type, size_t index)
: type_(type), index_(index) {}
struct Result {
NameAndType name_and_type;
SourcePosition pos;
size_t offset_bytes;
int num_bits;
int shift_bits;
};
const Result operator*() const {
if (const StructType* struct_type = StructType::DynamicCast(type_)) {
const auto& field = struct_type->fields()[index_];
return {field.name_and_type, field.pos, *field.offset, 0, 0};
}
if (const BitFieldStructType* bit_field_struct_type =
BitFieldStructType::DynamicCast(type_)) {
const auto& field = bit_field_struct_type->fields()[index_];
return {field.name_and_type, field.pos, 0, field.num_bits, field.offset};
}
UNREACHABLE();
}
ValueTypeFieldIterator& operator++() {
++index_;
return *this;
}
bool operator==(const ValueTypeFieldIterator& other) const {
return type_ == other.type_ && index_ == other.index_;
}
bool operator!=(const ValueTypeFieldIterator& other) const {
return !(*this == other);
}
private:
const Type* type_;
size_t index_;
};
// A way to iterate over the fields of structs or bitfield structs. For other
// types, the iterators returned from begin() and end() are immediately equal.
class ValueTypeFieldsRange {
public:
explicit ValueTypeFieldsRange(const Type* type) : type_(type) {}
ValueTypeFieldIterator begin() { return {type_, 0}; }
ValueTypeFieldIterator end() {
size_t index = 0;
if (const StructType* struct_type = StructType::DynamicCast(type_)) {
index = struct_type->fields().size();
}
if (const BitFieldStructType* bit_field_struct_type =
BitFieldStructType::DynamicCast(type_)) {
index = bit_field_struct_type->fields().size();
}
return {type_, index};
}
private:
const Type* type_;
};
// A convenient way to keep track of several different ways that we might need
// to represent a field's type in the generated C++.
class DebugFieldType {
public:
explicit DebugFieldType(const Field& field) : field_(field) {}
explicit DebugFieldType(const Field& field)
: name_and_type_(field.name_and_type), pos_(field.pos) {}
DebugFieldType(const NameAndType& name_and_type, const SourcePosition& pos)
: name_and_type_(name_and_type), pos_(pos) {}
bool IsTagged() const {
return field_.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType());
return name_and_type_.type->IsSubtypeOf(TypeOracle::GetTaggedType());
}
// Returns the type that should be used for this field's value within code
@ -65,7 +130,7 @@ class DebugFieldType {
// method are fully qualified and may refer to object types that are not
// included in the compilation of the debug helper library.
std::string GetOriginalType(TypeStorage storage) const {
if (field_.name_and_type.type->IsStructType()) {
if (name_and_type_.type->IsStructType()) {
// There's no meaningful type we could use here, because the V8 symbols
// don't have any definition of a C++ struct matching this struct type.
return "";
@ -75,29 +140,34 @@ class DebugFieldType {
return "v8::internal::TaggedValue";
}
base::Optional<const ClassType*> field_class_type =
field_.name_and_type.type->ClassSupertype();
name_and_type_.type->ClassSupertype();
return "v8::internal::" +
(field_class_type.has_value()
? (*field_class_type)->GetGeneratedTNodeTypeName()
: "Object");
}
return field_.name_and_type.type->GetConstexprGeneratedTypeName();
return name_and_type_.type->GetConstexprGeneratedTypeName();
}
// Returns the field's size in bytes.
size_t GetSize() const {
size_t size = 0;
std::tie(size, std::ignore) = field_.GetFieldSizeInformation();
return size;
auto opt_size = SizeOf(name_and_type_.type);
if (!opt_size.has_value()) {
Error("Size required for type ", name_and_type_.type->ToString())
.Position(pos_);
return 0;
}
return std::get<0>(*opt_size);
}
// Returns the name of the function for getting this field's address.
std::string GetAddressGetter() {
return "Get" + CamelifyString(field_.name_and_type.name) + "Address";
return "Get" + CamelifyString(name_and_type_.name) + "Address";
}
private:
const Field& field_;
NameAndType name_and_type_;
SourcePosition pos_;
};
// Emits a function to get the address of a field within a class, based on the
@ -196,6 +266,7 @@ void GenerateFieldValueAccessor(const Field& field,
// adding data about the current field to a result vector called "result".
// Example output:
//
// std::vector<std::unique_ptr<StructProperty>> prototype_struct_field_list;
// result.push_back(std::make_unique<ObjectProperty>(
// "prototype", // Field name
// "v8::internal::HeapObject", // Field type
@ -203,7 +274,7 @@ void GenerateFieldValueAccessor(const Field& field,
// GetPrototypeAddress(), // Field address
// 1, // Number of values
// 8, // Size of value
// std::vector<std::unique_ptr<StructProperty>>(), // Struct fields
// std::move(prototype_struct_field_list), // Struct fields
// d::PropertyKind::kSingle)); // Field kind
//
// In builds with pointer compression enabled, the field type for tagged values
@ -228,7 +299,9 @@ void GenerateFieldValueAccessor(const Field& field,
// "key", // Struct field name
// "v8::internal::PrimitiveHeapObject", // Struct field type
// "v8::internal::PrimitiveHeapObject", // Struct field decompressed type
// 0)); // Offset within struct data
// 0, // Byte offset within struct data
// 0, // Bitfield size (0=not a bitfield)
// 0)); // Bitfield shift
// // The line above is repeated for other struct fields. Omitted here.
// Value<uint16_t> indexed_field_count =
// GetNumberOfAllDescriptorsValue(accessor); // Fetch the array length.
@ -246,26 +319,26 @@ void GenerateGetPropsChunkForField(const Field& field,
std::ostream& get_props_impl) {
DebugFieldType debug_field_type(field);
// If the current field is a struct, create a vector describing its fields.
// If the current field is a struct or bitfield struct, create a vector
// describing its fields. Otherwise this vector will be empty.
std::string struct_field_list =
"std::vector<std::unique_ptr<StructProperty>>()";
if (const StructType* field_struct_type =
StructType::DynamicCast(field.name_and_type.type)) {
struct_field_list = field.name_and_type.name + "_struct_field_list";
get_props_impl << " std::vector<std::unique_ptr<StructProperty>> "
<< struct_field_list << ";\n";
for (const Field& struct_field : field_struct_type->fields()) {
DebugFieldType struct_field_type(struct_field);
get_props_impl << " " << struct_field_list
<< ".push_back(std::make_unique<StructProperty>(\""
<< struct_field.name_and_type.name << "\", \""
<< struct_field_type.GetOriginalType(kAsStoredInHeap)
<< "\", \""
<< struct_field_type.GetOriginalType(kUncompressed)
<< "\", " << *struct_field.offset << "));\n";
}
struct_field_list = "std::move(" + struct_field_list + ")";
field.name_and_type.name + "_struct_field_list";
get_props_impl << " std::vector<std::unique_ptr<StructProperty>> "
<< struct_field_list << ";\n";
for (const auto& struct_field :
ValueTypeFieldsRange(field.name_and_type.type)) {
DebugFieldType struct_field_type(struct_field.name_and_type,
struct_field.pos);
get_props_impl << " " << struct_field_list
<< ".push_back(std::make_unique<StructProperty>(\""
<< struct_field.name_and_type.name << "\", \""
<< struct_field_type.GetOriginalType(kAsStoredInHeap)
<< "\", \""
<< struct_field_type.GetOriginalType(kUncompressed) << "\", "
<< struct_field.offset_bytes << ", " << struct_field.num_bits
<< ", " << struct_field.shift_bits << "));\n";
}
struct_field_list = "std::move(" + struct_field_list + ")";
// The number of values and property kind for non-indexed properties:
std::string count_value = "1";

View File

@ -77,9 +77,12 @@ bool Contains(const std::string& full_string, const std::string& substr) {
void CheckStructProp(const d::StructProperty& property,
const char* expected_type, const char* expected_name,
size_t expected_offset) {
size_t expected_offset, uint8_t expected_num_bits = 0,
uint8_t expected_shift_bits = 0) {
CheckPropBase(property, expected_type, expected_name);
CHECK_EQ(property.offset, expected_offset);
CHECK_EQ(property.num_bits, expected_num_bits);
CHECK_EQ(property.shift_bits, expected_shift_bits);
}
const d::ObjectProperty& FindProp(const d::ObjectPropertiesResult& props,
@ -332,6 +335,20 @@ TEST(GetObjectProperties) {
"details", 1 * i::kTaggedSize);
CheckStructProp(*descriptors.struct_fields[2], "v8::internal::Object",
"value", 2 * i::kTaggedSize);
// Build a basic JS function and get its properties. This will allow us to
// exercise bitfield functionality.
v = CompileRun("(function () {})");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
props = d::GetObjectProperties(
ReadProp<i::Tagged_t>(*props, "shared_function_info"), &ReadMemory,
heap_addresses);
const d::ObjectProperty& flags = FindProp(*props, "flags");
CheckStructProp(*flags.struct_fields[0], "v8::internal::FunctionKind",
"function_kind", 0, 5, 0);
CheckStructProp(*flags.struct_fields[1], "bool", "is_native", 0, 1, 5);
CheckStructProp(*flags.struct_fields[2], "bool", "is_strict", 0, 1, 6);
}
TEST(ListObjectClasses) {

View File

@ -50,19 +50,26 @@ class PropertyBase {
class StructProperty : public PropertyBase {
public:
StructProperty(std::string name, std::string type,
std::string decompressed_type, size_t offset)
std::string decompressed_type, size_t offset, uint8_t num_bits,
uint8_t shift_bits)
: PropertyBase(std::move(name), std::move(type),
std::move(decompressed_type)),
offset_(offset) {}
offset_(offset),
num_bits_(num_bits),
shift_bits_(shift_bits) {}
d::StructProperty* GetPublicView() {
PropertyBase::SetFieldsOnPublicView(&public_view_);
public_view_.offset = offset_;
public_view_.num_bits = num_bits_;
public_view_.shift_bits = shift_bits_;
return &public_view_;
}
private:
size_t offset_;
uint8_t num_bits_;
uint8_t shift_bits_;
d::StructProperty public_view_;
};

View File

@ -86,6 +86,14 @@ struct PropertyBase {
struct StructProperty : public PropertyBase {
// The offset from the beginning of the struct to this field.
size_t offset;
// The number of bits that are present, if this value is a bitfield. Zero
// indicates that this value is not a bitfield (the full value is stored).
uint8_t num_bits;
// The number of bits by which this value has been left-shifted for storage as
// a bitfield.
uint8_t shift_bits;
};
struct ObjectProperty : public PropertyBase {
@ -104,10 +112,9 @@ struct ObjectProperty : public PropertyBase {
// tightly packed like in C.
size_t size;
// If |type| is nullptr, then this property does not correspond directly to
// any C++ type. Instead, the property is a struct made up of several pieces
// of data packed together. In that case, the |struct_fields| array contains
// the struct fields.
// If the property is a struct made up of several pieces of data packed
// together, then the |struct_fields| array contains descriptions of those
// fields.
size_t num_struct_fields;
StructProperty** struct_fields;