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