Skip to content

Commit

Permalink
src: add memory retainer traits for external types
Browse files Browse the repository at this point in the history
Add `MemoryRetainerTraits` to reveal external type memory info without
forcing them to inherit from `MemoryRetainer`.

PR-URL: #56881
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
  • Loading branch information
legendecas authored Feb 4, 2025
1 parent c889b85 commit 316ac8c
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 7 deletions.
21 changes: 21 additions & 0 deletions src/memory_tracker-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,27 @@ void MemoryTracker::TrackInlineField(const MemoryRetainer* retainer,
CurrentNode()->size_ -= retainer->SelfSize();
}

template <typename T>
inline void MemoryTracker::TraitTrack(const T& retainer,
const char* edge_name) {
MemoryRetainerNode* n =
PushNode(MemoryRetainerTraits<T>::MemoryInfoName(retainer),
MemoryRetainerTraits<T>::SelfSize(retainer),
edge_name);
MemoryRetainerTraits<T>::MemoryInfo(this, retainer);
CHECK_EQ(CurrentNode(), n);
CHECK_NE(n->size_, 0);
PopNode();
}

template <typename T>
inline void MemoryTracker::TraitTrackInline(const T& retainer,
const char* edge_name) {
TraitTrack(retainer, edge_name);
CHECK(CurrentNode());
CurrentNode()->size_ -= MemoryRetainerTraits<T>::SelfSize(retainer);
}

MemoryRetainerNode* MemoryTracker::CurrentNode() const {
if (node_stack_.empty()) return nullptr;
return node_stack_.top();
Expand Down
34 changes: 34 additions & 0 deletions src/memory_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExampleRetainer> {
* 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 <typename T, typename = void>
struct MemoryRetainerTraits {};

class MemoryTracker {
public:
// Used to specify node name and size explicitly
Expand Down Expand Up @@ -254,6 +281,13 @@ class MemoryTracker {
inline void TrackInlineField(const MemoryRetainer* retainer,
const char* edge_name = nullptr);

// MemoryRetainerTraits implementation helpers.
template <typename T>
inline void TraitTrack(const T& retainer, const char* edge_name = nullptr);
template <typename T>
inline void TraitTrackInline(const T& retainer,
const char* edge_name = nullptr);

inline v8::EmbedderGraph* graph() { return graph_; }
inline v8::Isolate* isolate() { return isolate_; }

Expand Down
50 changes: 43 additions & 7 deletions src/node_url_pattern.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,48 @@
#include "path.h"
#include "util-inl.h"

namespace node {
using node::url_pattern::URLPatternRegexProvider;

template <>
struct MemoryRetainerTraits<ada::url_pattern<URLPatternRegexProvider>> {
using Type = ada::url_pattern<URLPatternRegexProvider>;
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<URLPatternRegexProvider>> {
using Type = ada::url_pattern_component<URLPatternRegexProvider>;
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;
Expand Down Expand Up @@ -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<Value>& args) {
Expand Down
25 changes: 25 additions & 0 deletions test/pummel/test-heapdump-urlpattern.js
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit 316ac8c

Please sign in to comment.