xref: /third_party/node/src/memory_tracker-inl.h (revision 1cb0ef41)
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
9namespace node {
10
11// Fallback edge_name if node_name is not available, or "" if edge_name
12// is not available either.
13inline 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
23class MemoryRetainerNode : public v8::EmbedderGraph::Node {
24 public:
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)
42      : retainer_(nullptr) {
43    name_ = name;
44    size_ = size;
45    is_root_node_ = is_root_node;
46  }
47
48  const char* Name() override { return name_; }
49  const char* NamePrefix() override { return "Node /"; }
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.
54  Node* JSWrapperNode() { return wrapper_node_; }
55
56  bool IsRootNode() override {
57    if (retainer_ != nullptr) {
58      return retainer_->IsRootNode();
59    }
60    return is_root_node_;
61  }
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
85void 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
91void 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
99void MemoryTracker::TrackField(const char* edge_name,
100                               const MemoryRetainer& value,
101                               const char* node_name) {
102  TrackField(edge_name, &value);
103}
104
105void 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
117template <typename T, typename D>
118void 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
127template <typename T>
128void 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
137template <typename T, bool kIsWeak>
138void 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
145template <typename T, typename Iterator>
146void 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
167template <typename T>
168void 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
183template <typename T, typename test_for_number, typename dummy>
184void 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
191template <typename T, typename U>
192void 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
206template <typename T>
207void 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
213template <typename T>
214void 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
220template <typename T>
221void 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
228template <typename T>
229void 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
236template <typename T>
237void 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
243void 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
249void 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
255void 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
261void 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
267void 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
273void 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
290void 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
297MemoryRetainerNode* MemoryTracker::CurrentNode() const {
298  if (node_stack_.empty()) return nullptr;
299  return node_stack_.top();
300}
301
302MemoryRetainerNode* 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
322MemoryRetainerNode* 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
333MemoryRetainerNode* 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
340MemoryRetainerNode* 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
348void 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