Commit 6b11b700 authored by Seth Brenith's avatar Seth Brenith Committed by Commit Bot

[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: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65079}
parent c968607e
This diff is collapsed.
......@@ -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) {
......
......@@ -28,39 +28,85 @@ struct Value {
TValue value;
};
// Internal version of API class v8::debug_helper::PropertyBase.
class PropertyBase {
public:
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 {
class ObjectProperty : public 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),
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) {}
inline d::ObjectProperty* GetPublicView() {
public_view_.name = name_.c_str();
public_view_.type = type_.c_str();
public_view_.decompressed_type = decompressed_type_.c_str();
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:
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_;
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);
......
......@@ -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;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment