[torque][tools] Define layout of DescriptorArray for postmortem tools

This change defines a way that v8_debug_helper can describe object
fields which are packed structs, and uses it for the "descriptors" field
in DescriptorArray.

In more detail:
- debug-helper.h (the public interface for v8_debug_helper) adds a size
  and an optional list of struct properties to ObjectProperty.
- debug-helper-internal.h mirrors those changes to the internal class
  hierarchy which maintains proper unique_ptr ownership.
- In src/torque/class-debug-reader-generator.cc,
  - Some existing logic is moved into smaller functions.
  - New logic is added to generate the field list for structs. Example
    output is included in a comment above the function
    GenerateGetPropsChunkForField.

Bug: v8:9376
Change-Id: I531acac039ccb42050641448a4cbaec26186a7bc
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1894362
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65079}
This commit is contained in:
Seth Brenith 2019-11-18 13:48:44 -08:00 committed by Commit Bot
parent c968607e12
commit 6b11b700d7
4 changed files with 487 additions and 140 deletions

View File

@ -26,6 +26,330 @@ const d::ClassList kObjectClassList {
)";
namespace {
enum TypeStorage {
kAsStoredInHeap,
kUncompressed,
};
// 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) {}
bool IsTagged() const {
return field_.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType());
}
// Returns the type that should be used for this field's value within code
// that is compiled as part of the debug helper library. In particular, this
// simplifies any tagged type to a plain uintptr_t because the debug helper
// compiles without most of the V8 runtime code.
std::string GetValueType(TypeStorage storage) const {
if (IsTagged()) {
return storage == kAsStoredInHeap ? "i::Tagged_t" : "uintptr_t";
}
// Note that we need constexpr names to resolve correctly in the global
// namespace, because we're passing them as strings to a debugging
// extension. We can verify this during build of the debug helper, because
// we use this type for a local variable below, and generate this code in
// a disjoint namespace. However, we can't emit a useful error at this
// point. Instead we'll emit a comment that might be helpful.
return GetOriginalType(storage) +
" /*Failing? Ensure constexpr type name is fully qualified and "
"necessary #includes are in debug-helper-internal.h*/";
}
// Returns the type that should be used to represent a field's type to
// debugging tools that have full V8 symbols. The types returned from this
// 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()) {
// 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 "";
}
if (IsTagged()) {
if (storage == kAsStoredInHeap && COMPRESS_POINTERS_BOOL) {
return "v8::internal::TaggedValue";
}
base::Optional<const ClassType*> field_class_type =
field_.name_and_type.type->ClassSupertype();
return "v8::internal::" +
(field_class_type.has_value()
? (*field_class_type)->GetGeneratedTNodeTypeName()
: "Object");
}
const Type* constexpr_version =
field_.name_and_type.type->ConstexprVersion();
if (constexpr_version == nullptr) {
Error("Type '", field_.name_and_type.type->ToString(),
"' requires a constexpr representation")
.Position(field_.pos);
return "";
}
return constexpr_version->GetGeneratedTypeName();
}
// Returns the field's size in bytes.
size_t GetSize() const {
size_t size = 0;
std::tie(size, std::ignore) = field_.GetFieldSizeInformation();
return 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";
}
private:
const Field& field_;
};
// Emits a function to get the address of a field within a class, based on the
// member variable {address_}, which is a tagged pointer. Example
// implementation:
//
// uintptr_t TqFixedArray::GetObjectsAddress() const {
// return address_ - i::kHeapObjectTag + 16;
// }
void GenerateFieldAddressAccessor(const Field& field,
const std::string& class_name,
std::ostream& h_contents,
std::ostream& cc_contents) {
DebugFieldType debug_field_type(field);
const std::string address_getter = debug_field_type.GetAddressGetter();
h_contents << " uintptr_t " << address_getter << "() const;\n";
cc_contents << "\nuintptr_t Tq" << class_name << "::" << address_getter
<< "() const {\n";
cc_contents << " return address_ - i::kHeapObjectTag + " << field.offset
<< ";\n";
cc_contents << "}\n";
}
// Emits a function to get the value of a field, or the value from an indexed
// position within an array field, based on the member variable {address_},
// which is a tagged pointer, and the parameter {accessor}, a function pointer
// that allows for fetching memory from the debuggee. The returned result
// includes both a "validity", indicating whether the memory could be fetched,
// and the fetched value. If the field contains tagged data, then these
// functions call EnsureDecompressed to expand compressed data. Example:
//
// Value<uintptr_t> TqMap::GetPrototypeValue(d::MemoryAccessor accessor) const {
// i::Tagged_t value{};
// d::MemoryAccessResult validity = accessor(
// GetPrototypeAddress(),
// reinterpret_cast<uint8_t*>(&value),
// sizeof(value));
// return {validity, EnsureDecompressed(value, address_)};
// }
//
// For array fields, an offset parameter is included. Example:
//
// Value<uintptr_t> TqFixedArray::GetObjectsValue(d::MemoryAccessor accessor,
// size_t offset) const {
// i::Tagged_t value{};
// d::MemoryAccessResult validity = accessor(
// GetObjectsAddress() + offset * sizeof(value),
// reinterpret_cast<uint8_t*>(&value),
// sizeof(value));
// return {validity, EnsureDecompressed(value, address_)};
// }
void GenerateFieldValueAccessor(const Field& field,
const std::string& class_name,
std::ostream& h_contents,
std::ostream& cc_contents) {
// Currently not implemented for struct fields.
if (field.name_and_type.type->IsStructType()) return;
DebugFieldType debug_field_type(field);
const std::string address_getter = debug_field_type.GetAddressGetter();
const std::string field_getter =
"Get" + CamelifyString(field.name_and_type.name) + "Value";
std::string index_param;
std::string index_offset;
if (field.index) {
index_param = ", size_t offset";
index_offset = " + offset * sizeof(value)";
}
if (!field.name_and_type.type->IsStructType()) {
std::string field_value_type = debug_field_type.GetValueType(kUncompressed);
h_contents << " Value<" << field_value_type << "> " << field_getter
<< "(d::MemoryAccessor accessor " << index_param << ") const;\n";
cc_contents << "\nValue<" << field_value_type << "> Tq" << class_name
<< "::" << field_getter << "(d::MemoryAccessor accessor"
<< index_param << ") const {\n";
cc_contents << " " << debug_field_type.GetValueType(kAsStoredInHeap)
<< " value{};\n";
cc_contents << " d::MemoryAccessResult validity = accessor("
<< address_getter << "()" << index_offset
<< ", reinterpret_cast<uint8_t*>(&value), sizeof(value));\n";
cc_contents << " return {validity, "
<< (debug_field_type.IsTagged()
? "EnsureDecompressed(value, address_)"
: "value")
<< "};\n";
cc_contents << "}\n";
}
}
// Emits a portion of the member function GetProperties that is responsible for
// adding data about the current field to a result vector called "result".
// Example output:
//
// result.push_back(std::make_unique<ObjectProperty>(
// "prototype", // Field name
// "v8::internal::HeapObject", // Field type
// "v8::internal::HeapObject", // Decompressed type
// GetPrototypeAddress(), // Field address
// 1, // Number of values
// 8, // Size of value
// std::vector<std::unique_ptr<StructProperty>>(), // Struct fields
// d::PropertyKind::kSingle)); // Field kind
//
// In builds with pointer compression enabled, the field type for tagged values
// is "v8::internal::TaggedValue" (a four-byte class) and the decompressed type
// is a normal Object subclass that describes the expanded eight-byte type.
//
// If the field is an array, then its length is fetched from the debuggee. This
// could fail if the debuggee has incomplete memory, so the "validity" from that
// fetch is used to determine the result PropertyKind, which will say whether
// the array's length is known.
//
// If the field's type is a struct, then a local variable is created and filled
// with descriptions of each of the struct's fields. The type and decompressed
// type in the ObjectProperty are set to the empty string, to indicate to the
// caller that the struct fields vector should be used instead.
//
// The following example is an array of structs, so it uses both of the optional
// components described above:
//
// std::vector<std::unique_ptr<StructProperty>> descriptors_struct_field_list;
// descriptors_struct_field_list.push_back(std::make_unique<StructProperty>(
// "key", // Struct field name
// "v8::internal::PrimitiveHeapObject", // Struct field type
// "v8::internal::PrimitiveHeapObject", // Struct field decompressed type
// 0)); // Offset within struct data
// // The line above is repeated for other struct fields. Omitted here.
// Value<uint16_t> indexed_field_count =
// GetNumberOfAllDescriptorsValue(accessor); // Fetch the array length.
// result.push_back(std::make_unique<ObjectProperty>(
// "descriptors", // Field name
// "", // Field type
// "", // Decompressed type
// GetDescriptorsAddress(), // Field address
// indexed_field_count.value, // Number of values
// 24, // Size of value
// std::move(descriptors_struct_field_list), // Struct fields
// GetArrayKind(indexed_field_count.validity))); // Field kind
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.
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 + ")";
}
// The number of values and property kind for non-indexed properties:
std::string count_value = "1";
std::string property_kind = "d::PropertyKind::kSingle";
// If the field is indexed, emit a fetch of the array length, and change
// count_value and property_kind to be the correct values for an array.
if (field.index) {
const Type* index_type = field.index->type;
std::string index_type_name;
if (index_type == TypeOracle::GetSmiType()) {
index_type_name = "uintptr_t";
count_value =
"i::PlatformSmiTagging::SmiToInt(indexed_field_count.value)";
} else if (!index_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
const Type* constexpr_index = index_type->ConstexprVersion();
if (constexpr_index == nullptr) {
Error("Type '", index_type->ToString(),
"' requires a constexpr representation");
return;
}
index_type_name = constexpr_index->GetGeneratedTypeName();
count_value = "indexed_field_count.value";
} else {
Error("Unsupported index type: ", index_type);
return;
}
get_props_impl << " Value<" << index_type_name
<< "> indexed_field_count = Get"
<< CamelifyString(field.index->name) << "Value(accessor);\n";
property_kind = "GetArrayKind(indexed_field_count.validity)";
}
get_props_impl << " result.push_back(std::make_unique<ObjectProperty>(\""
<< field.name_and_type.name << "\", \""
<< debug_field_type.GetOriginalType(kAsStoredInHeap)
<< "\", \"" << debug_field_type.GetOriginalType(kUncompressed)
<< "\", " << debug_field_type.GetAddressGetter() << "(), "
<< count_value << ", " << debug_field_type.GetSize() << ", "
<< struct_field_list << ", " << property_kind << "));\n";
}
// For any Torque-defined class Foo, this function generates a class TqFoo which
// allows for convenient inspection of objects of type Foo in a crash dump or
// time travel session (where we can't just run the object printer). The
// generated class looks something like this:
//
// class TqFoo : public TqParentOfFoo {
// public:
// // {address} is an uncompressed tagged pointer.
// inline TqFoo(uintptr_t address) : TqParentOfFoo(address) {}
//
// // Creates and returns a list of this object's properties.
// std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
// d::MemoryAccessor accessor) const override;
//
// // Returns the name of this class, "v8::internal::Foo".
// const char* GetName() const override;
//
// // Visitor pattern; implementation just calls visitor->VisitFoo(this).
// void Visit(TqObjectVisitor* visitor) const override;
//
// // Returns whether Foo is a superclass of the other object's type.
// bool IsSuperclassOf(const TqObject* other) const override;
//
// // Field accessors omitted here (see other comments above).
// };
//
// Four output streams are written:
//
// h_contents: A header file which gets the class definition above.
// cc_contents: A cc file which gets implementations of that class's members.
// visitor: A stream that is accumulating the definition of the class
// TqObjectVisitor. Each class Foo gets its own virtual method
// VisitFoo in TqObjectVisitor.
// class_names: A stream that is accumulating a list of strings including fully-
// qualified names for every Torque-defined class type.
void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
std::ostream& cc_contents, std::ostream& visitor,
std::ostream& class_names,
@ -70,6 +394,9 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
<< name << "*>(other) != nullptr;\n";
cc_contents << "}\n";
// By default, the visitor method for this class just calls the visitor method
// for this class's parent. This allows custom visitors to only override a few
// classes they care about without needing to know about the entire hierarchy.
visitor << " virtual void Visit" << name << "(const Tq" << name
<< "* object) {\n";
visitor << " Visit" << super_name << "(object);\n";
@ -80,123 +407,20 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
std::stringstream get_props_impl;
for (const Field& field : type.fields()) {
const Type* field_type = field.name_and_type.type;
if (field_type == TypeOracle::GetVoidType()) continue;
if (field_type->IsStructType()) continue; // Not yet supported.
const std::string& field_name = field.name_and_type.name;
bool is_field_tagged = field_type->IsSubtypeOf(TypeOracle::GetTaggedType());
base::Optional<const ClassType*> field_class_type =
field_type->ClassSupertype();
size_t field_size = 0;
std::string field_size_string;
std::tie(field_size, field_size_string) = field.GetFieldSizeInformation();
std::string field_value_type;
std::string field_value_type_compressed;
std::string field_cc_type;
std::string field_cc_type_compressed;
if (is_field_tagged) {
field_value_type = "uintptr_t";
field_value_type_compressed = "i::Tagged_t";
field_cc_type = "v8::internal::" +
(field_class_type.has_value()
? (*field_class_type)->GetGeneratedTNodeTypeName()
: "Object");
field_cc_type_compressed =
COMPRESS_POINTERS_BOOL ? "v8::internal::TaggedValue" : field_cc_type;
} else {
const Type* constexpr_version = field_type->ConstexprVersion();
if (constexpr_version == nullptr) {
Error("Type '", field_type->ToString(),
"' requires a constexpr representation");
continue;
}
field_cc_type = constexpr_version->GetGeneratedTypeName();
field_cc_type_compressed = field_cc_type;
// Note that we need constexpr names to resolve correctly in the global
// namespace, because we're passing them as strings to a debugging
// extension. We can verify this during build of the debug helper, because
// we use this type for a local variable below, and generate this code in
// a disjoint namespace. However, we can't emit a useful error at this
// point. Instead we'll emit a comment that might be helpful.
field_value_type =
field_cc_type +
" /*Failing? Ensure constexpr type name is fully qualified and "
"necessary #includes are in debug-helper-internal.h*/";
field_value_type_compressed = field_value_type;
}
const std::string field_getter =
"Get" + CamelifyString(field_name) + "Value";
const std::string address_getter =
"Get" + CamelifyString(field_name) + "Address";
std::string indexed_field_info;
std::string index_param;
std::string index_offset;
if (field.index) {
const Type* index_type = field.index->type;
std::string index_type_name;
std::string index_value;
if (index_type == TypeOracle::GetSmiType()) {
index_type_name = "uintptr_t";
index_value =
"i::PlatformSmiTagging::SmiToInt(indexed_field_count.value)";
} else if (!index_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
const Type* constexpr_index = index_type->ConstexprVersion();
if (constexpr_index == nullptr) {
Error("Type '", index_type->ToString(),
"' requires a constexpr representation");
continue;
}
index_type_name = constexpr_index->GetGeneratedTypeName();
index_value = "indexed_field_count.value";
} else {
Error("Unsupported index type: ", index_type);
continue;
}
get_props_impl << " Value<" << index_type_name
<< "> indexed_field_count = Get"
<< CamelifyString(field.index->name)
<< "Value(accessor);\n";
indexed_field_info =
", " + index_value + ", GetArrayKind(indexed_field_count.validity)";
index_param = ", size_t offset";
index_offset = " + offset * sizeof(value)";
}
get_props_impl << " result.push_back(std::make_unique<ObjectProperty>(\""
<< field_name << "\", \"" << field_cc_type_compressed
<< "\", \"" << field_cc_type << "\", " << address_getter
<< "()" << indexed_field_info << "));\n";
h_contents << " uintptr_t " << address_getter << "() const;\n";
h_contents << " Value<" << field_value_type << "> " << field_getter
<< "(d::MemoryAccessor accessor " << index_param << ") const;\n";
cc_contents << "\nuintptr_t Tq" << name << "::" << address_getter
<< "() const {\n";
cc_contents << " return address_ - i::kHeapObjectTag + " << field.offset
<< ";\n";
cc_contents << "}\n";
cc_contents << "\nValue<" << field_value_type << "> Tq" << name
<< "::" << field_getter << "(d::MemoryAccessor accessor"
<< index_param << ") const {\n";
cc_contents << " " << field_value_type_compressed << " value{};\n";
cc_contents << " d::MemoryAccessResult validity = accessor("
<< address_getter << "()" << index_offset
<< ", reinterpret_cast<uint8_t*>(&value), sizeof(value));\n";
cc_contents << " return {validity, "
<< (is_field_tagged ? "EnsureDecompressed(value, address_)"
: "value")
<< "};\n";
cc_contents << "}\n";
if (field.name_and_type.type == TypeOracle::GetVoidType()) continue;
GenerateFieldAddressAccessor(field, name, h_contents, cc_contents);
GenerateFieldValueAccessor(field, name, h_contents, cc_contents);
GenerateGetPropsChunkForField(field, get_props_impl);
}
h_contents << "};\n";
cc_contents << "\nstd::vector<std::unique_ptr<ObjectProperty>> Tq" << name
<< "::GetProperties(d::MemoryAccessor accessor) const {\n";
// Start by getting the fields from the parent class.
cc_contents << " std::vector<std::unique_ptr<ObjectProperty>> result = Tq"
<< super_name << "::GetProperties(accessor);\n";
// Then add the fields from this class.
cc_contents << get_props_impl.str();
cc_contents << " return result;\n";
cc_contents << "}\n";

