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 |
4929 |
inline const char* GetNodeName(const char* node_name, const char* edge_name) { |
|
13 |
✓✓ | 4929 |
if (node_name != nullptr) { |
14 |
4670 |
return node_name; |
|
15 |
} |
||
16 |
✓✗ | 259 |
if (edge_name != nullptr) { |
17 |
259 |
return edge_name; |
|
18 |
} |
||
19 |
return ""; |
||
20 |
} |
||
21 |
|||
22 |
✗✓ | 11926 |
class MemoryRetainerNode : public v8::EmbedderGraph::Node { |
23 |
public: |
||
24 |
681 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
|
25 |
const MemoryRetainer* retainer) |
||
26 |
681 |
: retainer_(retainer) { |
|
27 |
✗✓ | 681 |
CHECK_NOT_NULL(retainer_); |
28 |
681 |
v8::HandleScope handle_scope(tracker->isolate()); |
|
29 |
681 |
v8::Local<v8::Object> obj = retainer_->WrappedObject(); |
|
30 |
✓✓ | 1032 |
if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj); |
31 |
|||
32 |
681 |
name_ = retainer_->MemoryInfoName(); |
|
33 |
681 |
size_ = retainer_->SelfSize(); |
|
34 |
681 |
} |
|
35 |
|||
36 |
5282 |
inline MemoryRetainerNode(MemoryTracker* tracker, |
|
37 |
const char* name, |
||
38 |
size_t size, |
||
39 |
bool is_root_node = false) |
||
40 |
5282 |
: retainer_(nullptr) { |
|
41 |
5282 |
name_ = name; |
|
42 |
5282 |
size_ = size; |
|
43 |
5282 |
is_root_node_ = is_root_node; |
|
44 |
5282 |
} |
|
45 |
|||
46 |
5963 |
const char* Name() override { return name_.c_str(); } |
|
47 |
7887 |
const char* NamePrefix() override { return "Node /"; } |
|
48 |
5963 |
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 |
1383 |
Node* JSWrapperNode() { return wrapper_node_; } |
|
53 |
|||
54 |
10002 |
bool IsRootNode() override { |
|
55 |
✓✓ | 10002 |
if (retainer_ != nullptr) { |
56 |
1133 |
return retainer_->IsRootNode(); |
|
57 |
} |
||
58 |
8869 |
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 |
4709 |
void MemoryTracker::TrackFieldWithSize(const char* edge_name, |
|
79 |
size_t size, |
||
80 |
const char* node_name) { |
||
81 |
✓✓ | 4709 |
if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name); |
82 |
4709 |
} |
|
83 |
|||
84 |
210 |
void MemoryTracker::TrackField(const char* edge_name, |
|
85 |
const MemoryRetainer& value, |
||
86 |
const char* node_name) { |
||
87 |
210 |
TrackField(edge_name, &value); |
|
88 |
210 |
} |
|
89 |
|||
90 |
646 |
void MemoryTracker::TrackField(const char* edge_name, |
|
91 |
const MemoryRetainer* value, |
||
92 |
const char* node_name) { |
||
93 |
✗✓ | 1292 |
if (value == nullptr) return; |
94 |
646 |
auto it = seen_.find(value); |
|
95 |
✓✓ | 646 |
if (it != seen_.end()) { |
96 |
20 |
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
|
97 |
} else { |
||
98 |
626 |
Track(value, edge_name); |
|
99 |
} |
||
100 |
} |
||
101 |
|||
102 |
template <typename T> |
||
103 |
24 |
void MemoryTracker::TrackField(const char* edge_name, |
|
104 |
const std::unique_ptr<T>& value, |
||
105 |
const char* node_name) { |
||
106 |
✓✓✓✗ |
24 |
if (value.get() == nullptr) { |
107 |
32 |
return; |
|
108 |
} |
||
109 |
16 |
TrackField(edge_name, value.get(), node_name); |
|
110 |
} |
||
111 |
|||
112 |
template <typename T, typename Iterator> |
||
113 |
380 |
void MemoryTracker::TrackField(const char* edge_name, |
|
114 |
const T& value, |
||
115 |
const char* node_name, |
||
116 |
const char* element_name, |
||
117 |
bool subtract_from_self) { |
||
118 |
// If the container is empty, the size has been accounted into the parent's |
||
119 |
// self size |
||
120 |
✓✓✓✓ ✓✓✗✓ ✗✓✓✓ ✓✗ |
760 |
if (value.begin() == value.end()) return; |
121 |
// Fall back to edge name if node names are not provided |
||
122 |
✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✓✗ ✓✗✗✗ ✗✗✗✗ |
257 |
if (CurrentNode() != nullptr && subtract_from_self) { |
123 |
// Shift the self size of this container out to a separate node |
||
124 |
257 |
CurrentNode()->size_ -= sizeof(T); |
|
125 |
} |
||
126 |
257 |
PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name); |
|
127 |
✗✗✓✓ ✓✓✓✓ ✓✓✗✗ ✓✓✓✓ |
7443 |
for (Iterator it = value.begin(); it != value.end(); ++it) { |
128 |
// Use nullptr as edge names so the elements appear as indexed properties |
||
129 |
7186 |
TrackField(nullptr, *it, element_name); |
|
130 |
} |
||
131 |
257 |
PopNode(); |
|
132 |
} |
||
133 |
|||
134 |
template <typename T> |
||
135 |
22 |
void MemoryTracker::TrackField(const char* edge_name, |
|
136 |
const std::queue<T>& value, |
||
137 |
const char* node_name, |
||
138 |
const char* element_name) { |
||
139 |
struct ContainerGetter : public std::queue<T> { |
||
140 |
22 |
static const typename std::queue<T>::container_type& Get( |
|
141 |
const std::queue<T>& value) { |
||
142 |
22 |
return value.*&ContainerGetter::c; |
|
143 |
} |
||
144 |
}; |
||
145 |
|||
146 |
22 |
const auto& container = ContainerGetter::Get(value); |
|
147 |
22 |
TrackField(edge_name, container, node_name, element_name); |
|
148 |
22 |
} |
|
149 |
|||
150 |
template <typename T, typename test_for_number, typename dummy> |
||
151 |
83 |
void MemoryTracker::TrackField(const char* edge_name, |
|
152 |
const T& value, |
||
153 |
const char* node_name) { |
||
154 |
// For numbers, creating new nodes is not worth the overhead. |
||
155 |
83 |
CurrentNode()->size_ += sizeof(T); |
|
156 |
83 |
} |
|
157 |
|||
158 |
template <typename T, typename U> |
||
159 |
4 |
void MemoryTracker::TrackField(const char* edge_name, |
|
160 |
const std::pair<T, U>& value, |
||
161 |
const char* node_name) { |
||
162 |
4 |
PushNode(node_name == nullptr ? "pair" : node_name, |
|
163 |
sizeof(const std::pair<T, U>), |
||
164 |
✗✓ | 4 |
edge_name); |
165 |
// TODO(joyeecheung): special case if one of these is a number type |
||
166 |
// that meets the test_for_number trait so that their sizes don't get |
||
167 |
// merged into the pair node |
||
168 |
4 |
TrackField("first", value.first); |
|
169 |
4 |
TrackField("second", value.second); |
|
170 |
4 |
PopNode(); |
|
171 |
4 |
} |
|
172 |
|||
173 |
template <typename T> |
||
174 |
4500 |
void MemoryTracker::TrackField(const char* edge_name, |
|
175 |
const std::basic_string<T>& value, |
||
176 |
const char* node_name) { |
||
177 |
4500 |
TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string"); |
|
178 |
4500 |
} |
|
179 |
|||
180 |
template <typename T> |
||
181 |
2244 |
void MemoryTracker::TrackField(const char* edge_name, |
|
182 |
const v8::Eternal<T>& value, |
||
183 |
const char* node_name) { |
||
184 |
4488 |
TrackField(edge_name, value.Get(isolate_)); |
|
185 |
2244 |
} |
|
186 |
|||
187 |
template <typename T> |
||
188 |
2 |
void MemoryTracker::TrackField(const char* edge_name, |
|
189 |
const v8::PersistentBase<T>& value, |
||
190 |
const char* node_name) { |
||
191 |
4 |
TrackField(edge_name, value.Get(isolate_)); |
|
192 |
2 |
} |
|
193 |
|||
194 |
template <typename T> |
||
195 |
15917 |
void MemoryTracker::TrackField(const char* edge_name, |
|
196 |
const v8::Local<T>& value, |
||
197 |
const char* node_name) { |
||
198 |
✓✗✓✗ ✓✗✓✗ ✓✗✓✓ ✓✓✓✗ ✓✗ |
15917 |
if (!value.IsEmpty()) |
199 |
29982 |
graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name); |
|
200 |
15917 |
} |
|
201 |
|||
202 |
template <typename T> |
||
203 |
void MemoryTracker::TrackField(const char* edge_name, |
||
204 |
const MallocedBuffer<T>& value, |
||
205 |
const char* node_name) { |
||
206 |
TrackFieldWithSize(edge_name, value.size, "MallocedBuffer"); |
||
207 |
} |
||
208 |
|||
209 |
2 |
void MemoryTracker::TrackField(const char* name, |
|
210 |
const uv_buf_t& value, |
||
211 |
const char* node_name) { |
||
212 |
2 |
TrackFieldWithSize(name, value.len, "uv_buf_t"); |
|
213 |
2 |
} |
|
214 |
|||
215 |
2 |
void MemoryTracker::TrackField(const char* name, |
|
216 |
const uv_timer_t& value, |
||
217 |
const char* node_name) { |
||
218 |
2 |
TrackFieldWithSize(name, sizeof(value), "uv_timer_t"); |
|
219 |
2 |
} |
|
220 |
|||
221 |
55 |
void MemoryTracker::TrackField(const char* name, |
|
222 |
const uv_async_t& value, |
||
223 |
const char* node_name) { |
||
224 |
55 |
TrackFieldWithSize(name, sizeof(value), "uv_async_t"); |
|
225 |
55 |
} |
|
226 |
|||
227 |
template <class NativeT, class V8T> |
||
228 |
462 |
void MemoryTracker::TrackField(const char* name, |
|
229 |
const AliasedBufferBase<NativeT, V8T>& value, |
||
230 |
const char* node_name) { |
||
231 |
462 |
TrackField(name, value.GetJSArray(), "AliasedBuffer"); |
|
232 |
462 |
} |
|
233 |
|||
234 |
681 |
void MemoryTracker::Track(const MemoryRetainer* retainer, |
|
235 |
const char* edge_name) { |
||
236 |
681 |
v8::HandleScope handle_scope(isolate_); |
|
237 |
681 |
auto it = seen_.find(retainer); |
|
238 |
✗✓ | 681 |
if (it != seen_.end()) { |
239 |
if (CurrentNode() != nullptr) { |
||
240 |
graph_->AddEdge(CurrentNode(), it->second, edge_name); |
||
241 |
} |
||
242 |
681 |
return; // It has already been tracked, no need to call MemoryInfo again |
|
243 |
} |
||
244 |
681 |
MemoryRetainerNode* n = PushNode(retainer, edge_name); |
|
245 |
681 |
retainer->MemoryInfo(this); |
|
246 |
✗✓ | 681 |
CHECK_EQ(CurrentNode(), n); |
247 |
✗✓ | 681 |
CHECK_NE(n->size_, 0); |
248 |
✓✗ | 681 |
PopNode(); |
249 |
} |
||
250 |
|||
251 |
4 |
void MemoryTracker::TrackInlineField(const MemoryRetainer* retainer, |
|
252 |
const char* edge_name) { |
||
253 |
4 |
Track(retainer, edge_name); |
|
254 |
✗✓ | 4 |
CHECK(CurrentNode()); |
255 |
4 |
CurrentNode()->size_ -= retainer->SelfSize(); |
|
256 |
4 |
} |
|
257 |
|||
258 |
28521 |
MemoryRetainerNode* MemoryTracker::CurrentNode() const { |
|
259 |
✓✓ | 28521 |
if (node_stack_.empty()) return nullptr; |
260 |
28470 |
return node_stack_.top(); |
|
261 |
} |
||
262 |
|||
263 |
681 |
MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer, |
|
264 |
const char* edge_name) { |
||
265 |
681 |
auto it = seen_.find(retainer); |
|
266 |
✗✓ | 681 |
if (it != seen_.end()) { |
267 |
return it->second; |
||
268 |
} |
||
269 |
|||
270 |
681 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer); |
|
271 |
681 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
|
272 |
681 |
seen_[retainer] = n; |
|
273 |
✓✓ | 681 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
274 |
|||
275 |
✓✓ | 681 |
if (n->JSWrapperNode() != nullptr) { |
276 |
351 |
graph_->AddEdge(n, n->JSWrapperNode(), "wrapped"); |
|
277 |
351 |
graph_->AddEdge(n->JSWrapperNode(), n, "wrapper"); |
|
278 |
} |
||
279 |
|||
280 |
681 |
return n; |
|
281 |
} |
||
282 |
|||
283 |
5282 |
MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name, |
|
284 |
size_t size, |
||
285 |
const char* edge_name) { |
||
286 |
5282 |
MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size); |
|
287 |
5282 |
graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n)); |
|
288 |
|||
289 |
✓✗ | 5282 |
if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name); |
290 |
|||
291 |
5282 |
return n; |
|
292 |
} |
||
293 |
|||
294 |
681 |
MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer, |
|
295 |
const char* edge_name) { |
||
296 |
681 |
MemoryRetainerNode* n = AddNode(retainer, edge_name); |
|
297 |
681 |
node_stack_.push(n); |
|
298 |
681 |
return n; |
|
299 |
} |
||
300 |
|||
301 |
610 |
MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name, |
|
302 |
size_t size, |
||
303 |
const char* edge_name) { |
||
304 |
610 |
MemoryRetainerNode* n = AddNode(node_name, size, edge_name); |
|
305 |
610 |
node_stack_.push(n); |
|
306 |
610 |
return n; |
|
307 |
} |
||
308 |
|||
309 |
1291 |
void MemoryTracker::PopNode() { |
|
310 |
1291 |
node_stack_.pop(); |
|
311 |
1291 |
} |
|
312 |
|||
313 |
} // namespace node |
||
314 |
|||
315 |
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
||
316 |
|||
317 |
#endif // SRC_MEMORY_TRACKER_INL_H_ |
Generated by: GCOVR (Version 3.4) |