Commit 4d0360aa authored by Seth Brenith's avatar Seth Brenith Committed by Commit Bot

[tools] Add list of classes to v8_debug_helper

This change extends v8_debug_helper to export a new method that returns
a list of all known heap object types.

Why? We can substantially improve the user experience in our work-in-
progress WinDbg extension if we register handlers not only for
v8::internal::Object but for every specific HeapObject type. This has
two benefits:

- You save a click: if you're expanding a local variable of a more
  specific type than Object, you can see properties immediately rather
  than first needing to expand a sub-item that casts the variable to
  Object.
- You retain the type hint: GetObjectProperties accepts a type hint
  string, and it's super important to pass it when working in a crash
  dump because the object's Map is probably inaccessible. If we have to
  cast to Object first, we lose this data.

Bug: v8:9376
Change-Id: I4d635a1826574a3d08ac657e848e1fe7b83849fe
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1822859Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Seth Brenith <seth.brenith@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#64331}
parent e69e343f
......@@ -10,7 +10,7 @@ namespace v8 {
namespace internal {
namespace torque {
const char* tq_object_override_decls =
constexpr char kTqObjectOverrideDecls[] =
R"( std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
d::MemoryAccessor accessor) const override;
const char* GetName() const override;
......@@ -18,9 +18,17 @@ const char* tq_object_override_decls =
bool IsSuperclassOf(const TqObject* other) const override;
)";
constexpr char kObjectClassListDefinition[] = R"(
const d::ClassList kObjectClassList {
sizeof(kObjectClassNames) / sizeof(const char*),
kObjectClassNames,
};
)";
namespace {
void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
std::ostream& cc_contents, std::ostream& visitor,
std::ostream& class_names,
std::unordered_set<const ClassType*>* done) {
// Make sure each class only gets generated once.
if (!done->insert(&type).second) return;
......@@ -30,7 +38,7 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
// been emitted yet, go handle it first.
if (super_type != nullptr) {
GenerateClassDebugReader(*super_type, h_contents, cc_contents, visitor,
done);
class_names, done);
}
// Classes with undefined layout don't grant any particular value here and may
......@@ -44,7 +52,7 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
h_contents << " public:\n";
h_contents << " inline Tq" << name << "(uintptr_t address) : Tq"
<< super_name << "(address) {}\n";
h_contents << tq_object_override_decls;
h_contents << kTqObjectOverrideDecls;
cc_contents << "\nconst char* Tq" << name << "::GetName() const {\n";
cc_contents << " return \"v8::internal::" << name << "\";\n";
......@@ -67,6 +75,8 @@ void GenerateClassDebugReader(const ClassType& type, std::ostream& h_contents,
visitor << " Visit" << super_name << "(object);\n";
visitor << " }\n";
class_names << " \"v8::internal::" << name << "\",\n";
std::stringstream get_props_impl;
for (const Field& field : type.fields()) {
......@@ -226,14 +236,22 @@ void ImplementationVisitor::GenerateClassDebugReaders(
visitor << " public:\n";
visitor << " virtual void VisitObject(const TqObject* object) {}\n";
std::stringstream class_names;
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, visitor, &done);
GenerateClassDebugReader(*type, h_contents, cc_contents, visitor,
class_names, &done);
}
visitor << "};\n";
h_contents << visitor.str();
cc_contents << "\nconst char* kObjectClassNames[] {\n";
cc_contents << class_names.str();
cc_contents << "};\n";
cc_contents << kObjectClassListDefinition;
}
WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
......
......@@ -256,5 +256,22 @@ TEST(GetObjectProperties) {
CHECK(std::string(props->brief).substr(79, 7) == std::string("aa...\" "));
}
TEST(ListObjectClasses) {
CcTest::InitializeVM();
// The ListObjectClasses result will change as classes are added, removed, or
// renamed. Just check that a few expected classes are included in the list,
// and that there are no duplicates.
const d::ClassList* class_list = d::ListObjectClasses();
std::unordered_set<std::string> class_set;
for (size_t i = 0; i < class_list->num_class_names; ++i) {
CHECK_WITH_MSG(class_set.insert(class_list->class_names[i]).second,
"there should be no duplicate entries");
}
CHECK_NE(class_set.find("v8::internal::HeapObject"), class_set.end());
CHECK_NE(class_set.find("v8::internal::String"), class_set.end());
CHECK_NE(class_set.find("v8::internal::JSRegExp"), class_set.end());
}
} // namespace internal
} // namespace v8
......@@ -86,6 +86,7 @@ v8_component("v8_debug_helper") {
"get-object-properties.cc",
"heap-constants.cc",
"heap-constants.h",
"list-object-classes.cc",
]
deps = [
......
......@@ -64,10 +64,9 @@ class ObjectProperty {
};
class ObjectPropertiesResult;
using ObjectPropertiesResultInternal = ObjectPropertiesResult;
struct ObjectPropertiesResultExtended : public d::ObjectPropertiesResult {
ObjectPropertiesResultInternal* base; // Back reference for cleanup
// Back reference for cleanup.
v8_debug_helper_internal::ObjectPropertiesResult* base;
};
// Internal version of API class v8::debug_helper::ObjectPropertiesResult.
......@@ -151,6 +150,10 @@ uintptr_t EnsureDecompressed(uintptr_t address,
// into the corresponding PropertyKind for the array.
d::PropertyKind GetArrayKind(d::MemoryAccessResult mem_result);
// List of fully-qualified names for every Object subtype, generated based on
// Torque class definitions.
extern const d::ClassList kObjectClassList;
} // namespace v8_debug_helper_internal
#endif
......@@ -141,6 +141,12 @@ struct HeapAddresses {
uintptr_t any_heap_pointer;
};
// Result type for ListObjectClasses.
struct ClassList {
size_t num_class_names;
const char* const* class_names; // Fully qualified class names.
};
} // namespace debug_helper
} // namespace v8
......@@ -154,6 +160,8 @@ _v8_debug_helper_GetObjectProperties(
const char* type_hint);
V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult(
v8::debug_helper::ObjectPropertiesResult* result);
V8_DEBUG_HELPER_EXPORT const v8::debug_helper::ClassList*
_v8_debug_helper_ListObjectClasses();
}
namespace v8 {
......@@ -183,6 +191,11 @@ inline ObjectPropertiesResultPtr GetObjectProperties(
object, memory_accessor, heap_addresses, type_hint));
}
// Get a list of all class names deriving from v8::internal::Object.
inline const ClassList* ListObjectClasses() {
return _v8_debug_helper_ListObjectClasses();
}
} // namespace debug_helper
} // namespace v8
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "debug-helper-internal.h"
#include "torque-generated/class-debug-readers-tq.h"
namespace di = v8_debug_helper_internal;
extern "C" {
V8_DEBUG_HELPER_EXPORT const d::ClassList*
_v8_debug_helper_ListObjectClasses() {
return &di::kObjectClassList;
}
}
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