• 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, bool kIsWeak>
TrackField(const char * edge_name,const BaseObjectPtrImpl<T,kIsWeak> & value,const char * node_name)121 void MemoryTracker::TrackField(const char* edge_name,
122                                const BaseObjectPtrImpl<T, kIsWeak>& value,
123                                const char* node_name) {
124   if (value.get() == nullptr || kIsWeak) return;
125   TrackField(edge_name, value.get(), node_name);
126 }
127 
128 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)129 void MemoryTracker::TrackField(const char* edge_name,
130                                const T& value,
131                                const char* node_name,
132                                const char* element_name,
133                                bool subtract_from_self) {
134   // If the container is empty, the size has been accounted into the parent's
135   // self size
136   if (value.begin() == value.end()) return;
137   // Fall back to edge name if node names are not provided
138   if (CurrentNode() != nullptr && subtract_from_self) {
139     // Shift the self size of this container out to a separate node
140     CurrentNode()->size_ -= sizeof(T);
141   }
142   PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name);
143   for (Iterator it = value.begin(); it != value.end(); ++it) {
144     // Use nullptr as edge names so the elements appear as indexed properties
145     TrackField(nullptr, *it, element_name);
146   }
147   PopNode();
148 }
149 
150 template <typename T>
TrackField(const char * edge_name,const std::queue<T> & value,const char * node_name,const char * element_name)151 void MemoryTracker::TrackField(const char* edge_name,
152                                const std::queue<T>& value,
153                                const char* node_name,
154                                const char* element_name) {
155   struct ContainerGetter : public std::queue<T> {
156     static const typename std::queue<T>::container_type& Get(
157         const std::queue<T>& value) {
158       return value.*&ContainerGetter::c;
159     }
160   };
161 
162   const auto& container = ContainerGetter::Get(value);
163   TrackField(edge_name, container, node_name, element_name);
164 }
165 
166 template <typename T, typename test_for_number, typename dummy>
TrackField(const char * edge_name,const T & value,const char * node_name)167 void MemoryTracker::TrackField(const char* edge_name,
168                                const T& value,
169                                const char* node_name) {
170   // For numbers, creating new nodes is not worth the overhead.
171   CurrentNode()->size_ += sizeof(T);
172 }
173 
174 template <typename T, typename U>
TrackField(const char * edge_name,const std::pair<T,U> & value,const char * node_name)175 void MemoryTracker::TrackField(const char* edge_name,
176                                const std::pair<T, U>& value,
177                                const char* node_name) {
178   PushNode(node_name == nullptr ? "pair" : node_name,
179            sizeof(const std::pair<T, U>),
180            edge_name);
181   // TODO(joyeecheung): special case if one of these is a number type
182   // that meets the test_for_number trait so that their sizes don't get
183   // merged into the pair node
184   TrackField("first", value.first);
185   TrackField("second", value.second);
186   PopNode();
187 }
188 
189 template <typename T>
TrackField(const char * edge_name,const std::basic_string<T> & value,const char * node_name)190 void MemoryTracker::TrackField(const char* edge_name,
191                                const std::basic_string<T>& value,
192                                const char* node_name) {
193   TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string");
194 }
195 
196 template <typename T>
TrackField(const char * edge_name,const v8::Eternal<T> & value,const char * node_name)197 void MemoryTracker::TrackField(const char* edge_name,
198                                const v8::Eternal<T>& value,
199                                const char* node_name) {
200   TrackField(edge_name, value.Get(isolate_));
201 }
202 
203 template <typename T>
TrackField(const char * edge_name,const v8::PersistentBase<T> & value,const char * node_name)204 void MemoryTracker::TrackField(const char* edge_name,
205                                const v8::PersistentBase<T>& value,
206                                const char* node_name) {
207   if (value.IsWeak()) return;
208   TrackField(edge_name, value.Get(isolate_));
209 }
210 
211 template <typename T>
TrackField(const char * edge_name,const v8::Local<T> & value,const char * node_name)212 void MemoryTracker::TrackField(const char* edge_name,
213                                const v8::Local<T>& value,
214                                const char* node_name) {
215   if (!value.IsEmpty())
216     graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name);
217 }
218 
219 template <typename T>
TrackField(const char * edge_name,const MallocedBuffer<T> & value,const char * node_name)220 void MemoryTracker::TrackField(const char* edge_name,
221                                const MallocedBuffer<T>& value,
222                                const char* node_name) {
223   TrackFieldWithSize(edge_name, value.size, "MallocedBuffer");
224 }
225 
TrackField(const char * name,const uv_buf_t & value,const char * node_name)226 void MemoryTracker::TrackField(const char* name,
227                                const uv_buf_t& value,
228                                const char* node_name) {
229   TrackFieldWithSize(name, value.len, "uv_buf_t");
230 }
231 
TrackField(const char * name,const uv_timer_t & value,const char * node_name)232 void MemoryTracker::TrackField(const char* name,
233                                const uv_timer_t& value,
234                                const char* node_name) {
235   TrackFieldWithSize(name, sizeof(value), "uv_timer_t");
236 }
237 
TrackField(const char * name,const uv_async_t & value,const char * node_name)238 void MemoryTracker::TrackField(const char* name,
239                                const uv_async_t& value,
240                                const char* node_name) {
241   TrackFieldWithSize(name, sizeof(value), "uv_async_t");
242 }
243 
TrackInlineField(const char * name,const uv_async_t & value,const char * node_name)244 void MemoryTracker::TrackInlineField(const char* name,
245                                      const uv_async_t& value,
246                                      const char* node_name) {
247   TrackInlineFieldWithSize(name, sizeof(value), "uv_async_t");
248 }
249 
250 template <class NativeT, class V8T>
TrackField(const char * name,const AliasedBufferBase<NativeT,V8T> & value,const char * node_name)251 void MemoryTracker::TrackField(const char* name,
252                                const AliasedBufferBase<NativeT, V8T>& value,
253                                const char* node_name) {
254   TrackField(name, value.GetJSArray(), "AliasedBuffer");
255 }
256 
Track(const MemoryRetainer * retainer,const char * edge_name)257 void MemoryTracker::Track(const MemoryRetainer* retainer,
258                           const char* edge_name) {
259   v8::HandleScope handle_scope(isolate_);
260   auto it = seen_.find(retainer);
261   if (it != seen_.end()) {
262     if (CurrentNode() != nullptr) {
263       graph_->AddEdge(CurrentNode(), it->second, edge_name);
264     }
265     return;  // It has already been tracked, no need to call MemoryInfo again
266   }
267   MemoryRetainerNode* n = PushNode(retainer, edge_name);
268   retainer->MemoryInfo(this);
269   CHECK_EQ(CurrentNode(), n);
270   CHECK_NE(n->size_, 0);
271   PopNode();
272 }
273 
TrackInlineField(const MemoryRetainer * retainer,const char * edge_name)274 void MemoryTracker::TrackInlineField(const MemoryRetainer* retainer,
275                                      const char* edge_name) {
276   Track(retainer, edge_name);
277   CHECK(CurrentNode());
278   CurrentNode()->size_ -= retainer->SelfSize();
279 }
280 
CurrentNode()281 MemoryRetainerNode* MemoryTracker::CurrentNode() const {
282   if (node_stack_.empty()) return nullptr;
283   return node_stack_.top();
284 }
285 
AddNode(const MemoryRetainer * retainer,const char * edge_name)286 MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer,
287                                            const char* edge_name) {
288   auto it = seen_.find(retainer);
289   if (it != seen_.end()) {
290     return it->second;
291   }
292 
293   MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer);
294   graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n));
295   seen_[retainer] = n;
296   if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name);
297 
298   if (n->JSWrapperNode() != nullptr) {
299     graph_->AddEdge(n, n->JSWrapperNode(), "wrapped");
300     graph_->AddEdge(n->JSWrapperNode(), n, "wrapper");
301   }
302 
303   return n;
304 }
305 
AddNode(const char * node_name,size_t size,const char * edge_name)306 MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name,
307                                            size_t size,
308                                            const char* edge_name) {
309   MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size);
310   graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n));
311 
312   if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name);
313 
314   return n;
315 }
316 
PushNode(const MemoryRetainer * retainer,const char * edge_name)317 MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer,
318                                             const char* edge_name) {
319   MemoryRetainerNode* n = AddNode(retainer, edge_name);
320   node_stack_.push(n);
321   return n;
322 }
323 
PushNode(const char * node_name,size_t size,const char * edge_name)324 MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name,
325                                             size_t size,
326                                             const char* edge_name) {
327   MemoryRetainerNode* n = AddNode(node_name, size, edge_name);
328   node_stack_.push(n);
329   return n;
330 }
331 
PopNode()332 void MemoryTracker::PopNode() {
333   node_stack_.pop();
334 }
335 
336 }  // namespace node
337 
338 #endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
339 
340 #endif  // SRC_MEMORY_TRACKER_INL_H_
341