[heap-profiler] Merge embedder nodes and V8 wrapper nodes.

Each DOM node has the corresponding V8 wrapper object. This leads to
apparent duplication in the heap snapshot and may confuse the users.

This patch allows the embedder to specify V8 wrapper for each embedder
node. In the heap snapshot the wrapper node will be merged into the
embedder node. The resulting node will have the same properties as
the embedder node. If the wrapper node name has a tag, then the tag
is also added to the merged node.

Bug: chromium:811925

Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I2492f5b28163a78aee707b9ced1b09ac4b203e3f
Reviewed-on: https://chromium-review.googlesource.com/919482
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Alexei Filippov <alph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51394}
This commit is contained in:
Ulan Degenbaev 2018-02-20 14:16:34 +01:00 committed by Commit Bot
parent da83b61848
commit 252e8a1c2d
4 changed files with 96 additions and 12 deletions

View File

@ -648,6 +648,12 @@ class V8_EXPORT EmbedderGraph {
virtual ~Node() = default;
virtual const char* Name() = 0;
virtual size_t SizeInBytes() = 0;
/**
* The corresponding V8 wrapper node if not null.
* During heap snapshot generation the embedder node and the V8 wrapper
* node will be merged into one node to simplify retaining paths.
*/
virtual Node* WrapperNode() { return nullptr; }
virtual bool IsRootNode() { return false; }
/** Must return true for non-V8 nodes. */
virtual bool IsEmbedderNode() { return true; }

View File

@ -1812,7 +1812,7 @@ void V8HeapExplorer::SetGcSubrootReference(Root root, const char* description,
// Add a shortcut to JS global object reference at snapshot root.
// That allows the user to easily find global objects. They are
// also used as starting points in distance calculations.
if (!child_obj->IsNativeContext()) return;
if (is_weak || !child_obj->IsNativeContext()) return;
JSGlobalObject* global = Context::cast(child_obj)->global_object();
if (!global->IsJSGlobalObject()) return;
@ -2016,29 +2016,52 @@ HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) {
class EmbedderGraphEntriesAllocator : public HeapEntriesAllocator {
public:
EmbedderGraphEntriesAllocator(HeapSnapshot* snapshot,
HeapEntry::Type entries_type)
explicit EmbedderGraphEntriesAllocator(HeapSnapshot* snapshot)
: snapshot_(snapshot),
names_(snapshot_->profiler()->names()),
heap_object_map_(snapshot_->profiler()->heap_object_map()),
entries_type_(entries_type) {}
heap_object_map_(snapshot_->profiler()->heap_object_map()) {}
virtual HeapEntry* AllocateEntry(HeapThing ptr);
private:
HeapSnapshot* snapshot_;
StringsStorage* names_;
HeapObjectsMap* heap_object_map_;
HeapEntry::Type entries_type_;
};
namespace {
const char* EmbedderGraphNodeName(StringsStorage* names,
EmbedderGraphImpl::Node* node) {
return names->GetCopy(node->Name());
}
HeapEntry::Type EmbedderGraphNodeType(EmbedderGraphImpl::Node* node) {
return HeapEntry::kNative;
}
// Merges the names of an embedder node and its wrapper node.
// If the wrapper node name contains a tag suffix (part after '/') then the
// result is the embedder node name concatenated with the tag suffix.
// Otherwise, the result is the embedder node name.
const char* MergeNames(StringsStorage* names, const char* embedder_name,
const char* wrapper_name) {
for (const char* suffix = wrapper_name; suffix; suffix++) {
if (*suffix == '/') {
return names->GetFormatted("%s %s", embedder_name, suffix);
}
}
return embedder_name;
}
} // anonymous namespace
HeapEntry* EmbedderGraphEntriesAllocator::AllocateEntry(HeapThing ptr) {
EmbedderGraphImpl::Node* node =
reinterpret_cast<EmbedderGraphImpl::Node*>(ptr);
DCHECK(node->IsEmbedderNode());
const char* name = names_->GetCopy(node->Name());
size_t size = node->SizeInBytes();
return snapshot_->AddEntry(
entries_type_, name,
EmbedderGraphNodeType(node), EmbedderGraphNodeName(names_, node),
static_cast<SnapshotObjectId>(reinterpret_cast<uintptr_t>(node) << 1),
static_cast<int>(size), 0);
}
@ -2056,7 +2079,7 @@ NativeObjectsExplorer::NativeObjectsExplorer(
native_entries_allocator_(
new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative)),
embedder_graph_entries_allocator_(
new EmbedderGraphEntriesAllocator(snapshot, HeapEntry::kNative)),
new EmbedderGraphEntriesAllocator(snapshot)),
filler_(nullptr) {}
NativeObjectsExplorer::~NativeObjectsExplorer() {
@ -2149,6 +2172,10 @@ std::vector<HeapObject*>* NativeObjectsExplorer::GetVectorMaybeDisposeInfo(
HeapEntry* NativeObjectsExplorer::EntryForEmbedderGraphNode(
EmbedderGraphImpl::Node* node) {
EmbedderGraphImpl::Node* wrapper = node->WrapperNode();
if (wrapper) {
node = wrapper;
}
if (node->IsEmbedderNode()) {
return filler_->FindOrAddEntry(node,
embedder_graph_entries_allocator_.get());
@ -2172,13 +2199,21 @@ bool NativeObjectsExplorer::IterateAndExtractReferences(
DisallowHeapAllocation no_allocation;
EmbedderGraphImpl graph;
snapshot_->profiler()->BuildEmbedderGraph(isolate_, &graph);
// Fill root nodes of the graph.
for (const auto& node : graph.nodes()) {
if (node->IsRootNode()) {
filler_->SetIndexedAutoIndexReference(
HeapGraphEdge::kElement, snapshot_->root()->index(),
EntryForEmbedderGraphNode(node.get()));
}
// Adjust the name and the type of the V8 wrapper node.
auto wrapper = node->WrapperNode();
if (wrapper) {
HeapEntry* wrapper_entry = EntryForEmbedderGraphNode(wrapper);
wrapper_entry->set_name(
MergeNames(names_, EmbedderGraphNodeName(names_, node.get()),
wrapper_entry->name()));
wrapper_entry->set_type(EmbedderGraphNodeType(node.get()));
}
}
// Fill edges of the graph.
for (const auto& edge : graph.edges()) {

View File

@ -113,6 +113,7 @@ class HeapEntry BASE_EMBEDDED {
HeapSnapshot* snapshot() { return snapshot_; }
Type type() const { return static_cast<Type>(type_); }
void set_type(Type type) { type_ = type; }
const char* name() const { return name_; }
void set_name(const char* name) { name_ = name; }
SnapshotObjectId id() const { return id_; }

View File

@ -2816,16 +2816,19 @@ TEST(JSPromise) {
class EmbedderNode : public v8::EmbedderGraph::Node {
public:
explicit EmbedderNode(const char* name, size_t size)
: name_(name), size_(size) {}
EmbedderNode(const char* name, size_t size,
v8::EmbedderGraph::Node* wrapper_node = nullptr)
: name_(name), size_(size), wrapper_node_(wrapper_node) {}
// Graph::Node overrides.
const char* Name() override { return name_; }
size_t SizeInBytes() override { return size_; }
Node* WrapperNode() override { return wrapper_node_; }
private:
const char* name_;
size_t size_;
Node* wrapper_node_;
};
class EmbedderRootNode : public EmbedderNode {
@ -2923,6 +2926,45 @@ TEST(StrongHandleAnnotation) {
CHECK_EQ(2, found);
}
void BuildEmbedderGraphWithWrapperNode(v8::Isolate* v8_isolate,
v8::EmbedderGraph* graph) {
using Node = v8::EmbedderGraph::Node;
Node* global_node = graph->V8Node(*global_object_pointer);
Node* wrapper_node = graph->AddNode(
std::unique_ptr<Node>(new EmbedderNode("WrapperNode / TAG", 10)));
Node* embedder_node = graph->AddNode(std::unique_ptr<Node>(
new EmbedderNode("EmbedderNode", 10, wrapper_node)));
Node* other_node =
graph->AddNode(std::unique_ptr<Node>(new EmbedderNode("OtherNode", 20)));
graph->AddEdge(global_node, embedder_node);
graph->AddEdge(wrapper_node, other_node);
}
TEST(EmbedderGraphWithWrapperNode) {
i::FLAG_heap_profiler_use_embedder_graph = true;
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate());
v8::Local<v8::Value> global_object =
v8::Utils::ToLocal(i::Handle<i::JSObject>(
(isolate->context()->native_context()->global_object())));
global_object_pointer = &global_object;
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
heap_profiler->SetBuildEmbedderGraphCallback(
BuildEmbedderGraphWithWrapperNode);
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
CHECK(ValidateSnapshot(snapshot));
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
const v8::HeapGraphNode* embedder_node =
GetChildByName(global, "EmbedderNode / TAG");
const v8::HeapGraphNode* other_node =
GetChildByName(embedder_node, "OtherNode");
CHECK(other_node);
const v8::HeapGraphNode* wrapper_node =
GetChildByName(embedder_node, "WrapperNode / TAG");
CHECK(!wrapper_node);
}
static inline i::Address ToAddress(int n) {
return reinterpret_cast<i::Address>(n);
}