From 950ccee3860286c4e780baaa2e39eb4a5dc8b7c8 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 5 Oct 2018 21:39:43 +0200 Subject: [PATCH] src: name EmbededderGraph edges and use class names for nodes This patch: - Refactors the `MemoryRetainer` API so that the impementer no longer calls `TrackThis()` that sets the size of node on the top of the stack, which may be hard to understand. Instead now they implements `SelfSize()` to provide their self sizes. Also documents the API in the header. - Refactors `MemoryTracker` so it calls `MemoryInfoName()` and `SelfSize()` of `MemoryRetainer` to retrieve info about them, and separate `node_names` and `edge_names` so the edges can be properly named with reference names and the nodes can be named with class names. (Previously the nodes are named with reference names while the edges are all indexed and appear as array elements). - Adds `SET_MEMORY_INFO_NAME()`, `SET_SELF_SIZE()` and `SET_NO_MEMORY_INFO()` convenience macros - Fixes a few `MemoryInfo` calls in some `MemoryRetainers` to track their references properly. - Refactors the heapdump tests to check both node names and edge names, distinguishing between wrapped JS nodes (without prefixes) and embedder wrappers (prefixed with `Node / `). Backport-PR-URL: https://github.com/nodejs/node/pull/23295 PR-URL: https://github.com/nodejs/node/pull/23072 Reviewed-By: Anna Henningsen --- lib/internal/test/heap.js | 4 +- src/async_wrap.cc | 12 +- src/base_object.h | 5 - src/cares_wrap.cc | 133 ++++----- src/connect_wrap.h | 8 +- src/fs_event_wrap.cc | 8 +- src/heap_utils.cc | 48 ++-- src/inspector_js_api.cc | 7 +- src/js_stream.h | 8 +- src/memory_tracker-inl.h | 268 ++++++++++++++----- src/memory_tracker.h | 196 +++++++++++--- src/module_wrap.h | 4 +- src/node_contextify.cc | 10 +- src/node_crypto.h | 63 ++--- src/node_crypto_bio.h | 6 +- src/node_file.cc | 4 + src/node_file.h | 29 +- src/node_http2.cc | 2 - src/node_http2.h | 20 +- src/node_http_parser.cc | 4 +- src/node_i18n.cc | 8 +- src/node_messaging.cc | 2 - src/node_messaging.h | 10 +- src/node_serdes.cc | 16 +- src/node_stat_watcher.h | 8 +- src/node_trace_events.cc | 4 +- src/node_worker.h | 12 +- src/node_zlib.cc | 9 +- src/pipe_wrap.h | 8 +- src/process_wrap.cc | 8 +- src/sharedarraybuffer_metadata.cc | 8 +- src/signal_wrap.cc | 8 +- src/stream_base.h | 18 +- src/stream_pipe.h | 8 +- src/tcp_wrap.h | 6 +- src/timer_wrap.cc | 6 +- src/tls_wrap.cc | 1 - src/tls_wrap.h | 3 +- src/tty_wrap.h | 8 +- src/udp_wrap.cc | 8 +- src/udp_wrap.h | 8 +- test/cctest/test_node_postmortem_metadata.cc | 18 +- test/common/heap.js | 115 +++++--- test/parallel/test-heapdump-dns.js | 10 +- test/parallel/test-heapdump-fs-promise.js | 8 +- test/parallel/test-heapdump-http2.js | 38 +-- test/parallel/test-heapdump-inspector.js | 11 +- test/parallel/test-heapdump-tls.js | 11 +- test/parallel/test-heapdump-worker.js | 16 +- test/parallel/test-heapdump-zlib.js | 8 +- 50 files changed, 734 insertions(+), 507 deletions(-) diff --git a/lib/internal/test/heap.js b/lib/internal/test/heap.js index a9260f651b9c1a..0a5bf7059713a5 100644 --- a/lib/internal/test/heap.js +++ b/lib/internal/test/heap.js @@ -37,8 +37,8 @@ function createJSHeapDump() { const fromNode = nodes[fromNodeIndex]; const edge = { type, - toNode, - fromNode, + to: toNode, + from: fromNode, name: typeof name_or_index === 'string' ? name_or_index : null }; toNode.incomingEdges.push(edge); diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 00c71b47e4ad40..0cfe0202ccf675 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -72,9 +72,9 @@ struct AsyncWrapObject : public AsyncWrap { inline AsyncWrapObject(Environment* env, Local object, ProviderType type) : AsyncWrap(env, object, type) {} - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(AsyncWrapObject) + SET_SELF_SIZE(AsyncWrapObject) }; @@ -180,9 +180,9 @@ class PromiseWrap : public AsyncWrap { MakeWeak(); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(PromiseWrap) + SET_SELF_SIZE(PromiseWrap) static constexpr int kPromiseField = 1; static constexpr int kIsChainedPromiseField = 2; diff --git a/src/base_object.h b/src/base_object.h index 64a237143386f2..e0f3f27950e7d0 100644 --- a/src/base_object.h +++ b/src/base_object.h @@ -33,11 +33,6 @@ namespace node { class Environment; -#define ADD_MEMORY_INFO_NAME(name) \ - std::string MemoryInfoName() const override { \ - return #name; \ - } - class BaseObject : public MemoryRetainer { public: // Associates this object with `object`. It uses the 0th internal field for diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 5970ec387fa516..1941fa1b4e3a1d 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -127,8 +127,9 @@ struct node_ares_task : public MemoryRetainer { ares_socket_t sock; uv_poll_t poll_watcher; - void MemoryInfo(MemoryTracker* tracker) const override; - ADD_MEMORY_INFO_NAME(node_ares_task) + inline void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(node_ares_task) + SET_SELF_SIZE(node_ares_task) }; struct TaskHash { @@ -172,13 +173,13 @@ class ChannelWrap : public AsyncWrap { inline node_ares_task_list* task_list() { return &task_list_; } void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); if (timer_handle_ != nullptr) - tracker->TrackFieldWithSize("timer handle", sizeof(*timer_handle_)); - tracker->TrackField("node_ares_task_list", task_list_); + tracker->TrackField("timer_handle", *timer_handle_); + tracker->TrackField("task_list", task_list_, "node_ares_task_list"); } - ADD_MEMORY_INFO_NAME(ChannelWrap) + SET_MEMORY_INFO_NAME(ChannelWrap) + SET_SELF_SIZE(ChannelWrap) static void AresTimeout(uv_timer_t* handle); @@ -192,11 +193,6 @@ class ChannelWrap : public AsyncWrap { node_ares_task_list task_list_; }; -void node_ares_task::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackThis(this); - tracker->TrackField("channel", channel); -} - ChannelWrap::ChannelWrap(Environment* env, Local object) : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), @@ -225,11 +221,9 @@ class GetAddrInfoReqWrap : public ReqWrap { Local req_wrap_obj, bool verbatim); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(GetAddrInfoReqWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(GetAddrInfoReqWrap) + SET_SELF_SIZE(GetAddrInfoReqWrap) bool verbatim() const { return verbatim_; } @@ -249,11 +243,9 @@ class GetNameInfoReqWrap : public ReqWrap { public: GetNameInfoReqWrap(Environment* env, Local req_wrap_obj); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(GetNameInfoReqWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(GetNameInfoReqWrap) + SET_SELF_SIZE(GetNameInfoReqWrap) }; GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, @@ -298,6 +290,9 @@ void ares_poll_close_cb(uv_poll_t* watcher) { delete task; } +void node_ares_task::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("channel", channel); +} /* Allocates and returns a new node_ares_task */ node_ares_task* ares_task_create(ChannelWrap* channel, ares_socket_t sock) { @@ -1195,11 +1190,9 @@ class QueryAnyWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryAnyWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryAnyWrap) + SET_SELF_SIZE(QueryAnyWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1376,11 +1369,9 @@ class QueryAWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryAWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryAWrap) + SET_SELF_SIZE(QueryAWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1424,11 +1415,9 @@ class QueryAaaaWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryAaaaWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryAaaaWrap) + SET_SELF_SIZE(QueryAaaaWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1472,11 +1461,9 @@ class QueryCnameWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryCnameWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryCnameWrap) + SET_SELF_SIZE(QueryCnameWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1507,11 +1494,9 @@ class QueryMxWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryMxWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryMxWrap) + SET_SELF_SIZE(QueryMxWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1542,11 +1527,9 @@ class QueryNsWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryNsWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryNsWrap) + SET_SELF_SIZE(QueryNsWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1577,11 +1560,9 @@ class QueryTxtWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryTxtWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryTxtWrap) + SET_SELF_SIZE(QueryTxtWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1611,11 +1592,9 @@ class QuerySrvWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QuerySrvWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QuerySrvWrap) + SET_SELF_SIZE(QuerySrvWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1644,11 +1623,9 @@ class QueryPtrWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryPtrWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryPtrWrap) + SET_SELF_SIZE(QueryPtrWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1679,11 +1656,9 @@ class QueryNaptrWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QueryNaptrWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QueryNaptrWrap) + SET_SELF_SIZE(QueryNaptrWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1713,11 +1688,9 @@ class QuerySoaWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(QuerySoaWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(QuerySoaWrap) + SET_SELF_SIZE(QuerySoaWrap) protected: void Parse(unsigned char* buf, int len) override { @@ -1801,11 +1774,9 @@ class GetHostByAddrWrap: public QueryWrap { return 0; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(GetHostByAddrWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(GetHostByAddrWrap) + SET_SELF_SIZE(GetHostByAddrWrap) protected: void Parse(struct hostent* host) override { diff --git a/src/connect_wrap.h b/src/connect_wrap.h index 2370157eaa2a11..88221b77468631 100644 --- a/src/connect_wrap.h +++ b/src/connect_wrap.h @@ -16,11 +16,9 @@ class ConnectWrap : public ReqWrap { v8::Local req_wrap_obj, AsyncWrap::ProviderType provider); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(ConnectWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(ConnectWrap) + SET_SELF_SIZE(ConnectWrap) }; } // namespace node diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 1444937e7a853e..130996a6b7fcdd 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -57,11 +57,9 @@ class FSEventWrap: public HandleWrap { static void Start(const FunctionCallbackInfo& args); static void GetInitialized(const FunctionCallbackInfo& args); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(FSEventWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(FSEventWrap) + SET_SELF_SIZE(FSEventWrap) private: static const encoding kDefaultEncoding = UTF8; diff --git a/src/heap_utils.cc b/src/heap_utils.cc index 5ed5119bdb0ca0..c4c42bb3a17fd4 100644 --- a/src/heap_utils.cc +++ b/src/heap_utils.cc @@ -110,15 +110,28 @@ class JSGraph : public EmbedderGraph { for (const std::unique_ptr& n : nodes_) { Local obj = info_objects[n.get()]; Local value; - if (!String::NewFromUtf8(isolate_, n->Name(), - v8::NewStringType::kNormal).ToLocal(&value) || + std::string name_str; + const char* prefix = n->NamePrefix(); + if (prefix == nullptr) { + name_str = n->Name(); + } else { + name_str = n->NamePrefix(); + name_str += " "; + name_str += n->Name(); + } + if (!String::NewFromUtf8( + isolate_, name_str.c_str(), v8::NewStringType::kNormal) + .ToLocal(&value) || obj->Set(context, name_string, value).IsNothing() || - obj->Set(context, is_root_string, - Boolean::New(isolate_, n->IsRootNode())).IsNothing() || - obj->Set(context, size_string, - Number::New(isolate_, n->SizeInBytes())).IsNothing() || - obj->Set(context, edges_string, - Array::New(isolate_)).IsNothing()) { + obj->Set(context, + is_root_string, + Boolean::New(isolate_, n->IsRootNode())) + .IsNothing() || + obj->Set(context, + size_string, + Number::New(isolate_, n->SizeInBytes())) + .IsNothing() || + obj->Set(context, edges_string, Array::New(isolate_)).IsNothing()) { return MaybeLocal(); } if (nodes->Set(context, i++, obj).IsNothing()) @@ -152,20 +165,21 @@ class JSGraph : public EmbedderGraph { size_t j = 0; for (const auto& edge : edge_info.second) { Local to_object = info_objects[edge.second]; - Local edge_info = Object::New(isolate_); + Local edge_obj = Object::New(isolate_); Local edge_name_value; const char* edge_name = edge.first; - if (edge_name != nullptr && - !String::NewFromUtf8( - isolate_, edge_name, v8::NewStringType::kNormal) - .ToLocal(&edge_name_value)) { - return MaybeLocal(); + if (edge_name != nullptr) { + if (!String::NewFromUtf8( + isolate_, edge_name, v8::NewStringType::kNormal) + .ToLocal(&edge_name_value)) { + return MaybeLocal(); + } } else { edge_name_value = Number::New(isolate_, j++); } - if (edge_info->Set(context, name_string, edge_name_value).IsNothing() || - edge_info->Set(context, to_string, to_object).IsNothing() || - edges.As()->Set(context, i++, edge_info).IsNothing()) { + if (edge_obj->Set(context, name_string, edge_name_value).IsNothing() || + edge_obj->Set(context, to_string, to_object).IsNothing() || + edges.As()->Set(context, i++, edge_obj).IsNothing()) { return MaybeLocal(); } } diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index 49e1dcc6e8a30c..8ec6603e5b4bc3 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -105,12 +105,13 @@ class JSBindingsConnection : public AsyncWrap { } void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("callback", callback_); - tracker->TrackFieldWithSize("session", sizeof(*session_)); + tracker->TrackFieldWithSize( + "session", sizeof(*session_), "InspectorSession"); } - ADD_MEMORY_INFO_NAME(JSBindingsConnection) + SET_MEMORY_INFO_NAME(JSBindingsConnection) + SET_SELF_SIZE(JSBindingsConnection) private: std::unique_ptr session_; diff --git a/src/js_stream.h b/src/js_stream.h index 05fb688f2f4115..6612e558aea1d7 100644 --- a/src/js_stream.h +++ b/src/js_stream.h @@ -27,11 +27,9 @@ class JSStream : public AsyncWrap, public StreamBase { size_t count, uv_stream_t* send_handle) override; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(JSStream) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(JSStream) + SET_SELF_SIZE(JSStream) protected: JSStream(Environment* env, v8::Local obj); diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h index 568a4364f9c64d..20300297043ea7 100644 --- a/src/memory_tracker-inl.h +++ b/src/memory_tracker-inl.h @@ -7,23 +7,40 @@ namespace node { +// Fallback edge_name if node_name is not available, or "" if edge_name +// is not available either. +inline const char* GetNodeName(const char* node_name, const char* edge_name) { + if (node_name != nullptr) { + return node_name; + } + if (edge_name != nullptr) { + return edge_name; + } + return ""; +} + class MemoryRetainerNode : public v8::EmbedderGraph::Node { public: - explicit inline MemoryRetainerNode(MemoryTracker* tracker, - const MemoryRetainer* retainer, - const char* name) - : retainer_(retainer) { - if (retainer_ != nullptr) { - v8::HandleScope handle_scope(tracker->isolate()); - v8::Local obj = retainer_->WrappedObject(); - if (!obj.IsEmpty()) - wrapper_node_ = tracker->graph()->V8Node(obj); + inline MemoryRetainerNode(MemoryTracker* tracker, + const MemoryRetainer* retainer) + : retainer_(retainer) { + CHECK_NOT_NULL(retainer_); + v8::HandleScope handle_scope(tracker->isolate()); + v8::Local obj = retainer_->WrappedObject(); + if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj); + + name_ = retainer_->MemoryInfoName(); + size_ = retainer_->SelfSize(); + } - name_ = retainer_->MemoryInfoName(); - } - if (name_.empty() && name != nullptr) { - name_ = name; - } + inline MemoryRetainerNode(MemoryTracker* tracker, + const char* name, + size_t size, + bool is_root_node = false) + : retainer_(nullptr) { + name_ = name; + size_ = size; + is_root_node_ = is_root_node; } const char* Name() override { return name_.c_str(); } @@ -35,60 +52,92 @@ class MemoryRetainerNode : public v8::EmbedderGraph::Node { Node* JSWrapperNode() { return wrapper_node_; } bool IsRootNode() override { - return retainer_ != nullptr && retainer_->IsRootNode(); + if (retainer_ != nullptr) { + return retainer_->IsRootNode(); + } + return is_root_node_; } private: friend class MemoryTracker; - Node* wrapper_node_ = nullptr; + // If retainer_ is not nullptr, then it must have a wrapper_node_, + // and we have + // name_ == retainer_->MemoryInfoName() + // size_ == retainer_->SelfSize() + // is_root_node_ == retainer_->IsRootNode() const MemoryRetainer* retainer_; + Node* wrapper_node_ = nullptr; + + // Otherwise (retainer == nullptr), we set these fields in an ad-hoc way + bool is_root_node_ = false; std::string name_; size_t size_ = 0; }; -template -void MemoryTracker::TrackThis(const T* obj) { - CurrentNode()->size_ = sizeof(T); -} - -void MemoryTracker::TrackFieldWithSize(const char* name, size_t size) { - if (size > 0) - AddNode(name)->size_ = size; +void MemoryTracker::TrackFieldWithSize(const char* edge_name, + size_t size, + const char* node_name) { + if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name); } -void MemoryTracker::TrackField(const char* name, const MemoryRetainer& value) { - TrackField(name, &value); +void MemoryTracker::TrackField(const char* edge_name, + const MemoryRetainer& value, + const char* node_name) { + TrackField(edge_name, &value); } -void MemoryTracker::TrackField(const char* name, const MemoryRetainer* value) { - if (track_only_self_ || value == nullptr) return; +void MemoryTracker::TrackField(const char* edge_name, + const MemoryRetainer* value, + const char* node_name) { + if (value == nullptr) return; auto it = seen_.find(value); if (it != seen_.end()) { - graph_->AddEdge(CurrentNode(), it->second); + // For ABI compatibility, we did not backport the virtual function + // AddEdge() with a name as last argument back to v10.x. + graph_->AddEdge(CurrentNode(), it->second/*, edge_name */); } else { - Track(value, name); + Track(value, edge_name); } } template -void MemoryTracker::TrackField(const char* name, - const std::unique_ptr& value) { - TrackField(name, value.get()); +void MemoryTracker::TrackField(const char* edge_name, + const std::unique_ptr& value, + const char* node_name) { + if (value.get() == nullptr) { + return; + } + TrackField(edge_name, value.get(), node_name); } template -void MemoryTracker::TrackField(const char* name, const T& value) { +void MemoryTracker::TrackField(const char* edge_name, + const T& value, + const char* node_name, + const char* element_name, + bool subtract_from_self) { + // If the container is empty, the size has been accounted into the parent's + // self size if (value.begin() == value.end()) return; - size_t index = 0; - PushNode(name); - for (Iterator it = value.begin(); it != value.end(); ++it) - TrackField(std::to_string(index++).c_str(), *it); + // Fall back to edge name if node names are not provided + if (CurrentNode() != nullptr && subtract_from_self) { + // Shift the self size of this container out to a separate node + CurrentNode()->size_ -= sizeof(T); + } + PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name); + for (Iterator it = value.begin(); it != value.end(); ++it) { + // Use nullptr as edge names so the elements appear as indexed properties + TrackField(nullptr, *it, element_name); + } PopNode(); } template -void MemoryTracker::TrackField(const char* name, const std::queue& value) { +void MemoryTracker::TrackField(const char* edge_name, + const std::queue& value, + const char* node_name, + const char* element_name) { struct ContainerGetter : public std::queue { static const typename std::queue::container_type& Get( const std::queue& value) { @@ -97,61 +146,103 @@ void MemoryTracker::TrackField(const char* name, const std::queue& value) { }; const auto& container = ContainerGetter::Get(value); - TrackField(name, container); + TrackField(edge_name, container, node_name, element_name); } template -void MemoryTracker::TrackField(const char* name, const T& value) { +void MemoryTracker::TrackField(const char* edge_name, + const T& value, + const char* node_name) { // For numbers, creating new nodes is not worth the overhead. CurrentNode()->size_ += sizeof(T); } template -void MemoryTracker::TrackField(const char* name, const std::pair& value) { - PushNode(name); +void MemoryTracker::TrackField(const char* edge_name, + const std::pair& value, + const char* node_name) { + PushNode(node_name == nullptr ? "pair" : node_name, + sizeof(const std::pair), + edge_name); + // TODO(joyeecheung): special case if one of these is a number type + // that meets the test_for_number trait so that their sizes don't get + // merged into the pair node TrackField("first", value.first); TrackField("second", value.second); PopNode(); } template -void MemoryTracker::TrackField(const char* name, - const std::basic_string& value) { - TrackFieldWithSize(name, value.size() * sizeof(T)); +void MemoryTracker::TrackField(const char* edge_name, + const std::basic_string& value, + const char* node_name) { + TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string"); } template -void MemoryTracker::TrackField(const char* name, - const v8::Persistent& value) { - TrackField(name, value.Get(isolate_)); +void MemoryTracker::TrackField(const char* edge_name, + const v8::Persistent& value, + const char* node_name) { + TrackField(edge_name, value.Get(isolate_)); } template -void MemoryTracker::TrackField(const char* name, const v8::Local& value) { - if (!value.IsEmpty()) - graph_->AddEdge(CurrentNode(), graph_->V8Node(value)); +void MemoryTracker::TrackField(const char* edge_name, + const v8::Local& value, + const char* node_name) { + if (!value.IsEmpty()) { + // For ABI compatibility, we did not backport the virtual function + // AddEdge() with a name as last argument back to v10.x. + graph_->AddEdge(CurrentNode(), graph_->V8Node(value)/*, edge_name */); + } } template +void MemoryTracker::TrackField(const char* edge_name, + const MallocedBuffer& value, + const char* node_name) { + TrackFieldWithSize(edge_name, value.size, "MallocedBuffer"); +} + +void MemoryTracker::TrackField(const char* name, + const uv_buf_t& value, + const char* node_name) { + TrackFieldWithSize(name, value.len, "uv_buf_t"); +} + void MemoryTracker::TrackField(const char* name, - const MallocedBuffer& value) { - TrackFieldWithSize(name, value.size); + const uv_timer_t& value, + const char* node_name) { + TrackFieldWithSize(name, sizeof(value), "uv_timer_t"); } -void MemoryTracker::TrackField(const char* name, const uv_buf_t& value) { - TrackFieldWithSize(name, value.len); +void MemoryTracker::TrackField(const char* name, + const uv_async_t& value, + const char* node_name) { + TrackFieldWithSize(name, sizeof(value), "uv_async_t"); } template void MemoryTracker::TrackField(const char* name, - const AliasedBuffer& value) { - TrackField(name, value.GetJSArray()); + const AliasedBuffer& value, + const char* node_name) { + TrackField(name, value.GetJSArray(), "AliasedBuffer"); } -void MemoryTracker::Track(const MemoryRetainer* value, const char* name) { +void MemoryTracker::Track(const MemoryRetainer* retainer, + const char* edge_name) { v8::HandleScope handle_scope(isolate_); - MemoryRetainerNode* n = PushNode(name, value); - value->MemoryInfo(this); + auto it = seen_.find(retainer); + if (it != seen_.end()) { + if (CurrentNode() != nullptr) { + // For ABI compatibility, we did not backport the virtual function + // AddEdge() with a name as last argument back to v10.x. + graph_->AddEdge(CurrentNode(), it->second/*, edge_name */); + } + return; // It has already been tracked, no need to call MemoryInfo again + } + MemoryRetainerNode* n = PushNode(retainer, edge_name); + retainer->MemoryInfo(this); CHECK_EQ(CurrentNode(), n); CHECK_NE(n->size_, 0); PopNode(); @@ -162,27 +253,56 @@ MemoryRetainerNode* MemoryTracker::CurrentNode() const { return node_stack_.top(); } -MemoryRetainerNode* MemoryTracker::AddNode( - const char* name, const MemoryRetainer* retainer) { - MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer, name); - graph_->AddNode(std::unique_ptr(n)); - if (retainer != nullptr) - seen_[retainer] = n; +MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer, + const char* edge_name) { + auto it = seen_.find(retainer); + if (it != seen_.end()) { + return it->second; + } - if (CurrentNode() != nullptr) - graph_->AddEdge(CurrentNode(), n); + MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer); + graph_->AddNode(std::unique_ptr(n)); + seen_[retainer] = n; + if (CurrentNode() != nullptr) { + // For ABI compatibility, we did not backport the virtual function + // AddEdge() with a name as last argument back to v10.x. + graph_->AddEdge(CurrentNode(), n/*, edge_name */); + } if (n->JSWrapperNode() != nullptr) { - graph_->AddEdge(n, n->JSWrapperNode()); - graph_->AddEdge(n->JSWrapperNode(), n); + graph_->AddEdge(n, n->JSWrapperNode()/*, "wrapped" */); + graph_->AddEdge(n->JSWrapperNode(), n/*, "wrapper" */); + } + + return n; +} + +MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name, + size_t size, + const char* edge_name) { + MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size); + graph_->AddNode(std::unique_ptr(n)); + + if (CurrentNode() != nullptr) { + // For ABI compatibility, we did not backport the virtual function + // AddEdge() with a name as last argument back to v10.x. + graph_->AddEdge(CurrentNode(), n/*, edge_name*/); } return n; } -MemoryRetainerNode* MemoryTracker::PushNode( - const char* name, const MemoryRetainer* retainer) { - MemoryRetainerNode* n = AddNode(name, retainer); +MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer, + const char* edge_name) { + MemoryRetainerNode* n = AddNode(retainer, edge_name); + node_stack_.push(n); + return n; +} + +MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name, + size_t size, + const char* edge_name) { + MemoryRetainerNode* n = AddNode(node_name, size, edge_name); node_stack_.push(n); return n; } diff --git a/src/memory_tracker.h b/src/memory_tracker.h index d0f9e0dcad8f1e..17992792128809 100644 --- a/src/memory_tracker.h +++ b/src/memory_tracker.h @@ -14,6 +14,19 @@ namespace node { +// Set the node name of a MemoryRetainer to klass +#define SET_MEMORY_INFO_NAME(Klass) \ + inline std::string MemoryInfoName() const override { return #Klass; } + +// Set the self size of a MemoryRetainer to the stack-allocated size of a +// certain class +#define SET_SELF_SIZE(Klass) \ + inline size_t SelfSize() const override { return sizeof(Klass); } + +// Used when there is no additional fields to track +#define SET_NO_MEMORY_INFO() \ + inline void MemoryInfo(node::MemoryTracker* tracker) const override {} + class MemoryTracker; class MemoryRetainerNode; @@ -21,61 +34,169 @@ namespace crypto { class NodeBIO; } +/* Example: + * + * class ExampleRetainer : public MemoryRetainer { + * public: + * // Or use SET_NO_MEMORY_INFO() when there is no additional fields + * // to track. + * void MemoryInfo(MemoryTracker* tracker) const override { + * // Node name and size comes from the MemoryInfoName and SelfSize of + * // AnotherRetainerClass + * tracker->TrackField("another_retainer", another_retainer_); + * // Specify node name and size explicitly + * tracker->TrackFieldWithSize("internal_member", + * internal_member_.size(), + * "InternalClass"); + * // Node name falls back to the edge name, + * // elements in the container appear as grandchildren nodes + * tracker->TrackField("vector", vector_); + * // Node name and size come from the JS object + * tracker->TrackField("target", target_); + * } + * + * // Or use SET_MEMORY_INFO_NAME(ExampleRetainer) + * std::string MemoryInfoName() const override { + * return "ExampleRetainer"; + * } + * + * // Or use SET_SELF_SIZE(ExampleRetainer) + * size_t SelfSize() const override { + * return sizeof(ExampleRetainer); + * } + * + * // Note: no need to implement these two methods when implementing + * // a BaseObject or an AsyncWrap class + * bool IsRootNode() const override { return !wrapped_.IsWeak(); } + * v8::Local WrappedObject() const override { + * return node::PersistentToLocal(wrapped_); + * } + * private: + * AnotherRetainerClass another_retainer_; + * InternalClass internal_member_; + * std::vector vector_; + * node::Persistent target_; + * + * node::Persistent wrapped_; + * } + * + * This creates the following graph: + * Node / ExampleRetainer + * |> another_retainer :: Node / AnotherRetainerClass + * |> internal_member :: Node / InternalClass + * |> vector :: Node / vector (elements will be grandchildren) + * |> [1] :: Node / uv_async_t (uv_async_t has predefined names) + * |> [2] :: Node / uv_async_t + * |> ... + * |> target :: TargetClass (JS class name of the target object) + * |> wrapped :: WrappedClass (JS class name of the wrapped object) + * |> wrapper :: Node / ExampleRetainer (back reference) + */ class MemoryRetainer { public: virtual ~MemoryRetainer() {} - // Subclasses should implement this to provide information for heap snapshots. + // Subclasses should implement these methods to provide information + // for the V8 heap snapshot generator. + // The MemoryInfo() method is assumed to be called within a context + // where all the edges start from the node of the current retainer, + // and point to the nodes as specified by tracker->Track* calls. virtual void MemoryInfo(MemoryTracker* tracker) const = 0; + virtual std::string MemoryInfoName() const = 0; + virtual size_t SelfSize() const = 0; virtual v8::Local WrappedObject() const { return v8::Local(); } virtual bool IsRootNode() const { return false; } - - virtual std::string MemoryInfoName() const { return std::string(); } }; class MemoryTracker { public: + // Used to specify node name and size explicitly + inline void TrackFieldWithSize(const char* edge_name, + size_t size, + const char* node_name = nullptr); + // Shortcut to extract the underlying object out of the smart pointer template - inline void TrackThis(const T* obj); - - inline void TrackFieldWithSize(const char* name, size_t size); - - inline void TrackField(const char* name, const MemoryRetainer& value); - inline void TrackField(const char* name, const MemoryRetainer* value); - template - inline void TrackField(const char* name, const std::unique_ptr& value); + inline void TrackField(const char* edge_name, + const std::unique_ptr& value, + const char* node_name = nullptr); + + // For containers, the elements will be graphed as grandchildren nodes + // if the container is not empty. + // By default, we assume the parent count the stack size of the container + // into its SelfSize so that will be subtracted from the parent size when we + // spin off a new node for the container. + // TODO(joyeecheung): use RTTI to retrieve the class name at runtime? template - inline void TrackField(const char* name, const T& value); + inline void TrackField(const char* edge_name, + const T& value, + const char* node_name = nullptr, + const char* element_name = nullptr, + bool subtract_from_self = true); template - inline void TrackField(const char* name, const std::queue& value); - template - inline void TrackField(const char* name, const std::basic_string& value); - template ::is_specialized, bool>::type, - typename dummy = bool> - inline void TrackField(const char* name, const T& value); + inline void TrackField(const char* edge_name, + const std::queue& value, + const char* node_name = nullptr, + const char* element_name = nullptr); template - inline void TrackField(const char* name, const std::pair& value); + inline void TrackField(const char* edge_name, + const std::pair& value, + const char* node_name = nullptr); + + // For the following types, node_name will be ignored and predefined names + // will be used instead. They are only in the signature for template + // expansion. + inline void TrackField(const char* edge_name, + const MemoryRetainer& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const MemoryRetainer* value, + const char* node_name = nullptr); + template + inline void TrackField(const char* edge_name, + const std::basic_string& value, + const char* node_name = nullptr); + template ::is_specialized, bool>::type, + typename dummy = bool> + inline void TrackField(const char* edge_name, + const T& value, + const char* node_name = nullptr); template - inline void TrackField(const char* name, - const v8::Persistent& value); + inline void TrackField(const char* edge_name, + const v8::Persistent& value, + const char* node_name = nullptr); template - inline void TrackField(const char* name, const v8::Local& value); + inline void TrackField(const char* edge_name, + const v8::Local& value, + const char* node_name = nullptr); template - inline void TrackField(const char* name, const MallocedBuffer& value); - inline void TrackField(const char* name, const uv_buf_t& value); + inline void TrackField(const char* edge_name, + const MallocedBuffer& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const uv_buf_t& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const uv_timer_t& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const uv_async_t& value, + const char* node_name = nullptr); template - inline void TrackField(const char* name, - const AliasedBuffer& value); + inline void TrackField(const char* edge_name, + const AliasedBuffer& value, + const char* node_name = nullptr); - inline void Track(const MemoryRetainer* value, const char* name = nullptr); + // Put a memory container into the graph, create an edge from + // the current node if there is one on the stack. + inline void Track(const MemoryRetainer* retainer, + const char* edge_name = nullptr); - inline void set_track_only_self(bool value) { track_only_self_ = value; } inline v8::EmbedderGraph* graph() { return graph_; } inline v8::Isolate* isolate() { return isolate_; } @@ -88,13 +209,18 @@ class MemoryTracker { NodeMap; inline MemoryRetainerNode* CurrentNode() const; - inline MemoryRetainerNode* AddNode(const char* name, - const MemoryRetainer* retainer = nullptr); - inline MemoryRetainerNode* PushNode(const char* name, - const MemoryRetainer* retainer = nullptr); + inline MemoryRetainerNode* AddNode(const MemoryRetainer* retainer, + const char* edge_name = nullptr); + inline MemoryRetainerNode* PushNode(const MemoryRetainer* retainer, + const char* edge_name = nullptr); + inline MemoryRetainerNode* AddNode(const char* node_name, + size_t size, + const char* edge_name = nullptr); + inline MemoryRetainerNode* PushNode(const char* node_name, + size_t size, + const char* edge_name = nullptr); inline void PopNode(); - bool track_only_self_ = false; v8::Isolate* isolate_; v8::EmbedderGraph* graph_; std::stack node_stack_; diff --git a/src/module_wrap.h b/src/module_wrap.h index 3e19b6c9eb3ebe..d6593c48135d18 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -34,12 +34,12 @@ class ModuleWrap : public BaseObject { v8::Local meta); void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("url", url_); tracker->TrackField("resolve_cache", resolve_cache_); } - ADD_MEMORY_INFO_NAME(ModuleWrap) + SET_MEMORY_INFO_NAME(ModuleWrap) + SET_SELF_SIZE(ModuleWrap) private: ModuleWrap(Environment* env, diff --git a/src/node_contextify.cc b/src/node_contextify.cc index e850b94e71cba9..f46193d77510b7 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -589,13 +589,11 @@ class ContextifyScript : public BaseObject { private: Persistent script_; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(ContextifyScript) - public: + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(ContextifyScript) + SET_SELF_SIZE(ContextifyScript) + static void Init(Environment* env, Local target) { HandleScope scope(env->isolate()); Local class_name = diff --git a/src/node_crypto.h b/src/node_crypto.h index 714afd0d3bb868..acb61885f28213 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -107,11 +107,10 @@ class SecureContext : public BaseObject { static void Initialize(Environment* env, v8::Local target); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(SecureContext) + // TODO(joyeecheung): track the memory used by OpenSSL types + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SecureContext) + SET_SELF_SIZE(SecureContext) SSLCtxPointer ctx_; X509Pointer cert_; @@ -347,11 +346,10 @@ class CipherBase : public BaseObject { public: static void Initialize(Environment* env, v8::Local target); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(CipherBase) + // TODO(joyeecheung): track the memory used by OpenSSL types + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(CipherBase) + SET_SELF_SIZE(CipherBase) protected: enum CipherKind { @@ -436,11 +434,10 @@ class Hmac : public BaseObject { public: static void Initialize(Environment* env, v8::Local target); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(Hmac) + // TODO(joyeecheung): track the memory used by OpenSSL types + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(Hmac) + SET_SELF_SIZE(Hmac) protected: void HmacInit(const char* hash_type, const char* key, int key_len); @@ -465,11 +462,10 @@ class Hash : public BaseObject { public: static void Initialize(Environment* env, v8::Local target); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(Hash) + // TODO(joyeecheung): track the memory used by OpenSSL types + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(Hash) + SET_SELF_SIZE(Hash) bool HashInit(const char* hash_type); bool HashUpdate(const char* data, int len); @@ -510,11 +506,10 @@ class SignBase : public BaseObject { Error Init(const char* sign_type); Error Update(const char* data, int len); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(SignBase) + // TODO(joyeecheung): track the memory used by OpenSSL types + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SignBase) + SET_SELF_SIZE(SignBase) protected: void CheckThrow(Error error); @@ -628,11 +623,10 @@ class DiffieHellman : public BaseObject { MakeWeak(); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(DiffieHellman) + // TODO(joyeecheung): track the memory used by OpenSSL types + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(DiffieHellman) + SET_SELF_SIZE(DiffieHellman) private: static void GetField(const v8::FunctionCallbackInfo& args, @@ -659,11 +653,10 @@ class ECDH : public BaseObject { char* data, size_t len); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(ECDH) + // TODO(joyeecheung): track the memory used by OpenSSL types + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(ECDH) + SET_SELF_SIZE(ECDH) protected: ECDH(Environment* env, v8::Local wrap, ECKeyPointer&& key) diff --git a/src/node_crypto_bio.h b/src/node_crypto_bio.h index 0c61f19d0189d2..1c62fbbd359405 100644 --- a/src/node_crypto_bio.h +++ b/src/node_crypto_bio.h @@ -108,11 +108,11 @@ class NodeBIO : public MemoryRetainer { static NodeBIO* FromBIO(BIO* bio); void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - tracker->TrackFieldWithSize("buffer", length_); + tracker->TrackFieldWithSize("buffer", length_, "NodeBIO::Buffer"); } - ADD_MEMORY_INFO_NAME(NodeBIO) + SET_MEMORY_INFO_NAME(NodeBIO) + SET_SELF_SIZE(NodeBIO) private: static int New(BIO* bio); diff --git a/src/node_file.cc b/src/node_file.cc index 3d40c8e9bf4dc1..6a2f2107d6df27 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -277,6 +277,10 @@ void FileHandle::AfterClose() { EmitRead(UV_EOF); } +void FileHandleReadWrap::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("buffer", buffer_); + tracker->TrackField("file_handle", this->file_handle_); +} FileHandleReadWrap::FileHandleReadWrap(FileHandle* handle, Local obj) : ReqWrap(handle->env(), obj, AsyncWrap::PROVIDER_FSREQWRAP), diff --git a/src/node_file.h b/src/node_file.h index 457e72c33a7321..cbbb8b037d8e0a 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -53,10 +53,12 @@ class FSContinuationData : public MemoryRetainer { } void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("paths", paths); } + SET_MEMORY_INFO_NAME(FSContinuationData) + SET_SELF_SIZE(FSContinuationData) + private: uv_fs_cb done_cb; }; @@ -136,11 +138,11 @@ class FSReqWrap : public FSReqBase { void SetReturnValue(const FunctionCallbackInfo& args) override; void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("continuation_data", continuation_data); } - ADD_MEMORY_INFO_NAME(FSReqWrap) + SET_MEMORY_INFO_NAME(FSReqWrap) + SET_SELF_SIZE(FSReqWrap) private: DISALLOW_COPY_AND_ASSIGN(FSReqWrap); @@ -201,12 +203,12 @@ class FSReqPromise : public FSReqBase { } void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("stats_field_array", stats_field_array_); tracker->TrackField("continuation_data", continuation_data); } - ADD_MEMORY_INFO_NAME(FSReqPromise) + SET_MEMORY_INFO_NAME(FSReqPromise) + SET_SELF_SIZE(FSReqPromise) private: bool finished_ = false; @@ -242,12 +244,9 @@ class FileHandleReadWrap : public ReqWrap { return static_cast(ReqWrap::from_req(req)); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - tracker->TrackField("buffer", buffer_); - } - - ADD_MEMORY_INFO_NAME(FileHandleReadWrap) + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(FileHandleReadWrap) + SET_SELF_SIZE(FileHandleReadWrap) private: FileHandle* file_handle_; @@ -296,11 +295,11 @@ class FileHandle : public AsyncWrap, public StreamBase { } void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("current_read", current_read_); } - ADD_MEMORY_INFO_NAME(FileHandle) + SET_MEMORY_INFO_NAME(FileHandle) + SET_SELF_SIZE(FileHandle) private: // Synchronous close that emits a warning @@ -329,12 +328,12 @@ class FileHandle : public AsyncWrap, public StreamBase { FileHandle* file_handle(); void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("promise", promise_); tracker->TrackField("ref", ref_); } - ADD_MEMORY_INFO_NAME(CloseReq) + SET_MEMORY_INFO_NAME(CloseReq) + SET_SELF_SIZE(CloseReq) void Resolve(); diff --git a/src/node_http2.cc b/src/node_http2.cc index fcc83f4acc53ef..bde0ef79e95dc2 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -2897,7 +2897,6 @@ void Http2Session::Http2Ping::Done(bool ack, const uint8_t* payload) { void nghttp2_stream_write::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackThis(this); if (req_wrap != nullptr) tracker->TrackField("req_wrap", req_wrap->GetAsyncWrap()); tracker->TrackField("buf", buf); @@ -2905,7 +2904,6 @@ void nghttp2_stream_write::MemoryInfo(MemoryTracker* tracker) const { void nghttp2_header::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackThis(this); tracker->TrackFieldWithSize("name", nghttp2_rcbuf_get_buf(name).len); tracker->TrackFieldWithSize("value", nghttp2_rcbuf_get_buf(value).len); } diff --git a/src/node_http2.h b/src/node_http2.h index 7fa230979a87cb..2ab452bf02aaa8 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -92,6 +92,8 @@ struct nghttp2_stream_write : public MemoryRetainer { req_wrap(req), buf(buf_) {} void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(nghttp2_stream_write) + SET_SELF_SIZE(nghttp2_stream_write) }; struct nghttp2_header : public MemoryRetainer { @@ -100,6 +102,8 @@ struct nghttp2_header : public MemoryRetainer { uint8_t flags = 0; void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(nghttp2_header) + SET_SELF_SIZE(nghttp2_header) }; @@ -570,12 +574,12 @@ class Http2Stream : public AsyncWrap, uv_stream_t* send_handle) override; void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("current_headers", current_headers_); tracker->TrackField("queue", queue_); } - ADD_MEMORY_INFO_NAME(Http2Stream) + SET_MEMORY_INFO_NAME(Http2Stream) + SET_SELF_SIZE(Http2Stream) std::string diagnostic_name() const override; @@ -755,7 +759,6 @@ class Http2Session : public AsyncWrap, public StreamListener { ssize_t Write(const uv_buf_t* bufs, size_t nbufs); void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("streams", streams_); tracker->TrackField("outstanding_pings", outstanding_pings_); tracker->TrackField("outstanding_settings", outstanding_settings_); @@ -765,7 +768,8 @@ class Http2Session : public AsyncWrap, public StreamListener { pending_rst_streams_.size() * sizeof(int32_t)); } - ADD_MEMORY_INFO_NAME(Http2Session) + SET_MEMORY_INFO_NAME(Http2Session) + SET_SELF_SIZE(Http2Session) std::string diagnostic_name() const override; @@ -1085,11 +1089,11 @@ class Http2Session::Http2Ping : public AsyncWrap { explicit Http2Ping(Http2Session* session); void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("session", session_); } - ADD_MEMORY_INFO_NAME(Http2Ping) + SET_MEMORY_INFO_NAME(Http2Ping) + SET_SELF_SIZE(Http2Ping) void Send(uint8_t* payload); void Done(bool ack, const uint8_t* payload = nullptr); @@ -1110,11 +1114,11 @@ class Http2Session::Http2Settings : public AsyncWrap { explicit Http2Settings(Http2Session* session); void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("session", session_); } - ADD_MEMORY_INFO_NAME(Http2Settings) + SET_MEMORY_INFO_NAME(Http2Settings) + SET_SELF_SIZE(Http2Settings) void Send(); void Done(bool ack); diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index eb7a69ce752f37..0907f0a4329e88 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -157,11 +157,11 @@ class Parser : public AsyncWrap, public StreamListener { void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("current_buffer", current_buffer_); } - ADD_MEMORY_INFO_NAME(Parser) + SET_MEMORY_INFO_NAME(Parser) + SET_SELF_SIZE(Parser) int on_message_begin() { num_fields_ = num_values_ = 0; diff --git a/src/node_i18n.cc b/src/node_i18n.cc index e87f8f59550e19..5966e3ff678e34 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -251,11 +251,9 @@ class ConverterObject : public BaseObject, Converter { args.GetReturnValue().Set(status); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(ConverterObject) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(ConverterObject) + SET_SELF_SIZE(ConverterObject) protected: ConverterObject(Environment* env, diff --git a/src/node_messaging.cc b/src/node_messaging.cc index 0a79d6f9d3d36a..a8b95401fdf06c 100644 --- a/src/node_messaging.cc +++ b/src/node_messaging.cc @@ -326,7 +326,6 @@ Maybe Message::Serialize(Environment* env, } void Message::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackThis(this); tracker->TrackField("array_buffer_contents", array_buffer_contents_); tracker->TrackFieldWithSize("shared_array_buffers", shared_array_buffers_.size() * sizeof(shared_array_buffers_[0])); @@ -342,7 +341,6 @@ MessagePortData::~MessagePortData() { void MessagePortData::MemoryInfo(MemoryTracker* tracker) const { Mutex::ScopedLock lock(mutex_); - tracker->TrackThis(this); tracker->TrackField("incoming_messages", incoming_messages_); } diff --git a/src/node_messaging.h b/src/node_messaging.h index b7fd392ccc6fab..e4674885d2b89e 100644 --- a/src/node_messaging.h +++ b/src/node_messaging.h @@ -57,7 +57,8 @@ class Message : public MemoryRetainer { void MemoryInfo(MemoryTracker* tracker) const override; - ADD_MEMORY_INFO_NAME(Message) + SET_MEMORY_INFO_NAME(Message) + SET_SELF_SIZE(Message) private: MallocedBuffer main_message_buf_; @@ -100,7 +101,8 @@ class MessagePortData : public MemoryRetainer { void MemoryInfo(MemoryTracker* tracker) const override; - ADD_MEMORY_INFO_NAME(MessagePortData) + SET_MEMORY_INFO_NAME(MessagePortData) + SET_SELF_SIZE(MessagePortData) private: // After disentangling this message port, the owner handle (if any) @@ -187,11 +189,11 @@ class MessagePort : public HandleWrap { inline bool IsDetached() const; void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("data", data_); } - ADD_MEMORY_INFO_NAME(MessagePort) + SET_MEMORY_INFO_NAME(MessagePort) + SET_SELF_SIZE(MessagePort) private: void OnClose() override; diff --git a/src/node_serdes.cc b/src/node_serdes.cc index 5de0ddd81909b0..a6f91d56c2b2bb 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -53,11 +53,9 @@ class SerializerContext : public BaseObject, static void WriteDouble(const FunctionCallbackInfo& args); static void WriteRawBytes(const FunctionCallbackInfo& args); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(SerializerContext) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SerializerContext) + SET_SELF_SIZE(SerializerContext) private: ValueSerializer serializer_; @@ -84,11 +82,9 @@ class DeserializerContext : public BaseObject, static void ReadDouble(const FunctionCallbackInfo& args); static void ReadRawBytes(const FunctionCallbackInfo& args); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(DeserializerContext) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(DeserializerContext) + SET_SELF_SIZE(DeserializerContext) private: const uint8_t* data_; diff --git a/src/node_stat_watcher.h b/src/node_stat_watcher.h index 33c90ad3cde737..3d819b45787e98 100644 --- a/src/node_stat_watcher.h +++ b/src/node_stat_watcher.h @@ -44,11 +44,9 @@ class StatWatcher : public HandleWrap { static void New(const v8::FunctionCallbackInfo& args); static void Start(const v8::FunctionCallbackInfo& args); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(StatWatcher) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(StatWatcher) + SET_SELF_SIZE(StatWatcher) private: static void Callback(uv_fs_poll_t* handle, diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index 3c18b0d45b11c9..f06648b24492fd 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -28,11 +28,11 @@ class NodeCategorySet : public BaseObject { const std::set& GetCategories() const { return categories_; } void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("categories", categories_); } - ADD_MEMORY_INFO_NAME(NodeCategorySet) + SET_MEMORY_INFO_NAME(NodeCategorySet) + SET_SELF_SIZE(NodeCategorySet) private: NodeCategorySet(Environment* env, diff --git a/src/node_worker.h b/src/node_worker.h index 8491ad221b6dde..cbd4a861570b27 100644 --- a/src/node_worker.h +++ b/src/node_worker.h @@ -26,15 +26,15 @@ class Worker : public AsyncWrap { void JoinThread(); void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - tracker->TrackFieldWithSize("isolate_data", sizeof(IsolateData)); - tracker->TrackFieldWithSize("env", sizeof(Environment)); - tracker->TrackFieldWithSize("thread_exit_async", sizeof(uv_async_t)); + tracker->TrackFieldWithSize( + "isolate_data", sizeof(IsolateData), "IsolateData"); + tracker->TrackFieldWithSize("env", sizeof(Environment), "Environment"); + tracker->TrackField("thread_exit_async", *thread_exit_async_); tracker->TrackField("parent_port", parent_port_); } - - ADD_MEMORY_INFO_NAME(Worker) + SET_MEMORY_INFO_NAME(Worker) + SET_SELF_SIZE(Worker) bool is_stopped() const; diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 5def50d3b9dceb..6e99f68108730f 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -661,14 +661,13 @@ class ZCtx : public AsyncWrap, public ThreadPoolWork { } void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); tracker->TrackField("dictionary", dictionary_); - tracker->TrackFieldWithSize("zlib memory", - zlib_memory_ + unreported_allocations_); + tracker->TrackFieldWithSize("zlib_memory", + zlib_memory_ + unreported_allocations_); } - - ADD_MEMORY_INFO_NAME(ZCtx) + SET_MEMORY_INFO_NAME(ZCtx) + SET_SELF_SIZE(ZCtx) private: void Ref() { diff --git a/src/pipe_wrap.h b/src/pipe_wrap.h index 7faf5145abdcfe..05a5ba6e113a18 100644 --- a/src/pipe_wrap.h +++ b/src/pipe_wrap.h @@ -45,11 +45,9 @@ class PipeWrap : public ConnectionWrap { v8::Local unused, v8::Local context); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(PipeWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(PipeWrap) + SET_SELF_SIZE(PipeWrap) private: PipeWrap(Environment* env, diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 03628c856b68b4..cd443b34aa972c 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -66,11 +66,9 @@ class ProcessWrap : public HandleWrap { constructor->GetFunction(context).ToLocalChecked()); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(ProcessWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(ProcessWrap) + SET_SELF_SIZE(ProcessWrap) private: static void New(const FunctionCallbackInfo& args) { diff --git a/src/sharedarraybuffer_metadata.cc b/src/sharedarraybuffer_metadata.cc index 3d5b96051ead6e..b20d9f46a44d5e 100644 --- a/src/sharedarraybuffer_metadata.cc +++ b/src/sharedarraybuffer_metadata.cc @@ -47,11 +47,9 @@ class SABLifetimePartner : public BaseObject { MakeWeak(); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(SABLifetimePartner) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SABLifetimePartner) + SET_SELF_SIZE(SABLifetimePartner) SharedArrayBufferMetadataReference reference; }; diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index d7f21db8b3e8dd..b390066140a739 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -59,11 +59,9 @@ class SignalWrap : public HandleWrap { constructor->GetFunction(env->context()).ToLocalChecked()); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(SignalWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SignalWrap) + SET_SELF_SIZE(SignalWrap) private: static void New(const FunctionCallbackInfo& args) { diff --git a/src/stream_base.h b/src/stream_base.h index 05c2a9623625d6..d8e6df960f4f54 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -347,11 +347,9 @@ class SimpleShutdownWrap : public ShutdownWrap, public OtherBase { AsyncWrap* GetAsyncWrap() override { return this; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(SimpleShutdownWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SimpleShutdownWrap) + SET_SELF_SIZE(SimpleShutdownWrap) }; template @@ -362,13 +360,9 @@ class SimpleWriteWrap : public WriteWrap, public OtherBase { AsyncWrap* GetAsyncWrap() override { return this; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - tracker->TrackFieldWithSize("storage", StorageSize()); - } - - - ADD_MEMORY_INFO_NAME(SimpleWriteWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SimpleWriteWrap) + SET_SELF_SIZE(SimpleWriteWrap) }; } // namespace node diff --git a/src/stream_pipe.h b/src/stream_pipe.h index c76afac41689a6..51a33b7ef69776 100644 --- a/src/stream_pipe.h +++ b/src/stream_pipe.h @@ -18,11 +18,9 @@ class StreamPipe : public AsyncWrap { static void Start(const v8::FunctionCallbackInfo& args); static void Unpipe(const v8::FunctionCallbackInfo& args); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(StreamPipe) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(StreamPipe) + SET_SELF_SIZE(StreamPipe) private: inline StreamBase* source(); diff --git a/src/tcp_wrap.h b/src/tcp_wrap.h index 829c1b22bf3aec..90c81bcae6fd6f 100644 --- a/src/tcp_wrap.h +++ b/src/tcp_wrap.h @@ -44,10 +44,8 @@ class TCPWrap : public ConnectionWrap { v8::Local unused, v8::Local context); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - + SET_NO_MEMORY_INFO() + SET_SELF_SIZE(TCPWrap) std::string MemoryInfoName() const override { switch (provider_type()) { case ProviderType::PROVIDER_TCPWRAP: diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 436e3def174328..4e5a9b4e83ea86 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -66,9 +66,9 @@ class TimerWrap : public HandleWrap { ->GetFunction(env->context()).ToLocalChecked()).FromJust(); } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(TimerWrap) + SET_SELF_SIZE(TimerWrap) private: static void SetupTimers(const FunctionCallbackInfo& args) { diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index b392ab6016a5ca..6577ffd3ec2080 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -861,7 +861,6 @@ void TLSWrap::GetWriteQueueSize(const FunctionCallbackInfo& info) { void TLSWrap::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackThis(this); tracker->TrackField("error", error_); tracker->TrackField("pending_cleartext_input", pending_cleartext_input_); if (enc_in_ != nullptr) diff --git a/src/tls_wrap.h b/src/tls_wrap.h index aea8568b11b51c..0e265764822f29 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -78,7 +78,8 @@ class TLSWrap : public AsyncWrap, void MemoryInfo(MemoryTracker* tracker) const override; - ADD_MEMORY_INFO_NAME(TLSWrap) + SET_MEMORY_INFO_NAME(TLSWrap) + SET_SELF_SIZE(TLSWrap) protected: inline StreamBase* underlying_stream() { diff --git a/src/tty_wrap.h b/src/tty_wrap.h index 45357cfa4637cf..ad5f364134e3e2 100644 --- a/src/tty_wrap.h +++ b/src/tty_wrap.h @@ -38,11 +38,9 @@ class TTYWrap : public LibuvStreamWrap { uv_tty_t* UVHandle(); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(TTYWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(TTYWrap) + SET_SELF_SIZE(TTYWrap) private: TTYWrap(Environment* env, diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index d1802bc4809726..28fa51f1c639d2 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -56,11 +56,9 @@ class SendWrap : public ReqWrap { inline bool have_callback() const; size_t msg_size; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(SendWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(SendWrap) + SET_SELF_SIZE(SendWrap) private: const bool have_callback_; diff --git a/src/udp_wrap.h b/src/udp_wrap.h index ca048f5aef98af..c1261fdb3945e4 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -64,11 +64,9 @@ class UDPWrap: public HandleWrap { SocketType type); uv_udp_t* UVHandle(); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } - - ADD_MEMORY_INFO_NAME(UDPWrap) + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(UDPWrap) + SET_SELF_SIZE(UDPWrap) private: typedef uv_udp_t HandleType; diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc index 743b4ccc88ae0e..bb56fbc2047f43 100644 --- a/test/cctest/test_node_postmortem_metadata.cc +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -34,9 +34,9 @@ class DebugSymbolsTest : public EnvironmentTestFixture {}; class TestHandleWrap : public node::HandleWrap { public: - void MemoryInfo(node::MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(TestHandleWrap) + SET_SELF_SIZE(TestHandleWrap) TestHandleWrap(node::Environment* env, v8::Local object, @@ -50,9 +50,9 @@ class TestHandleWrap : public node::HandleWrap { class TestReqWrap : public node::ReqWrap { public: - void MemoryInfo(node::MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(TestReqWrap) + SET_SELF_SIZE(TestReqWrap) TestReqWrap(node::Environment* env, v8::Local object) : node::ReqWrap(env, @@ -76,9 +76,9 @@ class DummyBaseObject : public node::BaseObject { DummyBaseObject(node::Environment* env, v8::Local obj) : BaseObject(env, obj) {} - void MemoryInfo(node::MemoryTracker* tracker) const override { - tracker->TrackThis(this); - } + SET_NO_MEMORY_INFO() + SET_MEMORY_INFO_NAME(DummyBaseObject) + SET_SELF_SIZE(DummyBaseObject) }; TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { diff --git a/test/common/heap.js b/test/common/heap.js index 382d1d3642c959..2fe99bf67eb643 100644 --- a/test/common/heap.js +++ b/test/common/heap.js @@ -12,75 +12,110 @@ try { } const { createJSHeapDump, buildEmbedderGraph } = internalTestHeap; +function inspectNode(snapshot) { + return util.inspect(snapshot, { depth: 4 }); +} + +function isEdge(edge, { node_name, edge_name }) { + // For ABI compatibility, we did not backport the virtual function + // AddEdge() with a name as last argument back to v10.x, so edge_name. + // is ignored. + // if (edge.name !== edge_name) { + // return false; + // } + // From our internal embedded graph + if (edge.to.value) { + if (edge.to.value.constructor.name !== node_name) { + return false; + } + } else if (edge.to.name !== node_name) { + return false; + } + return true; +} + class State { constructor() { this.snapshot = createJSHeapDump(); this.embedderGraph = buildEmbedderGraph(); } - validateSnapshotNodes(name, expected, { loose = false } = {}) { - const snapshot = this.snapshot.filter( - (node) => node.name === 'Node / ' + name && node.type !== 'string'); - if (loose) - assert(snapshot.length >= expected.length); - else - assert.strictEqual(snapshot.length, expected.length); - for (const expectedNode of expected) { - if (expectedNode.children) { - for (const expectedChild of expectedNode.children) { - const check = typeof expectedChild === 'function' ? - expectedChild : - (node) => [expectedChild.name, 'Node / ' + expectedChild.name] - .includes(node.name); + // Validate the v8 heap snapshot + validateSnapshot(rootName, expected, { loose = false } = {}) { + const rootNodes = this.snapshot.filter( + (node) => node.name === rootName && node.type !== 'string'); + if (loose) { + assert(rootNodes.length >= expected.length, + `Expect to find at least ${expected.length} '${rootName}', ` + + `found ${rootNodes.length}`); + } else { + assert.strictEqual( + rootNodes.length, expected.length, + `Expect to find ${expected.length} '${rootName}', ` + + `found ${rootNodes.length}`); + } - const hasChild = snapshot.some((node) => { - return node.outgoingEdges.map((edge) => edge.toNode).some(check); - }); + for (const expectation of expected) { + if (expectation.children) { + for (const expectedEdge of expectation.children) { + const check = typeof expectedEdge === 'function' ? expectedEdge : + (edge) => (isEdge(edge, expectedEdge)); + const hasChild = rootNodes.some( + (node) => node.outgoingEdges.some(check) + ); // Don't use assert with a custom message here. Otherwise the // inspection in the message is done eagerly and wastes a lot of CPU // time. if (!hasChild) { throw new Error( 'expected to find child ' + - `${util.inspect(expectedChild)} in ${util.inspect(snapshot)}`); + `${util.inspect(expectedEdge)} in ${inspectNode(rootNodes)}`); } } } } + } - const graph = this.embedderGraph.filter((node) => node.name === name); - if (loose) - assert(graph.length >= expected.length); - else - assert.strictEqual(graph.length, expected.length); - for (const expectedNode of expected) { - if (expectedNode.children) { - for (const expectedChild of expectedNode.children) { - const check = (edge) => { - // TODO(joyeecheung): check the edge names - const node = edge.to; - if (typeof expectedChild === 'function') { - return expectedChild(node); - } - return node.name === expectedChild.name || - (node.value && - node.value.constructor && - node.value.constructor.name === expectedChild.name); - }; - + // Validate our internal embedded graph representation + validateGraph(rootName, expected, { loose = false } = {}) { + const rootNodes = this.embedderGraph.filter( + (node) => node.name === rootName + ); + if (loose) { + assert(rootNodes.length >= expected.length, + `Expect to find at least ${expected.length} '${rootName}', ` + + `found ${rootNodes.length}`); + } else { + assert.strictEqual( + rootNodes.length, expected.length, + `Expect to find ${expected.length} '${rootName}', ` + + `found ${rootNodes.length}`); + } + for (const expectation of expected) { + if (expectation.children) { + for (const expectedEdge of expectation.children) { + const check = typeof expectedEdge === 'function' ? expectedEdge : + (edge) => (isEdge(edge, expectedEdge)); // Don't use assert with a custom message here. Otherwise the // inspection in the message is done eagerly and wastes a lot of CPU // time. - const hasChild = graph.some((node) => node.edges.some(check)); + const hasChild = rootNodes.some( + (node) => node.edges.some(check) + ); if (!hasChild) { throw new Error( 'expected to find child ' + - `${util.inspect(expectedChild)} in ${util.inspect(snapshot)}`); + `${util.inspect(expectedEdge)} in ${inspectNode(rootNodes)}`); } } } } } + + validateSnapshotNodes(rootName, expected, { loose = false } = {}) { + this.validateSnapshot(rootName, expected, { loose }); + this.validateGraph(rootName, expected, { loose }); + } } function recordState() { diff --git a/test/parallel/test-heapdump-dns.js b/test/parallel/test-heapdump-dns.js index 00ca54917f5df5..6fe79f7dd4ec5a 100644 --- a/test/parallel/test-heapdump-dns.js +++ b/test/parallel/test-heapdump-dns.js @@ -3,16 +3,16 @@ require('../common'); const { validateSnapshotNodes } = require('../common/heap'); -validateSnapshotNodes('ChannelWrap', []); +validateSnapshotNodes('Node / ChannelWrap', []); const dns = require('dns'); -validateSnapshotNodes('ChannelWrap', [{}]); +validateSnapshotNodes('Node / ChannelWrap', [{}]); dns.resolve('localhost', () => {}); -validateSnapshotNodes('ChannelWrap', [ +validateSnapshotNodes('Node / ChannelWrap', [ { children: [ - { name: 'node_ares_task_list' }, + { node_name: 'Node / node_ares_task_list', edge_name: 'task_list' }, // `Node / ChannelWrap` (C++) -> `ChannelWrap` (JS) - { name: 'ChannelWrap' } + { node_name: 'ChannelWrap', edge_name: 'wrapped' } ] } ]); diff --git a/test/parallel/test-heapdump-fs-promise.js b/test/parallel/test-heapdump-fs-promise.js index 855b135f6ae31b..ca170e13a61770 100644 --- a/test/parallel/test-heapdump-fs-promise.js +++ b/test/parallel/test-heapdump-fs-promise.js @@ -4,13 +4,13 @@ require('../common'); const { validateSnapshotNodes } = require('../common/heap'); const fs = require('fs').promises; -validateSnapshotNodes('FSReqPromise', []); +validateSnapshotNodes('Node / FSReqPromise', []); fs.stat(__filename); -validateSnapshotNodes('FSReqPromise', [ +validateSnapshotNodes('Node / FSReqPromise', [ { children: [ - { name: 'FSReqPromise' }, - { name: 'Float64Array' } // Stat array + { node_name: 'FSReqPromise', edge_name: 'wrapped' }, + { node_name: 'Float64Array', edge_name: 'stats_field_array' } ] } ]); diff --git a/test/parallel/test-heapdump-http2.js b/test/parallel/test-heapdump-http2.js index b503951e65851b..caece96d01cc72 100644 --- a/test/parallel/test-heapdump-http2.js +++ b/test/parallel/test-heapdump-http2.js @@ -8,8 +8,8 @@ const http2 = require('http2'); { const state = recordState(); - state.validateSnapshotNodes('Http2Session', []); - state.validateSnapshotNodes('Http2Stream', []); + state.validateSnapshotNodes('Node / Http2Session', []); + state.validateSnapshotNodes('Node / Http2Stream', []); } const server = http2.createServer(); @@ -24,50 +24,56 @@ server.listen(0, () => { const state = recordState(); // `Node / Http2Stream` (C++) -> Http2Stream (JS) - state.validateSnapshotNodes('Http2Stream', [ + state.validateSnapshotNodes('Node / Http2Stream', [ { children: [ - { name: 'Http2Stream' } + // current_headers and/or queue could be empty + { node_name: 'Http2Stream', edge_name: 'wrapped' } ] }, ], { loose: true }); // `Node / FileHandle` (C++) -> FileHandle (JS) - state.validateSnapshotNodes('FileHandle', [ + state.validateSnapshotNodes('Node / FileHandle', [ { children: [ - { name: 'FileHandle' } + { node_name: 'FileHandle', edge_name: 'wrapped' } + // current_headers could be empty ] } - ]); - state.validateSnapshotNodes('TCPSocketWrap', [ + ], { loose: true }); + state.validateSnapshotNodes('Node / TCPSocketWrap', [ { children: [ - { name: 'TCP' } + { node_name: 'TCP', edge_name: 'wrapped' } ] } ], { loose: true }); - state.validateSnapshotNodes('TCPServerWrap', [ + state.validateSnapshotNodes('Node / TCPServerWrap', [ { children: [ - { name: 'TCP' } + { node_name: 'TCP', edge_name: 'wrapped' } ] } ], { loose: true }); // `Node / StreamPipe` (C++) -> StreamPipe (JS) - state.validateSnapshotNodes('StreamPipe', [ + state.validateSnapshotNodes('Node / StreamPipe', [ { children: [ - { name: 'StreamPipe' } + { node_name: 'StreamPipe', edge_name: 'wrapped' } ] } ]); // `Node / Http2Session` (C++) -> Http2Session (JS) - state.validateSnapshotNodes('Http2Session', [ + state.validateSnapshotNodes('Node / Http2Session', [ { children: [ - { name: 'Http2Session' }, - { name: 'streams' } + { node_name: 'Http2Session', edge_name: 'wrapped' }, + { + node_name: 'Node / streams', edge_name: 'streams' + } + // outstanding_pings, outgoing_buffers, outgoing_storage, + // pending_rst_streams could be empty ] } ], { loose: true }); diff --git a/test/parallel/test-heapdump-inspector.js b/test/parallel/test-heapdump-inspector.js index 08fc6703d87c5c..4ac78edf6b928d 100644 --- a/test/parallel/test-heapdump-inspector.js +++ b/test/parallel/test-heapdump-inspector.js @@ -8,14 +8,15 @@ const { validateSnapshotNodes } = require('../common/heap'); const inspector = require('inspector'); const session = new inspector.Session(); -validateSnapshotNodes('JSBindingsConnection', []); +validateSnapshotNodes('Node / JSBindingsConnection', []); session.connect(); -validateSnapshotNodes('JSBindingsConnection', [ +validateSnapshotNodes('Node / JSBindingsConnection', [ { children: [ - { name: 'session' }, - { name: 'Connection' }, - (node) => node.type === 'closure' || typeof node.value === 'function' + { node_name: 'Node / InspectorSession', edge_name: 'session' }, + { node_name: 'Connection', edge_name: 'wrapped' }, + (edge) => (edge.to.type === undefined || // embedded graph + edge.to.type === 'closure') // snapshot ] } ]); diff --git a/test/parallel/test-heapdump-tls.js b/test/parallel/test-heapdump-tls.js index 90b2d8dc952f56..fee19bf67625b2 100644 --- a/test/parallel/test-heapdump-tls.js +++ b/test/parallel/test-heapdump-tls.js @@ -9,7 +9,7 @@ const { validateSnapshotNodes } = require('../common/heap'); const net = require('net'); const tls = require('tls'); -validateSnapshotNodes('TLSWrap', []); +validateSnapshotNodes('Node / TLSWrap', []); const server = net.createServer(common.mustCall((c) => { c.end(); @@ -21,13 +21,14 @@ const server = net.createServer(common.mustCall((c) => { })); c.write('hello'); - validateSnapshotNodes('TLSWrap', [ + validateSnapshotNodes('Node / TLSWrap', [ { children: [ - { name: 'NodeBIO' }, - { name: 'NodeBIO' }, + { node_name: 'Node / NodeBIO', edge_name: 'enc_out' }, + { node_name: 'Node / NodeBIO', edge_name: 'enc_in' }, // `Node / TLSWrap` (C++) -> `TLSWrap` (JS) - { name: 'TLSWrap' } + { node_name: 'TLSWrap', edge_name: 'wrapped' } + // pending_cleartext_input could be empty ] } ]); diff --git a/test/parallel/test-heapdump-worker.js b/test/parallel/test-heapdump-worker.js index 44a50dd66b5fca..02b989c6b324dd 100644 --- a/test/parallel/test-heapdump-worker.js +++ b/test/parallel/test-heapdump-worker.js @@ -4,22 +4,22 @@ require('../common'); const { validateSnapshotNodes } = require('../common/heap'); const { Worker } = require('worker_threads'); -validateSnapshotNodes('Worker', []); +validateSnapshotNodes('Node / Worker', []); const worker = new Worker('setInterval(() => {}, 100);', { eval: true }); -validateSnapshotNodes('Worker', [ +validateSnapshotNodes('Node / Worker', [ { children: [ - { name: 'thread_exit_async' }, - { name: 'env' }, - { name: 'MessagePort' }, - { name: 'Worker' } + { node_name: 'Node / uv_async_t', edge_name: 'thread_exit_async' }, + { node_name: 'Node / Environment', edge_name: 'env' }, + { node_name: 'Node / MessagePort', edge_name: 'parent_port' }, + { node_name: 'Worker', edge_name: 'wrapped' } ] } ]); -validateSnapshotNodes('MessagePort', [ +validateSnapshotNodes('Node / MessagePort', [ { children: [ - { name: 'MessagePortData' } + { node_name: 'Node / MessagePortData', edge_name: 'data' } ] } ], { loose: true }); diff --git a/test/parallel/test-heapdump-zlib.js b/test/parallel/test-heapdump-zlib.js index 936e3a1a500b2d..f79e345821ea50 100644 --- a/test/parallel/test-heapdump-zlib.js +++ b/test/parallel/test-heapdump-zlib.js @@ -4,14 +4,14 @@ require('../common'); const { validateSnapshotNodes } = require('../common/heap'); const zlib = require('zlib'); -validateSnapshotNodes('ZCtx', []); +validateSnapshotNodes('Node / ZCtx', []); // eslint-disable-next-line no-unused-vars const gunzip = zlib.createGunzip(); -validateSnapshotNodes('ZCtx', [ +validateSnapshotNodes('Node / ZCtx', [ { children: [ - { name: 'Zlib' }, - { name: 'zlib memory' } + { node_name: 'Zlib', edge_name: 'wrapped' }, + { node_name: 'Node / zlib_memory', edge_name: 'zlib_memory' } ] } ]);