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