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. |
12 |
|
4109 |
inline const char* GetNodeName(const char* node_name, const char* edge_name) { |
13 |
✓✓ |
4109 |
if (node_name != nullptr) { |
14 |
|
3954 |
return node_name; |
15 |
|
|
} |
16 |
✓✗ |
155 |
if (edge_name != nullptr) { |
17 |
|
155 |
return edge_name; |
18 |
|
|
} |
19 |
|
|
return ""; |
20 |
|
|
} |
21 |
|
|
|
22 |
|
|
class MemoryRetainerNode : public v8::EmbedderGraph::Node { |
23 |
|
|
public: |
24 |
|
1205 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
25 |
|
|
const MemoryRetainer* retainer) |
26 |
|
1205 |
: retainer_(retainer) { |
27 |
✗✓ |
1205 |
CHECK_NOT_NULL(retainer_); |
28 |
|
1205 |
v8::HandleScope handle_scope(tracker->isolate()); |
29 |
|
1205 |
v8::Local<v8::Object> obj = retainer_->WrappedObject(); |
30 |
✓✓ |
1205 |
if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj); |
31 |
|
|
|
32 |
|
1205 |
name_ = retainer_->MemoryInfoName(); |
33 |
|
1205 |
size_ = retainer_->SelfSize(); |
34 |
|
1205 |
detachedness_ = retainer_->GetDetachedness(); |
35 |
|
1205 |
} |
36 |
|
|
|
37 |
|
4109 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
38 |
|
|
const char* name, |
39 |
|
|
size_t size, |
40 |
|
|
bool is_root_node = false) |
41 |
|
4109 |
: retainer_(nullptr) { |
42 |
|
4109 |
name_ = name; |
43 |
|
4109 |
size_ = size; |
44 |
|
4109 |
is_root_node_ = is_root_node; |
45 |
|
4109 |
} |
46 |
|
|
|
47 |
|
5314 |
const char* Name() override { return name_.c_str(); } |
48 |
|
6195 |
const char* NamePrefix() override { return "Node /"; } |
49 |
|
5314 |
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. |
53 |
|
2975 |
Node* JSWrapperNode() { return wrapper_node_; } |
54 |
|
|
|
55 |
|
9747 |
bool IsRootNode() override { |
56 |
✓✓ |
9747 |
if (retainer_ != nullptr) { |
57 |
|
2185 |
return retainer_->IsRootNode(); |
58 |
|
|
} |
59 |
|
7562 |
return is_root_node_; |
60 |
|
|
} |
61 |
|
4433 |
v8::EmbedderGraph::Node::Detachedness GetDetachedness() override { |
62 |
|
4433 |
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 |
|
|
std::string name_; |
79 |
|
|
size_t size_ = 0; |
80 |
|
|
v8::EmbedderGraph::Node::Detachedness detachedness_ = |
81 |
|
|
v8::EmbedderGraph::Node::Detachedness::kUnknown; |
82 |
|
|
}; |
83 |
|
|
|
84 |
|
3978 |
void MemoryTracker::TrackFieldWithSize(const char* edge_name, |
85 |
|
|
size_t size, |
86 |
|
|
const char* node_name) { |
87 |
✓✓ |
3978 |
if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name); |
88 |
|
3978 |
} |
89 |
|
|
|
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 |
|
|
|
98 |
|
191 |
void MemoryTracker::TrackField(const char* edge_name, |
99 |
|
|
const MemoryRetainer& value, |
100 |
|
|
const char* node_name) { |
101 |
|
191 |
TrackField(edge_name, &value); |
102 |
|
191 |
} |
103 |
|
|
|
104 |
|
325 |
void MemoryTracker::TrackField(const char* edge_name, |
105 |
|
|
const MemoryRetainer* value, |
106 |
|
|
const char* node_name) { |
107 |
✗✓ |
325 |
if (value == nullptr) return; |
108 |
|
325 |
auto it = seen_.find(value); |
109 |
✓✓ |
325 |
if (it != seen_.end()) { |
110 |
|
41 |
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
111 |
|
|
} else { |
112 |
|
284 |
Track(value, edge_name); |
113 |
|
|
} |
114 |
|
|
} |
115 |
|
|
|
116 |
|
|
template <typename T, typename D> |
117 |
|
67 |
void MemoryTracker::TrackField(const char* edge_name, |
118 |
|
|
const std::unique_ptr<T, D>& value, |
119 |
|
|
const char* node_name) { |
120 |
✓✓ |
67 |
if (value.get() == nullptr) { |
121 |
|
2 |
return; |
122 |
|
|
} |
123 |
|
65 |
TrackField(edge_name, value.get(), node_name); |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
template <typename T> |
127 |
|
8 |
void MemoryTracker::TrackField(const char* edge_name, |
128 |
|
|
const std::shared_ptr<T>& value, |
129 |
|
|
const char* node_name) { |
130 |
✗✓ |
8 |
if (value.get() == nullptr) { |
131 |
|
|
return; |
132 |
|
|
} |
133 |
|
8 |
TrackField(edge_name, value.get(), node_name); |
134 |
|
|
} |
135 |
|
|
|
136 |
|
|
template <typename T, bool kIsWeak> |
137 |
|
9 |
void MemoryTracker::TrackField(const char* edge_name, |
138 |
|
|
const BaseObjectPtrImpl<T, kIsWeak>& value, |
139 |
|
|
const char* node_name) { |
140 |
✗✓✗✓
|
9 |
if (value.get() == nullptr || kIsWeak) return; |
141 |
|
9 |
TrackField(edge_name, value.get(), node_name); |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
template <typename T, typename Iterator> |
145 |
|
924 |
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 |
✗✓✓ |
924 |
if (value.begin() == value.end()) return; |
153 |
|
|
// Fall back to edge name if node names are not provided |
154 |
✓✗✓✗ ✓✗ |
253 |
if (CurrentNode() != nullptr && subtract_from_self) { |
155 |
|
|
// Shift the self size of this container out to a separate node |
156 |
|
253 |
CurrentNode()->size_ -= sizeof(T); |
157 |
|
|
} |
158 |
|
253 |
PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name); |
159 |
✓✓✓ |
8915 |
for (Iterator it = value.begin(); it != value.end(); ++it) { |
160 |
|
|
// Use nullptr as edge names so the elements appear as indexed properties |
161 |
|
8662 |
TrackField(nullptr, *it, element_name); |
162 |
|
|
} |
163 |
|
253 |
PopNode(); |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
template <typename T> |
167 |
|
16 |
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 |
|
8 |
static const typename std::queue<T>::container_type& Get( |
173 |
|
|
const std::queue<T>& value) { |
174 |
|
8 |
return value.*&ContainerGetter::c; |
175 |
|
|
} |
176 |
|
|
}; |
177 |
|
|
|
178 |
|
16 |
const auto& container = ContainerGetter::Get(value); |
179 |
|
16 |
TrackField(edge_name, container, node_name, element_name); |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
template <typename T, typename test_for_number, typename dummy> |
183 |
|
79 |
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 |
|
79 |
CurrentNode()->size_ += sizeof(T); |
188 |
|
79 |
} |
189 |
|
|
|
190 |
|
|
template <typename T, typename U> |
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> |
206 |
|
3871 |
void MemoryTracker::TrackField(const char* edge_name, |
207 |
|
|
const std::basic_string<T>& value, |
208 |
|
|
const char* node_name) { |
209 |
|
3871 |
TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string"); |
210 |
|
3871 |
} |
211 |
|
|
|
212 |
|
|
template <typename T> |
213 |
|
2146 |
void MemoryTracker::TrackField(const char* edge_name, |
214 |
|
|
const v8::Eternal<T>& value, |
215 |
|
|
const char* node_name) { |
216 |
|
2146 |
TrackField(edge_name, value.Get(isolate_)); |
217 |
|
2146 |
} |
218 |
|
|
|
219 |
|
|
template <typename T> |
220 |
|
205 |
void MemoryTracker::TrackField(const char* edge_name, |
221 |
|
|
const v8::PersistentBase<T>& value, |
222 |
|
|
const char* node_name) { |
223 |
✗✓ |
205 |
if (value.IsWeak()) return; |
224 |
✓✓ |
410 |
TrackField(edge_name, value.Get(isolate_)); |
225 |
|
|
} |
226 |
|
|
|
227 |
|
|
template <typename T> |
228 |
|
31232 |
void MemoryTracker::TrackField(const char* edge_name, |
229 |
|
|
const v8::Local<T>& value, |
230 |
|
|
const char* node_name) { |
231 |
✓✓ |
31232 |
if (!value.IsEmpty()) |
232 |
|
29428 |
graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name); |
233 |
|
31232 |
} |
234 |
|
|
|
235 |
|
|
template <typename T> |
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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> |
273 |
|
926 |
void MemoryTracker::TrackField(const char* name, |
274 |
|
|
const AliasedBufferBase<NativeT, V8T>& value, |
275 |
|
|
const char* node_name) { |
276 |
|
926 |
TrackField(name, value.GetJSArray(), "AliasedBuffer"); |
277 |
|
926 |
} |
278 |
|
|
|
279 |
|
2091 |
void MemoryTracker::Track(const MemoryRetainer* retainer, |
280 |
|
|
const char* edge_name) { |
281 |
|
2091 |
v8::HandleScope handle_scope(isolate_); |
282 |
|
2091 |
auto it = seen_.find(retainer); |
283 |
✓✓ |
2091 |
if (it != seen_.end()) { |
284 |
✓✗ |
886 |
if (CurrentNode() != nullptr) { |
285 |
|
886 |
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
286 |
|
|
} |
287 |
|
886 |
return; // It has already been tracked, no need to call MemoryInfo again |
288 |
|
|
} |
289 |
|
1205 |
MemoryRetainerNode* n = PushNode(retainer, edge_name); |
290 |
|
1205 |
retainer->MemoryInfo(this); |
291 |
✗✓ |
1205 |
CHECK_EQ(CurrentNode(), n); |
292 |
✗✓ |
1205 |
CHECK_NE(n->size_, 0); |
293 |
|
1205 |
PopNode(); |
294 |
|
|
} |
295 |
|
|
|
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 |
|
|
|
303 |
|
28861 |
MemoryRetainerNode* MemoryTracker::CurrentNode() const { |
304 |
✓✓ |
28861 |
if (node_stack_.empty()) return nullptr; |
305 |
|
28824 |
return node_stack_.top(); |
306 |
|
|
} |
307 |
|
|
|
308 |
|
1205 |
MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer, |
309 |
|
|
const char* edge_name) { |
310 |
|
1205 |
auto it = seen_.find(retainer); |
311 |
✗✓ |
1205 |
if (it != seen_.end()) { |
312 |
|
|
return it->second; |
313 |
|
|
} |
314 |
|
|
|
315 |
|
1205 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer); |
316 |
|
1205 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
317 |
|
1205 |
seen_[retainer] = n; |
318 |
✓✓ |
1205 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
319 |
|
|
|
320 |
✓✓ |
1205 |
if (n->JSWrapperNode() != nullptr) { |
321 |
|
885 |
graph_->AddEdge(n, n->JSWrapperNode(), "wrapped"); |
322 |
|
885 |
graph_->AddEdge(n->JSWrapperNode(), n, "wrapper"); |
323 |
|
|
} |
324 |
|
|
|
325 |
|
1205 |
return n; |
326 |
|
|
} |
327 |
|
|
|
328 |
|
4109 |
MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name, |
329 |
|
|
size_t size, |
330 |
|
|
const char* edge_name) { |
331 |
|
4109 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size); |
332 |
|
4109 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
333 |
|
|
|
334 |
✓✗ |
4109 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
335 |
|
|
|
336 |
|
4109 |
return n; |
337 |
|
|
} |
338 |
|
|
|
339 |
|
1205 |
MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer, |
340 |
|
|
const char* edge_name) { |
341 |
|
1205 |
MemoryRetainerNode* n = AddNode(retainer, edge_name); |
342 |
|
1205 |
node_stack_.push(n); |
343 |
|
1205 |
return n; |
344 |
|
|
} |
345 |
|
|
|
346 |
|
145 |
MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name, |
347 |
|
|
size_t size, |
348 |
|
|
const char* edge_name) { |
349 |
|
145 |
MemoryRetainerNode* n = AddNode(node_name, size, edge_name); |
350 |
|
145 |
node_stack_.push(n); |
351 |
|
145 |
return n; |
352 |
|
|
} |
353 |
|
|
|
354 |
|
1350 |
void MemoryTracker::PopNode() { |
355 |
|
1350 |
node_stack_.pop(); |
356 |
|
1350 |
} |
357 |
|
|
|
358 |
|
|
} // namespace node |
359 |
|
|
|
360 |
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
361 |
|
|
|
362 |
|
|
#endif // SRC_MEMORY_TRACKER_INL_H_ |