src: implement GetDetachedness() in MemoryRetainerNode

This allows us to mark weak/detached references in the heap snapshot.
Also mark weak/detached BaseObject with Detachedness::kDetached
so that the state of the reference can be displayed by frontend
consuming the heap snapshot.

PR-URL: https://github.com/nodejs/node/pull/44803
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Joyee Cheung 2022-09-27 14:15:51 +08:00
parent e84e2e6c85
commit 3ab2d4f362
No known key found for this signature in database
GPG Key ID: 92B78A53C8303B8D
6 changed files with 34 additions and 1 deletions

View File

@ -115,6 +115,11 @@ bool BaseObject::IsWeakOrDetached() const {
return pd->wants_weak_jsobj || pd->is_detached;
}
v8::EmbedderGraph::Node::Detachedness BaseObject::GetDetachedness() const {
return IsWeakOrDetached() ? v8::EmbedderGraph::Node::Detachedness::kDetached
: v8::EmbedderGraph::Node::Detachedness::kUnknown;
}
template <int Field>
void BaseObject::InternalFieldGet(
v8::Local<v8::String> property,

View File

@ -96,6 +96,8 @@ class BaseObject : public MemoryRetainer {
// to it anymore.
inline bool IsWeakOrDetached() const;
inline v8::EmbedderGraph::Node::Detachedness GetDetachedness() const override;
// Utility to create a FunctionTemplate with one internal field (used for
// the `BaseObject*` pointer) and a constructor that initializes that field
// to `nullptr`.

View File

@ -31,6 +31,7 @@ class MemoryRetainerNode : public v8::EmbedderGraph::Node {
name_ = retainer_->MemoryInfoName();
size_ = retainer_->SelfSize();
detachedness_ = retainer_->GetDetachedness();
}
inline MemoryRetainerNode(MemoryTracker* tracker,
@ -57,6 +58,9 @@ class MemoryRetainerNode : public v8::EmbedderGraph::Node {
}
return is_root_node_;
}
v8::EmbedderGraph::Node::Detachedness GetDetachedness() override {
return detachedness_;
}
private:
friend class MemoryTracker;
@ -73,6 +77,8 @@ class MemoryRetainerNode : public v8::EmbedderGraph::Node {
bool is_root_node_ = false;
std::string name_;
size_t size_ = 0;
v8::EmbedderGraph::Node::Detachedness detachedness_ =
v8::EmbedderGraph::Node::Detachedness::kUnknown;
};
void MemoryTracker::TrackFieldWithSize(const char* edge_name,

View File

@ -127,6 +127,9 @@ class MemoryRetainer {
}
virtual bool IsRootNode() const { return false; }
virtual v8::EmbedderGraph::Node::Detachedness GetDetachedness() const {
return v8::EmbedderGraph::Node::Detachedness::kUnknown;
}
};
class MemoryTracker {

View File

@ -142,6 +142,22 @@ class State {
}
}
}
if (expectation.detachedness !== undefined) {
const matchedNodes = rootNodes.filter(
(node) => node.detachedness === expectation.detachedness);
if (loose) {
assert(matchedNodes.length >= rootNodes.length,
`Expect to find at least ${rootNodes.length} with ` +
`detachedness ${expectation.detachedness}, ` +
`found ${matchedNodes.length}`);
} else {
assert.strictEqual(
matchedNodes.length, rootNodes.length,
`Expect to find ${rootNodes.length} with detachedness ` +
`${expectation.detachedness}, found ${matchedNodes.length}`);
}
}
}
}

View File

@ -13,6 +13,7 @@ validateSnapshotNodes('Node / ChannelWrap', [
{ node_name: 'Node / NodeAresTask::List', edge_name: 'task_list' },
// `Node / ChannelWrap` (C++) -> `ChannelWrap` (JS)
{ node_name: 'ChannelWrap', edge_name: 'wrapped' },
]
],
detachedness: 2
},
]);