GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 |
1505 |
inline const char* GetNodeName(const char* node_name, const char* edge_name) { |
|
13 |
✓✓ | 1505 |
if (node_name != nullptr) { |
14 |
1396 |
return node_name; |
|
15 |
} |
||
16 |
✓✗ | 109 |
if (edge_name != nullptr) { |
17 |
109 |
return edge_name; |
|
18 |
} |
||
19 |
return ""; |
||
20 |
} |
||
21 |
|||
22 |
4002 |
class MemoryRetainerNode : public v8::EmbedderGraph::Node { |
|
23 |
public: |
||
24 |
496 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
|
25 |
const MemoryRetainer* retainer) |
||
26 |
496 |
: retainer_(retainer) { |
|
27 |
✗✓ | 496 |
CHECK_NOT_NULL(retainer_); |
28 |
992 |
v8::HandleScope handle_scope(tracker->isolate()); |
|
29 |
496 |
v8::Local<v8::Object> obj = retainer_->WrappedObject(); |
|
30 |
✓✓ | 877 |
if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj); |
31 |
|||
32 |
496 |
name_ = retainer_->MemoryInfoName(); |
|
33 |
496 |
size_ = retainer_->SelfSize(); |
|
34 |
496 |
} |
|
35 |
|||
36 |
1505 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
|
37 |
const char* name, |
||
38 |
size_t size, |
||
39 |
bool is_root_node = false) |
||
40 |
1505 |
: retainer_(nullptr) { |
|
41 |
1505 |
name_ = name; |
|
42 |
1505 |
size_ = size; |
|
43 |
1505 |
is_root_node_ = is_root_node; |
|
44 |
1505 |
} |
|
45 |
|||
46 |
2001 |
const char* Name() override { return name_.c_str(); } |
|
47 |
2001 |
const char* NamePrefix() override { return "Node /"; } |
|
48 |
2001 |
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 |
1258 |
Node* JSWrapperNode() { return wrapper_node_; } |
|
53 |
|||
54 |
4002 |
bool IsRootNode() override { |
|
55 |
✓✓ | 4002 |
if (retainer_ != nullptr) { |
56 |
992 |
return retainer_->IsRootNode(); |
|
57 |
} |
||
58 |
3010 |
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 |
1441 |
void MemoryTracker::TrackFieldWithSize(const char* edge_name, |
|
79 |
size_t size, |
||
80 |
const char* node_name) { |
||
81 |
✓✓ | 1441 |
if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name); |
82 |
1441 |
} |
|
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 |
69 |
void MemoryTracker::TrackField(const char* edge_name, |
|
93 |
const MemoryRetainer& value, |
||
94 |
const char* node_name) { |
||
95 |
69 |
TrackField(edge_name, &value); |
|
96 |
69 |
} |
|
97 |
|||
98 |
97 |
void MemoryTracker::TrackField(const char* edge_name, |
|
99 |
const MemoryRetainer* value, |
||
100 |
const char* node_name) { |
||
101 |
✗✓ | 97 |
if (value == nullptr) return; |
102 |
97 |
auto it = seen_.find(value); |
|
103 |
✓✓ | 97 |
if (it != seen_.end()) { |
104 |
2 |
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
|
105 |
} else { |
||
106 |
95 |
Track(value, edge_name); |
|
107 |
} |
||
108 |
} |
||
109 |
|||
110 |
template <typename T, typename D> |
||
111 |
4 |
void MemoryTracker::TrackField(const char* edge_name, |
|
112 |
const std::unique_ptr<T, D>& value, |
||
113 |
const char* node_name) { |
||
114 |
✓✗✗✓ |
4 |
if (value.get() == nullptr) { |
115 |
2 |
return; |
|
116 |
} |
||
117 |
2 |
TrackField(edge_name, value.get(), node_name); |
|
118 |
} |
||
119 |
|||
120 |
template <typename T> |
||
121 |
void MemoryTracker::TrackField(const char* edge_name, |
||
122 |
const std::shared_ptr<T>& value, |
||
123 |
const char* node_name) { |
||
124 |
if (value.get() == nullptr) { |
||
125 |
return; |
||
126 |
} |
||
127 |
TrackField(edge_name, value.get(), node_name); |
||
128 |
} |
||
129 |
|||
130 |
template <typename T, bool kIsWeak> |
||
131 |
4 |
void MemoryTracker::TrackField(const char* edge_name, |
|
132 |
const BaseObjectPtrImpl<T, kIsWeak>& value, |
||
133 |
const char* node_name) { |
||
134 |
✗✓✗✓ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ |
4 |
if (value.get() == nullptr || kIsWeak) return; |
135 |
4 |
TrackField(edge_name, value.get(), node_name); |
|
136 |
} |
||
137 |
|||
138 |
template <typename T, typename Iterator> |
||
139 |
342 |
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 |
✓✓✓✓ ✓✓✗✓ ✗✓✗✓ ✗ |
342 |
if (value.begin() == value.end()) return; |
147 |
// Fall back to edge name if node names are not provided |
||
148 |
✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ |
77 |
if (CurrentNode() != nullptr && subtract_from_self) { |
149 |
// Shift the self size of this container out to a separate node |
||
150 |
77 |
CurrentNode()->size_ -= sizeof(T); |
|
151 |
} |
||
152 |
77 |
PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name); |
|
153 |
✗✗✓✓ ✓✓✓✓ ✓✓✗✗ ✗ |
2845 |
for (Iterator it = value.begin(); it != value.end(); ++it) { |
154 |
// Use nullptr as edge names so the elements appear as indexed properties |
||
155 |
2768 |
TrackField(nullptr, *it, element_name); |
|
156 |
} |
||
157 |
77 |
PopNode(); |
|
158 |
} |
||
159 |
|||
160 |
template <typename T> |
||
161 |
8 |
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 |
8 |
static const typename std::queue<T>::container_type& Get( |
|
167 |
const std::queue<T>& value) { |
||
168 |
8 |
return value.*&ContainerGetter::c; |
|
169 |
} |
||
170 |
}; |
||
171 |
|||
172 |
8 |
const auto& container = ContainerGetter::Get(value); |
|
173 |
8 |
TrackField(edge_name, container, node_name, element_name); |
|
174 |
8 |
} |
|
175 |
|||
176 |
template <typename T, typename test_for_number, typename dummy> |
||
177 |
79 |
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 |
79 |
CurrentNode()->size_ += sizeof(T); |
|
182 |
79 |
} |
|
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 |
1343 |
void MemoryTracker::TrackField(const char* edge_name, |
|
201 |
const std::basic_string<T>& value, |
||
202 |
const char* node_name) { |
||
203 |
1343 |
TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string"); |
|
204 |
1343 |
} |
|
205 |
|||
206 |
template <typename T> |
||
207 |
1342 |
void MemoryTracker::TrackField(const char* edge_name, |
|
208 |
const v8::Eternal<T>& value, |
||
209 |
const char* node_name) { |
||
210 |
2684 |
TrackField(edge_name, value.Get(isolate_)); |
|
211 |
1342 |
} |
|
212 |
|||
213 |
template <typename T> |
||
214 |
30 |
void MemoryTracker::TrackField(const char* edge_name, |
|
215 |
const v8::PersistentBase<T>& value, |
||
216 |
const char* node_name) { |
||
217 |
✗✓✗✗ |
30 |
if (value.IsWeak()) return; |
218 |
60 |
TrackField(edge_name, value.Get(isolate_)); |
|
219 |
} |
||
220 |
|||
221 |
template <typename T> |
||
222 |
8645 |
void MemoryTracker::TrackField(const char* edge_name, |
|
223 |
const v8::Local<T>& value, |
||
224 |
const char* node_name) { |
||
225 |
✓✗✓✗ ✓✗✓✗ ✓✓✓✓ ✓✗✓✗ |
8645 |
if (!value.IsEmpty()) |
226 |
16442 |
graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name); |
|
227 |
8645 |
} |
|
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 |
255 |
void MemoryTracker::TrackField(const char* name, |
|
268 |
const AliasedBufferBase<NativeT, V8T>& value, |
||
269 |
const char* node_name) { |
||
270 |
255 |
TrackField(name, value.GetJSArray(), "AliasedBuffer"); |
|
271 |
255 |
} |
|
272 |
|||
273 |
498 |
void MemoryTracker::Track(const MemoryRetainer* retainer, |
|
274 |
const char* edge_name) { |
||
275 |
994 |
v8::HandleScope handle_scope(isolate_); |
|
276 |
498 |
auto it = seen_.find(retainer); |
|
277 |
✓✓ | 498 |
if (it != seen_.end()) { |
278 |
✗✓ | 2 |
if (CurrentNode() != nullptr) { |
279 |
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
||
280 |
} |
||
281 |
2 |
return; // It has already been tracked, no need to call MemoryInfo again |
|
282 |
} |
||
283 |
496 |
MemoryRetainerNode* n = PushNode(retainer, edge_name); |
|
284 |
496 |
retainer->MemoryInfo(this); |
|
285 |
✗✓ | 496 |
CHECK_EQ(CurrentNode(), n); |
286 |
✗✓ | 496 |
CHECK_NE(n->size_, 0); |
287 |
✓✓ | 496 |
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 |
12555 |
MemoryRetainerNode* MemoryTracker::CurrentNode() const { |
|
298 |
✓✓ | 12555 |
if (node_stack_.empty()) return nullptr; |
299 |
12152 |
return node_stack_.top(); |
|
300 |
} |
||
301 |
|||
302 |
496 |
MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer, |
|
303 |
const char* edge_name) { |
||
304 |
496 |
auto it = seen_.find(retainer); |
|
305 |
✗✓ | 496 |
if (it != seen_.end()) { |
306 |
return it->second; |
||
307 |
} |
||
308 |
|||
309 |
496 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer); |
|
310 |
496 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
|
311 |
496 |
seen_[retainer] = n; |
|
312 |
✓✓ | 496 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
313 |
|||
314 |
✓✓ | 496 |
if (n->JSWrapperNode() != nullptr) { |
315 |
381 |
graph_->AddEdge(n, n->JSWrapperNode(), "wrapped"); |
|
316 |
381 |
graph_->AddEdge(n->JSWrapperNode(), n, "wrapper"); |
|
317 |
} |
||
318 |
|||
319 |
496 |
return n; |
|
320 |
} |
||
321 |
|||
322 |
1505 |
MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name, |
|
323 |
size_t size, |
||
324 |
const char* edge_name) { |
||
325 |
1505 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size); |
|
326 |
1505 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
|
327 |
|||
328 |
✓✗ | 1505 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
329 |
|||
330 |
1505 |
return n; |
|
331 |
} |
||
332 |
|||
333 |
496 |
MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer, |
|
334 |
const char* edge_name) { |
||
335 |
496 |
MemoryRetainerNode* n = AddNode(retainer, edge_name); |
|
336 |
496 |
node_stack_.push(n); |
|
337 |
496 |
return n; |
|
338 |
} |
||
339 |
|||
340 |
77 |
MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name, |
|
341 |
size_t size, |
||
342 |
const char* edge_name) { |
||
343 |
77 |
MemoryRetainerNode* n = AddNode(node_name, size, edge_name); |
|
344 |
77 |
node_stack_.push(n); |
|
345 |
77 |
return n; |
|
346 |
} |
||
347 |
|||
348 |
573 |
void MemoryTracker::PopNode() { |
|
349 |
573 |
node_stack_.pop(); |
|
350 |
573 |
} |
|
351 |
|||
352 |
} // namespace node |
||
353 |
|||
354 |
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
||
355 |
|||
356 |
#endif // SRC_MEMORY_TRACKER_INL_H_ |
Generated by: GCOVR (Version 3.4) |