Commit 2ccca6c5 authored by Seth Brenith's avatar Seth Brenith Committed by Commit Bot

[tools][torque] Include string values in GetObjectProperties responses

This change provides a quick way to see string contents in postmortem
debugging sessions, without digging through a (possibly very large, in
the case of ConsString) tree of properties. As well as being convenient
for inspecting String objects, this functionality will also be necessary
for displaying property names on JSReceiver objects. In order to support
custom behaviors for specific classes, this change extends the existing
generated debug reader classes with a visitor pattern.

Bug: v8:9376
Change-Id: I70eab9ea4e74ca0fab39bf5998d6a602716a4202
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1771939Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#63485}
parent 3e545f38
......@@ -12,7 +12,7 @@ namespace torque {
namespace {
void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
std::ostream& cc_contents,
std::ostream& cc_contents, std::ostream& visitor,
std::unordered_set<const ClassType*>* done) {
// Make sure each class only gets generated once.
if (!type.IsExtern() || !done->insert(&type).second) return;
......@@ -21,7 +21,8 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
// We must emit the classes in dependency order. If the super class hasn't
// been emitted yet, go handle it first.
if (super_type != nullptr) {
GenerateClassDebugReader(*super_type, h_contents, cc_contents, done);
GenerateClassDebugReader(*super_type, h_contents, cc_contents, visitor,
done);
}
const std::string name = type.name();
......@@ -32,7 +33,24 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
h_contents << " inline Tq" << name << "(uintptr_t address) : Tq"
<< super_name << "(address) {}\n";
h_contents << " std::vector<std::unique_ptr<ObjectProperty>> "
"GetProperties(d::MemoryAccessor accessor);\n";
"GetProperties(d::MemoryAccessor accessor) const override;\n";
h_contents << " const char* GetName() const override;\n";
h_contents << " void Visit(TqObjectVisitor* visitor) const override;\n";
cc_contents << "\nconst char* Tq" << name << "::GetName() const {\n";
cc_contents << " return \"v8::internal::" << name << "\";\n";
cc_contents << "}\n";
cc_contents << "\nvoid Tq" << name
<< "::Visit(TqObjectVisitor* visitor) const {\n";
cc_contents << " visitor->Visit" << name << "(this);\n";
cc_contents << "}\n";
visitor << " virtual void Visit" << name << "(const Tq" << name
<< "* object) {\n";
visitor << " Visit" << super_name << "(object);\n";
visitor << " }\n";
std::stringstream get_props_impl;
for (const Field& field : type.fields()) {
......@@ -86,6 +104,8 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
"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)->name_and_type.type;
std::string index_type_name;
......@@ -113,6 +133,8 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
<< "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(v8::base::make_unique<ObjectProperty>(\""
......@@ -120,20 +142,21 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
<< field_cc_type << "\", " << address_getter << "()"
<< indexed_field_info << "));\n";
h_contents << " uintptr_t " << address_getter << "();\n";
h_contents << " uintptr_t " << address_getter << "() const;\n";
h_contents << " Value<" << field_value_type << "> " << field_getter
<< "(d::MemoryAccessor accessor);\n";
<< "(d::MemoryAccessor accessor " << index_param << ") const;\n";
cc_contents << "\nuintptr_t Tq" << name << "::" << address_getter
<< "() {\n";
<< "() 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) {\n";
<< "::" << 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
<< "(), reinterpret_cast<uint8_t*>(&value), sizeof(value));\n";
<< address_getter << "()" << index_offset
<< ", reinterpret_cast<uint8_t*>(&value), sizeof(value));\n";
cc_contents << " return {validity, "
<< (is_field_tagged ? "Decompress(value, address_)" : "value")
<< "};\n";
......@@ -143,7 +166,7 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
h_contents << "};\n";
cc_contents << "\nstd::vector<std::unique_ptr<ObjectProperty>> Tq" << name
<< "::GetProperties(d::MemoryAccessor accessor) {\n";
<< "::GetProperties(d::MemoryAccessor accessor) const {\n";
cc_contents << " std::vector<std::unique_ptr<ObjectProperty>> result = Tq"
<< super_name << "::GetProperties(accessor);\n";
cc_contents << get_props_impl.str();
......@@ -176,11 +199,19 @@ void ImplementationVisitor::GenerateClassDebugReaders(
NamespaceScope h_namespaces(h_contents, {"v8_debug_helper_internal"});
NamespaceScope cc_namespaces(cc_contents, {"v8_debug_helper_internal"});
std::stringstream visitor;
visitor << "\nclass TqObjectVisitor {\n";
visitor << " public:\n";
visitor << " virtual void VisitObject(const TqObject* object) {}\n";
std::unordered_set<const ClassType*> done;
for (const TypeAlias* alias : GlobalContext::GetClasses()) {
const ClassType* type = ClassType::DynamicCast(alias->type());
GenerateClassDebugReader(*type, h_contents, cc_contents, &done);
GenerateClassDebugReader(*type, h_contents, cc_contents, visitor, &done);
}
visitor << "};\n";
h_contents << visitor.str();
}
WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
......
......@@ -4,6 +4,13 @@
import("../../gni/v8.gni")
config("cctest_config") {
# Work around a bug in the gold linker.
if (use_gold && target_cpu == "x86") {
ldflags = [ "-Wl,--icf=none" ]
}
}
v8_executable("cctest") {
testonly = true
......@@ -28,6 +35,7 @@ v8_executable("cctest") {
configs = [
"../..:external_config",
"../..:internal_config_base",
":cctest_config",
]
ldflags = []
......
......@@ -109,8 +109,9 @@ TEST(GetObjectProperties) {
props = d::GetObjectProperties(properties_or_hash, &ReadMemory, roots);
CHECK(props->type_check_result ==
d::TypeCheckResult::kObjectPointerValidButInaccessible);
CHECK(props->type == std::string("v8::internal::Object"));
CHECK_EQ(props->num_properties, 0);
CHECK(props->type == std::string("v8::internal::HeapObject"));
CHECK_EQ(props->num_properties, 1);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CHECK(std::string(props->brief).substr(0, 21) ==
std::string("maybe EmptyFixedArray"));
......@@ -123,8 +124,9 @@ TEST(GetObjectProperties) {
props = d::GetObjectProperties(properties_or_hash, &ReadMemory, roots);
CHECK(props->type_check_result ==
d::TypeCheckResult::kObjectPointerValidButInaccessible);
CHECK(props->type == std::string("v8::internal::Object"));
CHECK_EQ(props->num_properties, 0);
CHECK(props->type == std::string("v8::internal::HeapObject"));
CHECK_EQ(props->num_properties, 1);
CheckProp(*props->properties[0], "v8::internal::Map", "map");
CHECK(std::string(props->brief).substr(0, 15) ==
std::string("EmptyFixedArray"));
}
......@@ -192,6 +194,33 @@ TEST(GetObjectProperties) {
CheckProp(*props2->properties[1], "uint32_t", "hash_field",
*reinterpret_cast<i::Tagged_t*>(props->properties[1]->address));
CheckProp(*props2->properties[2], "int32_t", "length", 2);
// Build a complicated string (multi-level cons with slices inside) to test
// string printing.
v = CompileRun(R"(
const alphabet = "abcdefghijklmnopqrstuvwxyz";
alphabet.substr(3,20) + alphabet.toUpperCase().substr(5,15) + "7")");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, roots);
CHECK(std::string(props->brief).substr(0, 38) ==
std::string("\"defghijklmnopqrstuvwFGHIJKLMNOPQRST7\""));
// Cause a failure when reading the "second" pointer within the top-level
// ConsString.
{
CheckProp(*props->properties[4], "v8::internal::String", "second");
uintptr_t second_address = props->properties[4]->address;
MemoryFailureRegion failure(second_address, second_address + 4);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, roots);
CHECK(std::string(props->brief).substr(0, 40) ==
std::string("\"defghijklmnopqrstuvwFGHIJKLMNOPQRST...\""));
}
// Build a very long string.
v = CompileRun("'a'.repeat(1000)");
o = v8::Utils::OpenHandle(*v);
props = d::GetObjectProperties(o->ptr(), &ReadMemory, roots);
CHECK(std::string(props->brief).substr(79, 7) == std::string("aa...\" "));
}
} // namespace internal
......
......@@ -4,6 +4,7 @@
#include "debug-helper-internal.h"
#include "src/common/ptr-compr-inl.h"
#include "torque-generated/class-debug-readers-tq.h"
namespace i = v8::internal;
......@@ -44,8 +45,14 @@ d::PropertyKind GetArrayKind(d::MemoryAccessResult mem_result) {
}
std::vector<std::unique_ptr<ObjectProperty>> TqObject::GetProperties(
d::MemoryAccessor accessor) {
d::MemoryAccessor accessor) const {
return std::vector<std::unique_ptr<ObjectProperty>>();
}
const char* TqObject::GetName() const { return "v8::internal::Object"; }
void TqObject::Visit(TqObjectVisitor* visitor) const {
visitor->VisitObject(this);
}
} // namespace v8_debug_helper_internal
......@@ -104,13 +104,18 @@ class ObjectPropertiesResult {
std::vector<d::ObjectProperty*> properties_raw_;
};
class TqObjectVisitor;
// Base class representing a V8 object in the debuggee's address space.
// Subclasses for specific object types are generated by the Torque compiler.
class TqObject {
public:
inline TqObject(uintptr_t address) : address_(address) {}
std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
d::MemoryAccessor accessor);
virtual ~TqObject() = default;
virtual std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
d::MemoryAccessor accessor) const;
virtual const char* GetName() const;
virtual void Visit(TqObjectVisitor* visitor) const;
protected:
uintptr_t address_;
......
This diff is collapsed.
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