• 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 
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