From 316ac8c25091c21b84f4a6b194a597281881034c Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Tue, 4 Feb 2025 17:07:11 +0000 Subject: [PATCH] src: add memory retainer traits for external types Add `MemoryRetainerTraits` to reveal external type memory info without forcing them to inherit from `MemoryRetainer`. PR-URL: https://github.com/nodejs/node/pull/56881 Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell Reviewed-By: Anna Henningsen --- src/memory_tracker-inl.h | 21 +++++++++++ src/memory_tracker.h | 34 +++++++++++++++++ src/node_url_pattern.cc | 50 +++++++++++++++++++++---- test/pummel/test-heapdump-urlpattern.js | 25 +++++++++++++ 4 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 test/pummel/test-heapdump-urlpattern.js diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h index c99ff860710..31ed6fad98c 100644 --- a/src/memory_tracker-inl.h +++ b/src/memory_tracker-inl.h @@ -297,6 +297,27 @@ void MemoryTracker::TrackInlineField(const MemoryRetainer* retainer, CurrentNode()->size_ -= retainer->SelfSize(); } +template +inline void MemoryTracker::TraitTrack(const T& retainer, + const char* edge_name) { + MemoryRetainerNode* n = + PushNode(MemoryRetainerTraits::MemoryInfoName(retainer), + MemoryRetainerTraits::SelfSize(retainer), + edge_name); + MemoryRetainerTraits::MemoryInfo(this, retainer); + CHECK_EQ(CurrentNode(), n); + CHECK_NE(n->size_, 0); + PopNode(); +} + +template +inline void MemoryTracker::TraitTrackInline(const T& retainer, + const char* edge_name) { + TraitTrack(retainer, edge_name); + CHECK(CurrentNode()); + CurrentNode()->size_ -= MemoryRetainerTraits::SelfSize(retainer); +} + MemoryRetainerNode* MemoryTracker::CurrentNode() const { if (node_stack_.empty()) return nullptr; return node_stack_.top(); diff --git a/src/memory_tracker.h b/src/memory_tracker.h index cae4e5c7a66..4e0a2fbaaac 100644 --- a/src/memory_tracker.h +++ b/src/memory_tracker.h @@ -138,6 +138,33 @@ class MemoryRetainer { } }; +/** + * MemoryRetainerTraits allows defining a custom memory info for a + * class that can not be modified to implement the MemoryRetainer interface. + * + * Example: + * + * template <> + * struct MemoryRetainerTraits { + * static void MemoryInfo(MemoryTracker* tracker, + * const ExampleRetainer& value) { + * tracker->TrackField("another_retainer", value.another_retainer_); + * } + * static const char* MemoryInfoName(const ExampleRetainer& value) { + * return "ExampleRetainer"; + * } + * static size_t SelfSize(const ExampleRetainer& value) { + * return sizeof(value); + * } + * }; + * + * This creates the following graph: + * Node / ExampleRetainer + * |> another_retainer :: Node / AnotherRetainerClass + */ +template +struct MemoryRetainerTraits {}; + class MemoryTracker { public: // Used to specify node name and size explicitly @@ -254,6 +281,13 @@ class MemoryTracker { inline void TrackInlineField(const MemoryRetainer* retainer, const char* edge_name = nullptr); + // MemoryRetainerTraits implementation helpers. + template + inline void TraitTrack(const T& retainer, const char* edge_name = nullptr); + template + inline void TraitTrackInline(const T& retainer, + const char* edge_name = nullptr); + inline v8::EmbedderGraph* graph() { return graph_; } inline v8::Isolate* isolate() { return isolate_; } diff --git a/src/node_url_pattern.cc b/src/node_url_pattern.cc index 0d1c7ad3e71..bcfe5203d41 100644 --- a/src/node_url_pattern.cc +++ b/src/node_url_pattern.cc @@ -8,6 +8,48 @@ #include "path.h" #include "util-inl.h" +namespace node { +using node::url_pattern::URLPatternRegexProvider; + +template <> +struct MemoryRetainerTraits> { + using Type = ada::url_pattern; + static void MemoryInfo(MemoryTracker* tracker, const Type& value) { + tracker->TraitTrackInline(value.protocol_component, "protocol_component"); + tracker->TraitTrackInline(value.username_component, "username_component"); + tracker->TraitTrackInline(value.password_component, "password_component"); + tracker->TraitTrackInline(value.hostname_component, "hostname_component"); + tracker->TraitTrackInline(value.port_component, "port_component"); + tracker->TraitTrackInline(value.pathname_component, "pathname_component"); + tracker->TraitTrackInline(value.search_component, "search_component"); + tracker->TraitTrackInline(value.hash_component, "hash_component"); + } + + static const char* MemoryInfoName(const Type& value) { + return "ada::url_pattern"; + } + + static size_t SelfSize(const Type& value) { return sizeof(value); } +}; + +template <> +struct MemoryRetainerTraits< + ada::url_pattern_component> { + using Type = ada::url_pattern_component; + static void MemoryInfo(MemoryTracker* tracker, const Type& value) { + tracker->TrackField("pattern", value.pattern); + tracker->TrackField("group_name_list", value.group_name_list); + } + + static const char* MemoryInfoName(const Type& value) { + return "ada::url_pattern_component"; + } + + static size_t SelfSize(const Type& value) { return sizeof(value); } +}; + +} // namespace node + namespace node::url_pattern { using v8::Array; @@ -125,13 +167,7 @@ URLPattern::URLPattern(Environment* env, } void URLPattern::MemoryInfo(MemoryTracker* tracker) const { - tracker->TrackFieldWithSize("protocol", url_pattern_.get_protocol().size()); - tracker->TrackFieldWithSize("username", url_pattern_.get_username().size()); - tracker->TrackFieldWithSize("password", url_pattern_.get_password().size()); - tracker->TrackFieldWithSize("hostname", url_pattern_.get_hostname().size()); - tracker->TrackFieldWithSize("pathname", url_pattern_.get_pathname().size()); - tracker->TrackFieldWithSize("search", url_pattern_.get_search().size()); - tracker->TrackFieldWithSize("hash", url_pattern_.get_hash().size()); + tracker->TraitTrackInline(url_pattern_, "url_pattern"); } void URLPattern::New(const FunctionCallbackInfo& args) { diff --git a/test/pummel/test-heapdump-urlpattern.js b/test/pummel/test-heapdump-urlpattern.js new file mode 100644 index 00000000000..f903feae85b --- /dev/null +++ b/test/pummel/test-heapdump-urlpattern.js @@ -0,0 +1,25 @@ +// Flags: --expose-internals +'use strict'; +require('../common'); +const { validateSnapshotNodes } = require('../common/heap'); +const { URLPattern } = require('node:url'); + +validateSnapshotNodes('Node / URLPattern', []); +const urlPattern = new URLPattern('https://example.com/:id'); +validateSnapshotNodes('Node / URLPattern', [ + { + children: [ + { node_name: 'Node / ada::url_pattern', edge_name: 'url_pattern' }, + ], + }, +]); +validateSnapshotNodes('Node / ada::url_pattern', [ + { + children: [ + { node_name: 'Node / ada::url_pattern_component', edge_name: 'protocol_component' }, + ], + }, +]); + +// Use `urlPattern`. +console.log(urlPattern);