View File

@ -43,16 +43,21 @@ d::MemoryAccessResult ReadMemory(uintptr_t address, uint8_t* destination,
return d::MemoryAccessResult::kOk;
}
void CheckPropBase(const d::PropertyBase& property, const char* expected_type,
const char* expected_name) {
CHECK(property.type == std::string("v8::internal::TaggedValue") ||
property.type == std::string(expected_type));
CHECK(property.decompressed_type == std::string(expected_type));
CHECK(property.name == std::string(expected_name));
}
void CheckProp(const d::ObjectProperty& property, const char* expected_type,
const char* expected_name,
d::PropertyKind expected_kind = d::PropertyKind::kSingle,
size_t expected_num_values = 1) {
CheckPropBase(property, expected_type, expected_name);
CHECK_EQ(property.num_values, expected_num_values);
CHECK(property.type == std::string("v8::internal::TaggedValue") ||
property.type == std::string(expected_type));
CHECK(property.decompressed_type == std::string(expected_type));
CHECK(property.kind == expected_kind);
CHECK(property.name == std::string(expected_name));
}
template <typename TValue>
@ -66,6 +71,24 @@ bool StartsWith(std::string full_string, std::string prefix) {
return full_string.substr(0, prefix.size()) == prefix;
}
void CheckStructProp(const d::StructProperty& property,
const char* expected_type, const char* expected_name,
size_t expected_offset) {
CheckPropBase(property, expected_type, expected_name);
CHECK_EQ(property.offset, expected_offset);
}
template <typename TValue>
TValue ReadProp(const d::ObjectPropertiesResult& props, std::string name) {
for (size_t i = 0; i < props.num_properties; ++i) {
if (name == props.properties[i]->name) {
return *reinterpret_cast<TValue*>(props.properties[i]->address);
}
}
CHECK_WITH_MSG(false, ("property '" + name + "' not found").c_str());
return {};
}
} // namespace
TEST(GetObjectProperties) {
@ -254,6 +277,39 @@ TEST(GetObjectProperties) {
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
CHECK(std::string(props->brief).substr(79, 7) == std::string("aa...\" "));
// Verify the result for a heap object field which is itself a struct: the
// "descriptors" field on a DescriptorArray.
// First we need to construct an object and get its map's descriptor array.
v = CompileRun("({a: 1, b: 2})");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, heap_addresses);
props = d::GetObjectProperties(ReadProp<i::Tagged_t>(*props, "map"),
&ReadMemory, heap_addresses);
props = d::GetObjectProperties(
ReadProp<i::Tagged_t>(*props, "instance_descriptors"), &ReadMemory,
heap_addresses);
// It should have at least two descriptors (possibly plus slack).
CheckProp(*props->properties[1], "uint16_t", "number_of_all_descriptors");
uint16_t number_of_all_descriptors =
*reinterpret_cast<uint16_t*>(props->properties[1]->address);
CHECK_GE(number_of_all_descriptors, 2);
// The "descriptors" property should describe the struct layout for each
// element in the array.
const d::ObjectProperty& descriptors = *props->properties[6];
// No C++ type is reported directly because there may not be an actual C++
// struct with this layout, hence the empty string in this check.
CheckProp(descriptors, /*type=*/"", "descriptors",
d::PropertyKind::kArrayOfKnownSize, number_of_all_descriptors);
CHECK_EQ(descriptors.size, 3 * i::kTaggedSize);
CHECK_EQ(descriptors.num_struct_fields, 3);
CheckStructProp(*descriptors.struct_fields[0],
"v8::internal::PrimitiveHeapObject", "key",
0 * i::kTaggedSize);
CheckStructProp(*descriptors.struct_fields[1], "v8::internal::Object",
"details", 1 * i::kTaggedSize);
CheckStructProp(*descriptors.struct_fields[2], "v8::internal::Object",
"value", 2 * i::kTaggedSize);
}
TEST(ListObjectClasses) {

View File

@ -28,39 +28,85 @@ struct Value {
TValue value;
};
// Internal version of API class v8::debug_helper::ObjectProperty.
class ObjectProperty {
// Internal version of API class v8::debug_helper::PropertyBase.
class PropertyBase {
public:
inline ObjectProperty(std::string name, std::string type,
std::string decompressed_type, uintptr_t address,
size_t num_values = 1,
d::PropertyKind kind = d::PropertyKind::kSingle)
: name_(name),
type_(type),
decompressed_type_(decompressed_type),
address_(address),
num_values_(num_values),
kind_(kind) {}
inline d::ObjectProperty* GetPublicView() {
public_view_.name = name_.c_str();
public_view_.type = type_.c_str();
public_view_.decompressed_type = decompressed_type_.c_str();
public_view_.address = address_;
public_view_.num_values = num_values_;
public_view_.kind = kind_;
return &public_view_;
PropertyBase(std::string name, std::string type,
std::string decompressed_type)
: name_(name), type_(type), decompressed_type_(decompressed_type) {}
void SetFieldsOnPublicView(d::PropertyBase* public_view) {
public_view->name = name_.c_str();
public_view->type = type_.c_str();
public_view->decompressed_type = decompressed_type_.c_str();
}
private:
std::string name_;
std::string type_;
std::string decompressed_type_;
};
// Internal version of API class v8::debug_helper::StructProperty.
class StructProperty : public PropertyBase {
public:
StructProperty(std::string name, std::string type,
std::string decompressed_type, size_t offset)
: PropertyBase(std::move(name), std::move(type),
std::move(decompressed_type)),
offset_(offset) {}
d::StructProperty* GetPublicView() {
PropertyBase::SetFieldsOnPublicView(&public_view_);
public_view_.offset = offset_;
return &public_view_;
}
private:
size_t offset_;
d::StructProperty public_view_;
};
// Internal version of API class v8::debug_helper::ObjectProperty.
class ObjectProperty : public PropertyBase {
public:
ObjectProperty(std::string name, std::string type,
std::string decompressed_type, uintptr_t address,
size_t num_values, size_t size,
std::vector<std::unique_ptr<StructProperty>> struct_fields,
d::PropertyKind kind)
: PropertyBase(std::move(name), std::move(type),
std::move(decompressed_type)),
address_(address),
num_values_(num_values),
size_(size),
struct_fields_(std::move(struct_fields)),
kind_(kind) {}
d::ObjectProperty* GetPublicView() {
PropertyBase::SetFieldsOnPublicView(&public_view_);
public_view_.address = address_;
public_view_.num_values = num_values_;
public_view_.size = size_;
public_view_.num_struct_fields = struct_fields_.size();
struct_fields_raw_.clear();
for (const auto& property : struct_fields_) {
struct_fields_raw_.push_back(property->GetPublicView());
}
public_view_.struct_fields = struct_fields_raw_.data();
public_view_.kind = kind_;
return &public_view_;
}
private:
uintptr_t address_;
size_t num_values_;
size_t size_;
std::vector<std::unique_ptr<StructProperty>> struct_fields_;
d::PropertyKind kind_;
d::ObjectProperty public_view_;
std::vector<d::StructProperty*> struct_fields_raw_;
};
class ObjectPropertiesResult;
@ -84,9 +130,9 @@ class ObjectPropertiesResult {
guessed_types_ = std::move(guessed_types);
}
inline void Prepend(const char* prefix) { brief_ = prefix + brief_; }
void Prepend(const char* prefix) { brief_ = prefix + brief_; }
inline d::ObjectPropertiesResult* GetPublicView() {
d::ObjectPropertiesResult* GetPublicView() {
public_view_.type_check_result = type_check_result_;
public_view_.brief = brief_.c_str();
public_view_.type = type_.c_str();
@ -124,7 +170,7 @@ class TqObjectVisitor;
// Subclasses for specific object types are generated by the Torque compiler.
class TqObject {
public:
inline TqObject(uintptr_t address) : address_(address) {}
TqObject(uintptr_t address) : address_(address) {}
virtual ~TqObject() = default;
virtual std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
d::MemoryAccessor accessor) const;
@ -137,7 +183,7 @@ class TqObject {
};
// In ptr-compr builds, returns whether the address looks like a compressed
// pointer (sign-extended from 32 bits). Otherwise returns false because no
// pointer (zero-extended from 32 bits). Otherwise returns false because no
// pointers can be compressed.
bool IsPointerCompressed(uintptr_t address);

View File

@ -66,10 +66,12 @@ enum class PropertyKind {
kArrayOfUnknownSizeDueToValidButInaccessibleMemory,
};
struct ObjectProperty {
struct PropertyBase {
const char* name;
// Statically-determined type, such as from .tq definition.
// Statically-determined type, such as from .tq definition. Can be an empty
// string if this property is itself a Torque-defined struct; in that case use
// |struct_fields| instead.
const char* type;
// In some cases, |type| may be a simple type representing a compressed
@ -79,7 +81,14 @@ struct ObjectProperty {
// to pass the |decompressed_type| value as the type_hint on a subsequent call
// to GetObjectProperties.
const char* decompressed_type;
};
struct StructProperty : public PropertyBase {
// The offset from the beginning of the struct to this field.
size_t offset;
};
struct ObjectProperty : public PropertyBase {
// The address where the property value can be found in the debuggee's address
// space, or the address of the first value for an array.
uintptr_t address;
@ -90,6 +99,18 @@ struct ObjectProperty {
// versus num_values=1 and kind=kArrayOfKnownSize (one-element array).
size_t num_values;
// The number of bytes occupied by a single instance of the value type for
// this property. This can also be used as the array stride because arrays are
// 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.
size_t num_struct_fields;
StructProperty** struct_fields;
PropertyKind kind;
};