77
88namespace node {
99
10+ class MemoryRetainerNode : public v8 ::EmbedderGraph::Node {
11+ public:
12+ explicit inline MemoryRetainerNode (MemoryTracker* tracker,
13+ const MemoryRetainer* retainer,
14+ const char * name)
15+ : retainer_(retainer) {
16+ if (retainer_ != nullptr ) {
17+ v8::HandleScope handle_scope (tracker->isolate ());
18+ v8::Local<v8::Object> obj = retainer_->WrappedObject ();
19+ if (!obj.IsEmpty ())
20+ wrapper_node_ = tracker->graph ()->V8Node (obj);
21+
22+ name_ = retainer_->MemoryInfoName ();
23+ }
24+ if (name_.empty () && name != nullptr ) {
25+ name_ = name;
26+ }
27+ }
28+
29+ const char * Name () override { return name_.c_str (); }
30+ const char * NamePrefix () override { return " Node /" ; }
31+ size_t SizeInBytes () override { return size_; }
32+ // TODO(addaleax): Merging this with the "official" WrapperNode() method
33+ // seems to lose accuracy, e.g. SizeInBytes() is disregarded.
34+ // Figure out whether to do anything about that.
35+ Node* JSWrapperNode () { return wrapper_node_; }
36+
37+ bool IsRootNode () override {
38+ return retainer_ != nullptr && retainer_->IsRootNode ();
39+ }
40+
41+ private:
42+ friend class MemoryTracker ;
43+
44+ Node* wrapper_node_ = nullptr ;
45+ const MemoryRetainer* retainer_;
46+ std::string name_;
47+ size_t size_ = 0 ;
48+ };
49+
1050template <typename T>
1151void MemoryTracker::TrackThis (const T* obj) {
12- accumulated_size_ + = sizeof (T);
52+ CurrentNode ()-> size_ = sizeof (T);
1353}
1454
1555void MemoryTracker::TrackFieldWithSize (const char * name, size_t size) {
16- accumulated_size_ += size;
56+ if (size > 0 )
57+ AddNode (name)->size_ = size;
1758}
1859
1960void MemoryTracker::TrackField (const char * name, const MemoryRetainer& value) {
2061 TrackField (name, &value);
2162}
2263
2364void MemoryTracker::TrackField (const char * name, const MemoryRetainer* value) {
24- if (track_only_self_ || value == nullptr || seen_.count (value) > 0 ) return ;
25- seen_.insert (value);
26- Track (value);
65+ if (track_only_self_ || value == nullptr ) return ;
66+ auto it = seen_.find (value);
67+ if (it != seen_.end ()) {
68+ graph_->AddEdge (CurrentNode (), it->second );
69+ } else {
70+ Track (value, name);
71+ }
2772}
2873
2974template <typename T>
@@ -36,8 +81,10 @@ template <typename T, typename Iterator>
3681void MemoryTracker::TrackField (const char * name, const T& value) {
3782 if (value.begin () == value.end ()) return ;
3883 size_t index = 0 ;
84+ PushNode (name);
3985 for (Iterator it = value.begin (); it != value.end (); ++it)
4086 TrackField (std::to_string (index++).c_str (), *it);
87+ PopNode ();
4188}
4289
4390template <typename T>
@@ -56,13 +103,15 @@ void MemoryTracker::TrackField(const char* name, const std::queue<T>& value) {
56103template <typename T, typename test_for_number, typename dummy>
57104void MemoryTracker::TrackField (const char * name, const T& value) {
58105 // For numbers, creating new nodes is not worth the overhead.
59- TrackFieldWithSize (name, sizeof (T) );
106+ CurrentNode ()-> size_ += sizeof (T);
60107}
61108
62109template <typename T, typename U>
63110void MemoryTracker::TrackField (const char * name, const std::pair<T, U>& value) {
111+ PushNode (name);
64112 TrackField (" first" , value.first );
65113 TrackField (" second" , value.second );
114+ PopNode ();
66115}
67116
68117template <typename T>
@@ -74,10 +123,13 @@ void MemoryTracker::TrackField(const char* name,
74123template <typename T, typename Traits>
75124void MemoryTracker::TrackField (const char * name,
76125 const v8::Persistent<T, Traits>& value) {
126+ TrackField (name, value.Get (isolate_));
77127}
78128
79129template <typename T>
80130void MemoryTracker::TrackField (const char * name, const v8::Local<T>& value) {
131+ if (!value.IsEmpty ())
132+ graph_->AddEdge (CurrentNode (), graph_->V8Node (value));
81133}
82134
83135template <typename T>
@@ -96,8 +148,47 @@ void MemoryTracker::TrackField(const char* name,
96148 TrackField (name, value.GetJSArray ());
97149}
98150
99- void MemoryTracker::Track (const MemoryRetainer* value) {
151+ void MemoryTracker::Track (const MemoryRetainer* value, const char * name) {
152+ v8::HandleScope handle_scope (isolate_);
153+ MemoryRetainerNode* n = PushNode (name, value);
100154 value->MemoryInfo (this );
155+ CHECK_EQ (CurrentNode (), n);
156+ CHECK_NE (n->size_ , 0 );
157+ PopNode ();
158+ }
159+
160+ MemoryRetainerNode* MemoryTracker::CurrentNode () const {
161+ if (node_stack_.empty ()) return nullptr ;
162+ return node_stack_.top ();
163+ }
164+
165+ MemoryRetainerNode* MemoryTracker::AddNode (
166+ const char * name, const MemoryRetainer* retainer) {
167+ MemoryRetainerNode* n = new MemoryRetainerNode (this , retainer, name);
168+ graph_->AddNode (std::unique_ptr<v8::EmbedderGraph::Node>(n));
169+ if (retainer != nullptr )
170+ seen_[retainer] = n;
171+
172+ if (CurrentNode () != nullptr )
173+ graph_->AddEdge (CurrentNode (), n);
174+
175+ if (n->JSWrapperNode () != nullptr ) {
176+ graph_->AddEdge (n, n->JSWrapperNode ());
177+ graph_->AddEdge (n->JSWrapperNode (), n);
178+ }
179+
180+ return n;
181+ }
182+
183+ MemoryRetainerNode* MemoryTracker::PushNode (
184+ const char * name, const MemoryRetainer* retainer) {
185+ MemoryRetainerNode* n = AddNode (name, retainer);
186+ node_stack_.push (n);
187+ return n;
188+ }
189+
190+ void MemoryTracker::PopNode () {
191+ node_stack_.pop ();
101192}
102193
103194} // namespace node
0 commit comments