From 85e6a550a9ad004f0fdb34bab70feb5456838e38 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sun, 2 Feb 2025 16:59:56 +0000 Subject: [PATCH 1/2] src: add memory retainer traits for external types Add `MemoryRetainerTraits` to reveal external type memory info without forcing them to inherit from `MemoryRetainer`. --- src/memory_tracker-inl.h | 21 +++++++++++++++++ src/memory_tracker.h | 34 +++++++++++++++++++++++++++ src/node_url_pattern.cc | 50 ++++++++++++++++++++++++++++++++++------ 3 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h index c99ff8607100ba..31ed6fad98ccc8 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 cae4e5c7a663c1..4e0a2fbaaac4f6 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 3d6340565d1e06..188d7df6a04166 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; @@ -123,13 +165,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) { From 6cb748115fd179221ea15b282a20915abadb688d Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sun, 2 Feb 2025 17:28:27 +0000 Subject: [PATCH 2/2] fixup! src: add memory retainer traits for external types --- test/pummel/test-heapdump-urlpattern.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/pummel/test-heapdump-urlpattern.js diff --git a/test/pummel/test-heapdump-urlpattern.js b/test/pummel/test-heapdump-urlpattern.js new file mode 100644 index 00000000000000..f903feae85b999 --- /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);