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