1 #ifndef SRC_MEMORY_TRACKER_INL_H_
2 #define SRC_MEMORY_TRACKER_INL_H_
3
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5
6 #include "memory_tracker.h"
7
8 namespace node {
9
10 // Fallback edge_name if node_name is not available, or "" if edge_name
11 // is not available either.
GetNodeName(const char * node_name,const char * edge_name)12 inline const char* GetNodeName(const char* node_name, const char* edge_name) {
13 if (node_name != nullptr) {
14 return node_name;
15 }
16 if (edge_name != nullptr) {
17 return edge_name;
18 }
19 return "";
20 }
21
22 class MemoryRetainerNode : public v8::EmbedderGraph::Node {
23 public:
MemoryRetainerNode(MemoryTracker * tracker,const MemoryRetainer * retainer)24 inline MemoryRetainerNode(MemoryTracker* tracker,
25 const MemoryRetainer* retainer)
26 : retainer_(retainer) {
27 CHECK_NOT_NULL(retainer_);
28 v8::HandleScope handle_scope(tracker->isolate());
29 v8::Local<v8::Object> obj = retainer_->WrappedObject();
30 if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj);
31
32 name_ = retainer_->MemoryInfoName();
33 size_ = retainer_->SelfSize();
34 detachedness_ = retainer_->GetDetachedness();
35 }
36
37 inline MemoryRetainerNode(MemoryTracker* tracker,
38 const char* name,
39 size_t size,
40 bool is_root_node = false)
retainer_(nullptr)41 : retainer_(nullptr) {
42 name_ = name;
43 size_ = size;
44 is_root_node_ = is_root_node;
45 }
46
Name()47 const char* Name() override { return name_; }
NamePrefix()48 const char* NamePrefix() override { return "Node /"; }
SizeInBytes()49 size_t SizeInBytes() override { return size_; }
50 // TODO(addaleax): Merging this with the "official" WrapperNode() method
51 // seems to lose accuracy, e.g. SizeInBytes() is disregarded.
52 // Figure out whether to do anything about that.
JSWrapperNode()53 Node* JSWrapperNode() { return wrapper_node_; }
54
IsRootNode()55 bool IsRootNode() override {
56 if (retainer_ != nullptr) {
57 return retainer_->IsRootNode();
58 }
59 return is_root_node_;
60 }
GetDetachedness()61 v8::EmbedderGraph::Node::Detachedness GetDetachedness() override {
62 return detachedness_;
63 }
64
65 private:
66 friend class MemoryTracker;
67
68 // If retainer_ is not nullptr, then it must have a wrapper_node_,
69 // and we have
70 // name_ == retainer_->MemoryInfoName()
71 // size_ == retainer_->SelfSize()
72 // is_root_node_ == retainer_->IsRootNode()
73 const MemoryRetainer* retainer_;
74 Node* wrapper_node_ = nullptr;
75
76 // Otherwise (retainer == nullptr), we set these fields in an ad-hoc way
77 bool is_root_node_ = false;
78 const char* name_;
79 size_t size_ = 0;
80 v8::EmbedderGraph::Node::Detachedness detachedness_ =
81 v8::EmbedderGraph::Node::Detachedness::kUnknown;
82 };
83
TrackFieldWithSize(const char * edge_name,size_t size,const char * node_name)84 void MemoryTracker::TrackFieldWithSize(const char* edge_name,
85 size_t size,
86 const char* node_name) {
87 if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name);
88 }
89
TrackInlineFieldWithSize(const char * edge_name,size_t size,const char * node_name)90 void MemoryTracker::TrackInlineFieldWithSize(const char* edge_name,
91 size_t size,
92 const char* node_name) {
93 if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name);
94 CHECK(CurrentNode());
95 CurrentNode()->size_ -= size;
96 }
97
TrackField(const char * edge_name,const MemoryRetainer & value,const char * node_name)98 void MemoryTracker::TrackField(const char* edge_name,
99 const MemoryRetainer& value,
100 const char* node_name) {
101 TrackField(edge_name, &value);
102 }
103
TrackField(const char * edge_name,const MemoryRetainer * value,const char * node_name)104 void MemoryTracker::TrackField(const char* edge_name,
105 const MemoryRetainer* value,
106 const char* node_name) {
107 if (value == nullptr) return;
108 auto it = seen_.find(value);
109 if (it != seen_.end()) {
110 graph_->AddEdge(CurrentNode(), it->second, edge_name);
111 } else {
112 Track(value, edge_name);
113 }
114 }
115
116 template <typename T, typename D>
TrackField(const char * edge_name,const std::unique_ptr<T,D> & value,const char * node_name)117 void MemoryTracker::TrackField(const char* edge_name,
118 const std::unique_ptr<T, D>& value,
119 const char* node_name) {
120 if (value.get() == nullptr) {
121 return;
122 }
123 TrackField(edge_name, value.get(), node_name);
124 }
125
126 template <typename T>
TrackField(const char * edge_name,const std::shared_ptr<T> & value,const char * node_name)127 void MemoryTracker::TrackField(const char* edge_name,
128 const std::shared_ptr<T>& value,
129 const char* node_name) {
130 if (value.get() == nullptr) {
131 return;
132 }
133 TrackField(edge_name, value.get(), node_name);
134 }
135
136 template <typename T, bool kIsWeak>
TrackField(const char * edge_name,const BaseObjectPtrImpl<T,kIsWeak> & value,const char * node_name)137 void MemoryTracker::TrackField(const char* edge_name,
138 const BaseObjectPtrImpl<T, kIsWeak>& value,
139 const char* node_name) {
140 if (value.get() == nullptr || kIsWeak) return;
141 TrackField(edge_name, value.get(), node_name);
142 }
143
144 template <typename T, typename Iterator>
TrackField(const char * edge_name,const T & value,const char * node_name,const char * element_name,bool subtract_from_self)145 void MemoryTracker::TrackField(const char* edge_name,
146 const T& value,
147 const char* node_name,
148 const char* element_name,
149 bool subtract_from_self) {
150 // If the container is empty, the size has been accounted into the parent's
151 // self size
152 if (value.begin() == value.end()) return;
153 // Fall back to edge name if node names are not provided
154 if (CurrentNode() != nullptr && subtract_from_self) {
155 // Shift the self size of this container out to a separate node
156 CurrentNode()->size_ -= sizeof(T);
157 }
158 PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name);
159 for (Iterator it = value.begin(); it != value.end(); ++it) {
160 // Use nullptr as edge names so the elements appear as indexed properties
161 TrackField(nullptr, *it, element_name);
162 }
163 PopNode();
164 }
165
166 template <typename T>
TrackField(const char * edge_name,const std::queue<T> & value,const char * node_name,const char * element_name)167 void MemoryTracker::TrackField(const char* edge_name,
168 const std::queue<T>& value,
169 const char* node_name,
170 const char* element_name) {
171 struct ContainerGetter : public std::queue<T> {
172 static const typename std::queue<T>::container_type& Get(
173 const std::queue<T>& value) {
174 return value.*&ContainerGetter::c;
175 }
176 };
177
178 const auto& container = ContainerGetter::Get(value);
179 TrackField(edge_name, container, node_name, element_name);
180 }
181
182 template <typename T, typename test_for_number, typename dummy>
TrackField(const char * edge_name,const T & value,const char * node_name)183 void MemoryTracker::TrackField(const char* edge_name,
184 const T& value,
185 const char* node_name) {
186 // For numbers, creating new nodes is not worth the overhead.
187 CurrentNode()->size_ += sizeof(T);
188 }
189
190 template <typename T, typename U>
TrackField(const char * edge_name,const std::pair<T,U> & value,const char * node_name)191 void MemoryTracker::TrackField(const char* edge_name,
192 const std::pair<T, U>& value,
193 const char* node_name) {
194 PushNode(node_name == nullptr ? "pair" : node_name,
195 sizeof(const std::pair<T, U>),
196 edge_name);
197 // TODO(joyeecheung): special case if one of these is a number type
198 // that meets the test_for_number trait so that their sizes don't get
199 // merged into the pair node
200 TrackField("first", value.first);
201 TrackField("second", value.second);
202 PopNode();
203 }
204
205 template <typename T>
TrackField(const char * edge_name,const std::basic_string<T> & value,const char * node_name)206 void MemoryTracker::TrackField(const char* edge_name,
207 const std::basic_string<T>& value,
208 const char* node_name) {
209 TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string");
210 }
211
212 template <typename T>
TrackField(const char * edge_name,const v8::Eternal<T> & value,const char * node_name)213 void MemoryTracker::TrackField(const char* edge_name,
214 const v8::Eternal<T>& value,
215 const char* node_name) {
216 TrackField(edge_name, value.Get(isolate_));
217 }
218
219 template <typename T>
TrackField(const char * edge_name,const v8::PersistentBase<T> & value,const char * node_name)220 void MemoryTracker::TrackField(const char* edge_name,
221 const v8::PersistentBase<T>& value,
222 const char* node_name) {
223 if (value.IsWeak()) return;
224 TrackField(edge_name, value.Get(isolate_));
225 }
226
227 template <typename T>
TrackField(const char * edge_name,const v8::Local<T> & value,const char * node_name)228 void MemoryTracker::TrackField(const char* edge_name,
229 const v8::Local<T>& value,
230 const char* node_name) {
231 if (!value.IsEmpty())
232 graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name);
233 }
234
235 template <typename T>
TrackField(const char * edge_name,const MallocedBuffer<T> & value,const char * node_name)236 void MemoryTracker::TrackField(const char* edge_name,
237 const MallocedBuffer<T>& value,
238 const char* node_name) {
239 TrackFieldWithSize(edge_name, value.size, "MallocedBuffer");
240 }
241
TrackField(const char * edge_name,const v8::BackingStore * value,const char * node_name)242 void MemoryTracker::TrackField(const char* edge_name,
243 const v8::BackingStore* value,
244 const char* node_name) {
245 TrackFieldWithSize(edge_name, value->ByteLength(), "BackingStore");
246 }
247
TrackField(const char * name,const uv_buf_t & value,const char * node_name)248 void MemoryTracker::TrackField(const char* name,
249 const uv_buf_t& value,
250 const char* node_name) {
251 TrackFieldWithSize(name, value.len, "uv_buf_t");
252 }
253
TrackField(const char * name,const uv_timer_t & value,const char * node_name)254 void MemoryTracker::TrackField(const char* name,
255 const uv_timer_t& value,
256 const char* node_name) {
257 TrackFieldWithSize(name, sizeof(value), "uv_timer_t");
258 }
259
TrackField(const char * name,const uv_async_t & value,const char * node_name)260 void MemoryTracker::TrackField(const char* name,
261 const uv_async_t& value,
262 const char* node_name) {
263 TrackFieldWithSize(name, sizeof(value), "uv_async_t");
264 }
265
TrackInlineField(const char * name,const uv_async_t & value,const char * node_name)266 void MemoryTracker::TrackInlineField(const char* name,
267 const uv_async_t& value,
268 const char* node_name) {
269 TrackInlineFieldWithSize(name, sizeof(value), "uv_async_t");
270 }
271
272 template <class NativeT, class V8T>
TrackField(const char * name,const AliasedBufferBase<NativeT,V8T> & value,const char * node_name)273 void MemoryTracker::TrackField(const char* name,
274 const AliasedBufferBase<NativeT, V8T>& value,
275 const char* node_name) {
276 TrackField(name, value.GetJSArray(), "AliasedBuffer");
277 }
278
Track(const MemoryRetainer * retainer,const char * edge_name)279 void MemoryTracker::Track(const MemoryRetainer* retainer,
280 const char* edge_name) {
281 v8::HandleScope handle_scope(isolate_);
282 auto it = seen_.find(retainer);
283 if (it != seen_.end()) {
284 if (CurrentNode() != nullptr) {
285 graph_->AddEdge(CurrentNode(), it->second, edge_name);
286 }
287 return; // It has already been tracked, no need to call MemoryInfo again
288 }
289 MemoryRetainerNode* n = PushNode(retainer, edge_name);
290 retainer->MemoryInfo(this);
291 CHECK_EQ(CurrentNode(), n);
292 CHECK_NE(n->size_, 0);
293 PopNode();
294 }
295
TrackInlineField(const MemoryRetainer * retainer,const char * edge_name)296 void MemoryTracker::TrackInlineField(const MemoryRetainer* retainer,
297 const char* edge_name) {
298 Track(retainer, edge_name);
299 CHECK(CurrentNode());
300 CurrentNode()->size_ -= retainer->SelfSize();
301 }
302
CurrentNode()303 MemoryRetainerNode* MemoryTracker::CurrentNode() const {
304 if (node_stack_.empty()) return nullptr;
305 return node_stack_.top();
306 }
307
AddNode(const MemoryRetainer * retainer,const char * edge_name)308 MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer,
309 const char* edge_name) {
310 auto it = seen_.find(retainer);
311 if (it != seen_.end()) {
312 return it->second;
313 }
314
315 MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer);
316 graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n));
317 seen_[retainer] = n;
318 if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name);
319
320 if (n->JSWrapperNode() != nullptr) {
321 graph_->AddEdge(n, n->JSWrapperNode(), "native_to_javascript");
322 graph_->AddEdge(n->JSWrapperNode(), n, "javascript_to_native");
323 }
324
325 return n;
326 }
327
AddNode(const char * node_name,size_t size,const char * edge_name)328 MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name,
329 size_t size,
330 const char* edge_name) {
331 MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size);
332 graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n));
333
334 if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name);
335
336 return n;
337 }
338
PushNode(const MemoryRetainer * retainer,const char * edge_name)339 MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer,
340 const char* edge_name) {
341 MemoryRetainerNode* n = AddNode(retainer, edge_name);
342 node_stack_.push(n);
343 return n;
344 }
345
PushNode(const char * node_name,size_t size,const char * edge_name)346 MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name,
347 size_t size,
348 const char* edge_name) {
349 MemoryRetainerNode* n = AddNode(node_name, size, edge_name);
350 node_stack_.push(n);
351 return n;
352 }
353
PopNode()354 void MemoryTracker::PopNode() {
355 node_stack_.pop();
356 }
357
358 } // namespace node
359
360 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
361
362 #endif // SRC_MEMORY_TRACKER_INL_H_
363