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 |
|
1742 |
inline const char* GetNodeName(const char* node_name, const char* edge_name) { |
13 |
✓✓ |
1742 |
if (node_name != nullptr) { |
14 |
|
1598 |
return node_name; |
15 |
|
|
} |
16 |
✓✗ |
144 |
if (edge_name != nullptr) { |
17 |
|
144 |
return edge_name; |
18 |
|
|
} |
19 |
|
|
return ""; |
20 |
|
|
} |
21 |
|
|
|
22 |
|
|
class MemoryRetainerNode : public v8::EmbedderGraph::Node { |
23 |
|
|
public: |
24 |
|
838 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
25 |
|
|
const MemoryRetainer* retainer) |
26 |
|
838 |
: retainer_(retainer) { |
27 |
✗✓ |
838 |
CHECK_NOT_NULL(retainer_); |
28 |
|
838 |
v8::HandleScope handle_scope(tracker->isolate()); |
29 |
|
838 |
v8::Local<v8::Object> obj = retainer_->WrappedObject(); |
30 |
✓✓ |
838 |
if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj); |
31 |
|
|
|
32 |
|
838 |
name_ = retainer_->MemoryInfoName(); |
33 |
|
838 |
size_ = retainer_->SelfSize(); |
34 |
|
838 |
} |
35 |
|
|
|
36 |
|
1742 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
37 |
|
|
const char* name, |
38 |
|
|
size_t size, |
39 |
|
|
bool is_root_node = false) |
40 |
|
1742 |
: retainer_(nullptr) { |
41 |
|
1742 |
name_ = name; |
42 |
|
1742 |
size_ = size; |
43 |
|
1742 |
is_root_node_ = is_root_node; |
44 |
|
1742 |
} |
45 |
|
|
|
46 |
|
2580 |
const char* Name() override { return name_.c_str(); } |
47 |
|
2580 |
const char* NamePrefix() override { return "Node /"; } |
48 |
|
2580 |
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. |
52 |
|
2010 |
Node* JSWrapperNode() { return wrapper_node_; } |
53 |
|
|
|
54 |
|
5160 |
bool IsRootNode() override { |
55 |
✓✓ |
5160 |
if (retainer_ != nullptr) { |
56 |
|
1676 |
return retainer_->IsRootNode(); |
57 |
|
|
} |
58 |
|
3484 |
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 |
|
|
|
78 |
|
1630 |
void MemoryTracker::TrackFieldWithSize(const char* edge_name, |
79 |
|
|
size_t size, |
80 |
|
|
const char* node_name) { |
81 |
✓✓ |
1630 |
if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name); |
82 |
|
1630 |
} |
83 |
|
|
|
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 |
|
|
|
92 |
|
78 |
void MemoryTracker::TrackField(const char* edge_name, |
93 |
|
|
const MemoryRetainer& value, |
94 |
|
|
const char* node_name) { |
95 |
|
78 |
TrackField(edge_name, &value); |
96 |
|
78 |
} |
97 |
|
|
|
98 |
|
229 |
void MemoryTracker::TrackField(const char* edge_name, |
99 |
|
|
const MemoryRetainer* value, |
100 |
|
|
const char* node_name) { |
101 |
✗✓ |
229 |
if (value == nullptr) return; |
102 |
|
229 |
auto it = seen_.find(value); |
103 |
✗✓ |
229 |
if (it != seen_.end()) { |
104 |
|
|
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
105 |
|
|
} else { |
106 |
|
229 |
Track(value, edge_name); |
107 |
|
|
} |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
template <typename T, typename D> |
111 |
|
58 |
void MemoryTracker::TrackField(const char* edge_name, |
112 |
|
|
const std::unique_ptr<T, D>& value, |
113 |
|
|
const char* node_name) { |
114 |
✓✓ |
58 |
if (value.get() == nullptr) { |
115 |
|
2 |
return; |
116 |
|
|
} |
117 |
|
56 |
TrackField(edge_name, value.get(), node_name); |
118 |
|
|
} |
119 |
|
|
|
120 |
|
|
template <typename T> |
121 |
|
76 |
void MemoryTracker::TrackField(const char* edge_name, |
122 |
|
|
const std::shared_ptr<T>& value, |
123 |
|
|
const char* node_name) { |
124 |
✗✓ |
76 |
if (value.get() == nullptr) { |
125 |
|
|
return; |
126 |
|
|
} |
127 |
|
76 |
TrackField(edge_name, value.get(), node_name); |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
template <typename T, bool kIsWeak> |
131 |
|
1 |
void MemoryTracker::TrackField(const char* edge_name, |
132 |
|
|
const BaseObjectPtrImpl<T, kIsWeak>& value, |
133 |
|
|
const char* node_name) { |
134 |
✗✓✗✓
|
1 |
if (value.get() == nullptr || kIsWeak) return; |
135 |
|
1 |
TrackField(edge_name, value.get(), node_name); |
136 |
|
|
} |
137 |
|
|
|
138 |
|
|
template <typename T, typename Iterator> |
139 |
|
645 |
void MemoryTracker::TrackField(const char* edge_name, |
140 |
|
|
const T& value, |
141 |
|
|
const char* node_name, |
142 |
|
|
const char* element_name, |
143 |
|
|
bool subtract_from_self) { |
144 |
|
|
// If the container is empty, the size has been accounted into the parent's |
145 |
|
|
// self size |
146 |
✗✓✓ |
645 |
if (value.begin() == value.end()) return; |
147 |
|
|
// Fall back to edge name if node names are not provided |
148 |
✓✗✓✗ ✓✗ |
204 |
if (CurrentNode() != nullptr && subtract_from_self) { |
149 |
|
|
// Shift the self size of this container out to a separate node |
150 |
|
204 |
CurrentNode()->size_ -= sizeof(T); |
151 |
|
|
} |
152 |
|
204 |
PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name); |
153 |
✓✓✓ |
6476 |
for (Iterator it = value.begin(); it != value.end(); ++it) { |
154 |
|
|
// Use nullptr as edge names so the elements appear as indexed properties |
155 |
|
6272 |
TrackField(nullptr, *it, element_name); |
156 |
|
|
} |
157 |
|
204 |
PopNode(); |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
template <typename T> |
161 |
|
|
void MemoryTracker::TrackField(const char* edge_name, |
162 |
|
|
const std::queue<T>& value, |
163 |
|
|
const char* node_name, |
164 |
|
|
const char* element_name) { |
165 |
|
|
struct ContainerGetter : public std::queue<T> { |
166 |
|
|
static const typename std::queue<T>::container_type& Get( |
167 |
|
|
const std::queue<T>& value) { |
168 |
|
|
return value.*&ContainerGetter::c; |
169 |
|
|
} |
170 |
|
|
}; |
171 |
|
|
|
172 |
|
|
const auto& container = ContainerGetter::Get(value); |
173 |
|
|
TrackField(edge_name, container, node_name, element_name); |
174 |
|
|
} |
175 |
|
|
|
176 |
|
|
template <typename T, typename test_for_number, typename dummy> |
177 |
|
81 |
void MemoryTracker::TrackField(const char* edge_name, |
178 |
|
|
const T& value, |
179 |
|
|
const char* node_name) { |
180 |
|
|
// For numbers, creating new nodes is not worth the overhead. |
181 |
|
81 |
CurrentNode()->size_ += sizeof(T); |
182 |
|
81 |
} |
183 |
|
|
|
184 |
|
|
template <typename T, typename U> |
185 |
|
|
void MemoryTracker::TrackField(const char* edge_name, |
186 |
|
|
const std::pair<T, U>& value, |
187 |
|
|
const char* node_name) { |
188 |
|
|
PushNode(node_name == nullptr ? "pair" : node_name, |
189 |
|
|
sizeof(const std::pair<T, U>), |
190 |
|
|
edge_name); |
191 |
|
|
// TODO(joyeecheung): special case if one of these is a number type |
192 |
|
|
// that meets the test_for_number trait so that their sizes don't get |
193 |
|
|
// merged into the pair node |
194 |
|
|
TrackField("first", value.first); |
195 |
|
|
TrackField("second", value.second); |
196 |
|
|
PopNode(); |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
template <typename T> |
200 |
|
1541 |
void MemoryTracker::TrackField(const char* edge_name, |
201 |
|
|
const std::basic_string<T>& value, |
202 |
|
|
const char* node_name) { |
203 |
|
1541 |
TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string"); |
204 |
|
1541 |
} |
205 |
|
|
|
206 |
|
|
template <typename T> |
207 |
|
1392 |
void MemoryTracker::TrackField(const char* edge_name, |
208 |
|
|
const v8::Eternal<T>& value, |
209 |
|
|
const char* node_name) { |
210 |
|
1392 |
TrackField(edge_name, value.Get(isolate_)); |
211 |
|
1392 |
} |
212 |
|
|
|
213 |
|
|
template <typename T> |
214 |
|
126 |
void MemoryTracker::TrackField(const char* edge_name, |
215 |
|
|
const v8::PersistentBase<T>& value, |
216 |
|
|
const char* node_name) { |
217 |
✗✓ |
126 |
if (value.IsWeak()) return; |
218 |
✓✓ |
252 |
TrackField(edge_name, value.Get(isolate_)); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
template <typename T> |
222 |
|
20490 |
void MemoryTracker::TrackField(const char* edge_name, |
223 |
|
|
const v8::Local<T>& value, |
224 |
|
|
const char* node_name) { |
225 |
✓✓ |
20490 |
if (!value.IsEmpty()) |
226 |
|
19380 |
graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name); |
227 |
|
20490 |
} |
228 |
|
|
|
229 |
|
|
template <typename T> |
230 |
|
|
void MemoryTracker::TrackField(const char* edge_name, |
231 |
|
|
const MallocedBuffer<T>& value, |
232 |
|
|
const char* node_name) { |
233 |
|
|
TrackFieldWithSize(edge_name, value.size, "MallocedBuffer"); |
234 |
|
|
} |
235 |
|
|
|
236 |
|
|
void MemoryTracker::TrackField(const char* edge_name, |
237 |
|
|
const v8::BackingStore* value, |
238 |
|
|
const char* node_name) { |
239 |
|
|
TrackFieldWithSize(edge_name, value->ByteLength(), "BackingStore"); |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
void MemoryTracker::TrackField(const char* name, |
243 |
|
|
const uv_buf_t& value, |
244 |
|
|
const char* node_name) { |
245 |
|
|
TrackFieldWithSize(name, value.len, "uv_buf_t"); |
246 |
|
|
} |
247 |
|
|
|
248 |
|
|
void MemoryTracker::TrackField(const char* name, |
249 |
|
|
const uv_timer_t& value, |
250 |
|
|
const char* node_name) { |
251 |
|
|
TrackFieldWithSize(name, sizeof(value), "uv_timer_t"); |
252 |
|
|
} |
253 |
|
|
|
254 |
|
|
void MemoryTracker::TrackField(const char* name, |
255 |
|
|
const uv_async_t& value, |
256 |
|
|
const char* node_name) { |
257 |
|
|
TrackFieldWithSize(name, sizeof(value), "uv_async_t"); |
258 |
|
|
} |
259 |
|
|
|
260 |
|
|
void MemoryTracker::TrackInlineField(const char* name, |
261 |
|
|
const uv_async_t& value, |
262 |
|
|
const char* node_name) { |
263 |
|
|
TrackInlineFieldWithSize(name, sizeof(value), "uv_async_t"); |
264 |
|
|
} |
265 |
|
|
|
266 |
|
|
template <class NativeT, class V8T> |
267 |
|
630 |
void MemoryTracker::TrackField(const char* name, |
268 |
|
|
const AliasedBufferBase<NativeT, V8T>& value, |
269 |
|
|
const char* node_name) { |
270 |
|
630 |
TrackField(name, value.GetJSArray(), "AliasedBuffer"); |
271 |
|
630 |
} |
272 |
|
|
|
273 |
|
839 |
void MemoryTracker::Track(const MemoryRetainer* retainer, |
274 |
|
|
const char* edge_name) { |
275 |
|
839 |
v8::HandleScope handle_scope(isolate_); |
276 |
|
839 |
auto it = seen_.find(retainer); |
277 |
✓✓ |
839 |
if (it != seen_.end()) { |
278 |
✗✓ |
1 |
if (CurrentNode() != nullptr) { |
279 |
|
|
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
280 |
|
|
} |
281 |
|
1 |
return; // It has already been tracked, no need to call MemoryInfo again |
282 |
|
|
} |
283 |
|
838 |
MemoryRetainerNode* n = PushNode(retainer, edge_name); |
284 |
|
838 |
retainer->MemoryInfo(this); |
285 |
✗✓ |
838 |
CHECK_EQ(CurrentNode(), n); |
286 |
✗✓ |
838 |
CHECK_NE(n->size_, 0); |
287 |
|
838 |
PopNode(); |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
void 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 |
|
|
|
297 |
|
15401 |
MemoryRetainerNode* MemoryTracker::CurrentNode() const { |
298 |
✓✓ |
15401 |
if (node_stack_.empty()) return nullptr; |
299 |
|
14791 |
return node_stack_.top(); |
300 |
|
|
} |
301 |
|
|
|
302 |
|
838 |
MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer, |
303 |
|
|
const char* edge_name) { |
304 |
|
838 |
auto it = seen_.find(retainer); |
305 |
✗✓ |
838 |
if (it != seen_.end()) { |
306 |
|
|
return it->second; |
307 |
|
|
} |
308 |
|
|
|
309 |
|
838 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer); |
310 |
|
838 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
311 |
|
838 |
seen_[retainer] = n; |
312 |
✓✓ |
838 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
313 |
|
|
|
314 |
✓✓ |
838 |
if (n->JSWrapperNode() != nullptr) { |
315 |
|
586 |
graph_->AddEdge(n, n->JSWrapperNode(), "wrapped"); |
316 |
|
586 |
graph_->AddEdge(n->JSWrapperNode(), n, "wrapper"); |
317 |
|
|
} |
318 |
|
|
|
319 |
|
838 |
return n; |
320 |
|
|
} |
321 |
|
|
|
322 |
|
1742 |
MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name, |
323 |
|
|
size_t size, |
324 |
|
|
const char* edge_name) { |
325 |
|
1742 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size); |
326 |
|
1742 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
327 |
|
|
|
328 |
✓✗ |
1742 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
329 |
|
|
|
330 |
|
1742 |
return n; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
838 |
MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer, |
334 |
|
|
const char* edge_name) { |
335 |
|
838 |
MemoryRetainerNode* n = AddNode(retainer, edge_name); |
336 |
|
838 |
node_stack_.push(n); |
337 |
|
838 |
return n; |
338 |
|
|
} |
339 |
|
|
|
340 |
|
114 |
MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name, |
341 |
|
|
size_t size, |
342 |
|
|
const char* edge_name) { |
343 |
|
114 |
MemoryRetainerNode* n = AddNode(node_name, size, edge_name); |
344 |
|
114 |
node_stack_.push(n); |
345 |
|
114 |
return n; |
346 |
|
|
} |
347 |
|
|
|
348 |
|
952 |
void MemoryTracker::PopNode() { |
349 |
|
952 |
node_stack_.pop(); |
350 |
|
952 |
} |
351 |
|
|
|
352 |
|
|
} // namespace node |
353 |
|
|
|
354 |
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
355 |
|
|
|
356 |
|
|
#endif // SRC_MEMORY_TRACKER_INL_H_ |