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