• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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