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 #include "util-inl.h"
8
9 namespace node {
10
11 // Fallback edge_name if node_name is not available, or "" if edge_name
12 // is not available either.
GetNodeName(const char * node_name,const char * edge_name)13 inline const char* GetNodeName(const char* node_name, const char* edge_name) {
14 if (node_name != nullptr) {
15 return node_name;
16 }
17 if (edge_name != nullptr) {
18 return edge_name;
19 }
20 return "";
21 }
22
23 class MemoryRetainerNode : public v8::EmbedderGraph::Node {
24 public:
MemoryRetainerNode(MemoryTracker * tracker,const MemoryRetainer * retainer)25 inline MemoryRetainerNode(MemoryTracker* tracker,
26 const MemoryRetainer* retainer)
27 : retainer_(retainer) {
28 CHECK_NOT_NULL(retainer_);
29 v8::HandleScope handle_scope(tracker->isolate());
30 v8::Local<v8::Object> obj = retainer_->WrappedObject();
31 if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj);
32
33 name_ = retainer_->MemoryInfoName();
34 size_ = retainer_->SelfSize();
35 detachedness_ = retainer_->GetDetachedness();
36 }
37
38 inline MemoryRetainerNode(MemoryTracker* tracker,
39 const char* name,
40 size_t size,
41 bool is_root_node = false)
retainer_(nullptr)42 : retainer_(nullptr) {
43 name_ = name;
44 size_ = size;
45 is_root_node_ = is_root_node;
46 }
47
Name()48 const char* Name() override { return name_; }
NamePrefix()49 const char* NamePrefix() override { return "Node /"; }
SizeInBytes()50 size_t SizeInBytes() override { return size_; }
51 // TODO(addaleax): Merging this with the "official" WrapperNode() method
52 // seems to lose accuracy, e.g. SizeInBytes() is disregarded.
53 // Figure out whether to do anything about that.
JSWrapperNode()54 Node* JSWrapperNode() { return wrapper_node_; }
55
IsRootNode()56 bool IsRootNode() override {
57 if (retainer_ != nullptr) {
58 return retainer_->IsRootNode();
59 }
60 return is_root_node_;
61 }
GetDetachedness()62 v8::EmbedderGraph::Node::Detachedness GetDetachedness() override {
63 return detachedness_;
64 }
65
66 private:
67 friend class MemoryTracker;
68
69 // If retainer_ is not nullptr, then it must have a wrapper_node_,
70 // and we have
71 // name_ == retainer_->MemoryInfoName()
72 // size_ == retainer_->SelfSize()
73 // is_root_node_ == retainer_->IsRootNode()
74 const MemoryRetainer* retainer_;
75 Node* wrapper_node_ = nullptr;
76
77 // Otherwise (retainer == nullptr), we set these fields in an ad-hoc way
78 bool is_root_node_ = false;
79 const char* name_;
80 size_t size_ = 0;
81 v8::EmbedderGraph::Node::Detachedness detachedness_ =
82 v8::EmbedderGraph::Node::Detachedness::kUnknown;
83 };
84
TrackFieldWithSize(const char * edge_name,size_t size,const char * node_name)85 void MemoryTracker::TrackFieldWithSize(const char* edge_name,
86 size_t size,
87 const char* node_name) {
88 if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name);
89 }
90
TrackInlineFieldWithSize(const char * edge_name,size_t size,const char * node_name)91 void MemoryTracker::TrackInlineFieldWithSize(const char* edge_name,
92 size_t size,
93 const char* node_name) {
94 if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name);
95 CHECK(CurrentNode());
96 CurrentNode()->size_ -= size;
97 }
98
TrackField(const char * edge_name,const MemoryRetainer & value,const char * node_name)99 void MemoryTracker::TrackField(const char* edge_name,
100 const MemoryRetainer& value,
101 const char* node_name) {
102 TrackField(edge_name, &value);
103 }
104
TrackField(const char * edge_name,const MemoryRetainer * value,const char * node_name)105 void MemoryTracker::TrackField(const char* edge_name,
106 const MemoryRetainer* value,
107 const char* node_name) {
108 if (value == nullptr) return;
109 auto it = seen_.find(value);
110 if (it != seen_.end()) {
111 graph_->AddEdge(CurrentNode(), it->second, edge_name);
112 } else {
113 Track(value, edge_name);
114 }
115 }
116
117 template <typename T, typename D>
TrackField(const char * edge_name,const std::unique_ptr<T,D> & value,const char * node_name)118 void MemoryTracker::TrackField(const char* edge_name,
119 const std::unique_ptr<T, D>& value,
120 const char* node_name) {
121 if (value.get() == nullptr) {
122 return;
123 }
124 TrackField(edge_name, value.get(), node_name);
125 }
126
127 template <typename T>
TrackField(const char * edge_name,const std::shared_ptr<T> & value,const char * node_name)128 void MemoryTracker::TrackField(const char* edge_name,
129 const std::shared_ptr<T>& value,
130 const char* node_name) {
131 if (value.get() == nullptr) {
132 return;
133 }
134 TrackField(edge_name, value.get(), node_name);
135 }
136
137 template <typename T, bool kIsWeak>
TrackField(const char * edge_name,const BaseObjectPtrImpl<T,kIsWeak> & value,const char * node_name)138 void MemoryTracker::TrackField(const char* edge_name,
139 const BaseObjectPtrImpl<T, kIsWeak>& value,
140 const char* node_name) {
141 if (value.get() == nullptr || kIsWeak) return;
142 TrackField(edge_name, value.get(), node_name);
143 }
144
145 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)146 void MemoryTracker::TrackField(const char* edge_name,
147 const T& value,
148 const char* node_name,
149 const char* element_name,
150 bool subtract_from_self) {
151 // If the container is empty, the size has been accounted into the parent's
152 // self size
153 if (value.begin() == value.end()) return;
154 // Fall back to edge name if node names are not provided
155 if (CurrentNode() != nullptr && subtract_from_self) {
156 // Shift the self size of this container out to a separate node
157 CurrentNode()->size_ -= sizeof(T);
158 }
159 PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name);
160 for (Iterator it = value.begin(); it != value.end(); ++it) {
161 // Use nullptr as edge names so the elements appear as indexed properties
162 TrackField(nullptr, *it, element_name);
163 }
164 PopNode();
165 }
166
167 template <typename T>
TrackField(const char * edge_name,const std::queue<T> & value,const char * node_name,const char * element_name)168 void MemoryTracker::TrackField(const char* edge_name,
169 const std::queue<T>& value,
170 const char* node_name,
171 const char* element_name) {
172 struct ContainerGetter : public std::queue<T> {
173 static const typename std::queue<T>::container_type& Get(
174 const std::queue<T>& value) {
175 return value.*&ContainerGetter::c;
176 }
177 };
178
179 const auto& container = ContainerGetter::Get(value);
180 TrackField(edge_name, container, node_name, element_name);
181 }
182
183 template <typename T, typename test_for_number, typename dummy>
TrackField(const char * edge_name,const T & value,const char * node_name)184 void MemoryTracker::TrackField(const char* edge_name,
185 const T& value,
186 const char* node_name) {
187 // For numbers, creating new nodes is not worth the overhead.
188 CurrentNode()->size_ += sizeof(T);
189 }
190
191 template <typename T, typename U>
TrackField(const char * edge_name,const std::pair<T,U> & value,const char * node_name)192 void MemoryTracker::TrackField(const char* edge_name,
193 const std::pair<T, U>& value,
194 const char* node_name) {
195 PushNode(node_name == nullptr ? "pair" : node_name,
196 sizeof(const std::pair<T, U>),
197 edge_name);
198 // TODO(joyeecheung): special case if one of these is a number type
199 // that meets the test_for_number trait so that their sizes don't get
200 // merged into the pair node
201 TrackField("first", value.first);
202 TrackField("second", value.second);
203 PopNode();
204 }
205
206 template <typename T>
TrackField(const char * edge_name,const std::basic_string<T> & value,const char * node_name)207 void MemoryTracker::TrackField(const char* edge_name,
208 const std::basic_string<T>& value,
209 const char* node_name) {
210 TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string");
211 }
212
213 template <typename T>
TrackField(const char * edge_name,const v8::Eternal<T> & value,const char * node_name)214 void MemoryTracker::TrackField(const char* edge_name,
215 const v8::Eternal<T>& value,
216 const char* node_name) {
217 TrackField(edge_name, value.Get(isolate_));
218 }
219
220 template <typename T>
TrackField(const char * edge_name,const v8::PersistentBase<T> & value,const char * node_name)221 void MemoryTracker::TrackField(const char* edge_name,
222 const v8::PersistentBase<T>& value,
223 const char* node_name) {
224 if (value.IsWeak()) return;
225 TrackField(edge_name, value.Get(isolate_));
226 }
227
228 template <typename T>
TrackField(const char * edge_name,const v8::Local<T> & value,const char * node_name)229 void MemoryTracker::TrackField(const char* edge_name,
230 const v8::Local<T>& value,
231 const char* node_name) {
232 if (!value.IsEmpty())
233 graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name);
234 }
235
236 template <typename T>
TrackField(const char * edge_name,const MallocedBuffer<T> & value,const char * node_name)237 void MemoryTracker::TrackField(const char* edge_name,
238 const MallocedBuffer<T>& value,
239 const char* node_name) {
240 TrackFieldWithSize(edge_name, value.size, "MallocedBuffer");
241 }
242
TrackField(const char * edge_name,const v8::BackingStore * value,const char * node_name)243 void MemoryTracker::TrackField(const char* edge_name,
244 const v8::BackingStore* value,
245 const char* node_name) {
246 TrackFieldWithSize(edge_name, value->ByteLength(), "BackingStore");
247 }
248
TrackField(const char * name,const uv_buf_t & value,const char * node_name)249 void MemoryTracker::TrackField(const char* name,
250 const uv_buf_t& value,
251 const char* node_name) {
252 TrackFieldWithSize(name, value.len, "uv_buf_t");
253 }
254
TrackField(const char * name,const uv_timer_t & value,const char * node_name)255 void MemoryTracker::TrackField(const char* name,
256 const uv_timer_t& value,
257 const char* node_name) {
258 TrackFieldWithSize(name, sizeof(value), "uv_timer_t");
259 }
260
TrackField(const char * name,const uv_async_t & value,const char * node_name)261 void MemoryTracker::TrackField(const char* name,
262 const uv_async_t& value,
263 const char* node_name) {
264 TrackFieldWithSize(name, sizeof(value), "uv_async_t");
265 }
266
TrackInlineField(const char * name,const uv_async_t & value,const char * node_name)267 void MemoryTracker::TrackInlineField(const char* name,
268 const uv_async_t& value,
269 const char* node_name) {
270 TrackInlineFieldWithSize(name, sizeof(value), "uv_async_t");
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(), "native_to_javascript");
316 graph_->AddEdge(n->JSWrapperNode(), n, "javascript_to_native");
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