GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: memory_tracker.h Lines: 10 10 100.0 %
Date: 2022-12-07 04:23:16 Branches: 0 0 - %

Line Branch Exec Source
1
#pragma once
2
3
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
4
5
#include "aliased_buffer.h"
6
#include "v8-profiler.h"
7
8
#include <uv.h>
9
10
#include <limits>
11
#include <queue>
12
#include <stack>
13
#include <string>
14
#include <unordered_map>
15
16
namespace node {
17
18
// Set the node name of a MemoryRetainer to klass
19
#define SET_MEMORY_INFO_NAME(Klass)                                            \
20
  inline std::string MemoryInfoName() const override { return #Klass; }
21
22
// Set the self size of a MemoryRetainer to the stack-allocated size of a
23
// certain class
24
#define SET_SELF_SIZE(Klass)                                                   \
25
  inline size_t SelfSize() const override { return sizeof(Klass); }
26
27
// Used when there is no additional fields to track
28
#define SET_NO_MEMORY_INFO()                                                   \
29
  inline void MemoryInfo(node::MemoryTracker* tracker) const override {}
30
31
class MemoryTracker;
32
class MemoryRetainerNode;
33
template <typename T, bool kIsWeak>
34
class BaseObjectPtrImpl;
35
36
namespace crypto {
37
class NodeBIO;
38
}
39
40
class CleanupHookCallback;
41
42
/* Example:
43
 *
44
 * class ExampleRetainer : public MemoryRetainer {
45
 *   public:
46
 *     // Or use SET_NO_MEMORY_INFO() when there is no additional fields
47
 *     // to track.
48
 *     void MemoryInfo(MemoryTracker* tracker) const override {
49
 *       // Node name and size comes from the MemoryInfoName and SelfSize of
50
 *       // AnotherRetainerClass
51
 *       tracker->TrackField("another_retainer", another_retainer_);
52
 *
53
 *       // Add non_pointer_retainer as a separate node into the graph
54
 *       // and track its memory information recursively.
55
 *       // Note that we need to make sure its size is not accounted in
56
 *       // ExampleRetainer::SelfSize().
57
 *       tracker->TrackField("non_pointer_retainer", &non_pointer_retainer_);
58
 *
59
 *       // Specify node name and size explicitly
60
 *       tracker->TrackFieldWithSize("internal_member",
61
 *                                   internal_member_.size(),
62
 *                                   "InternalClass");
63
 *       // Node name falls back to the edge name,
64
 *       // elements in the container appear as grandchildren nodes
65
 *       tracker->TrackField("vector", vector_);
66
 *       // Node name and size come from the JS object
67
 *       tracker->TrackField("target", target_);
68
 *     }
69
 *
70
 *     // Or use SET_MEMORY_INFO_NAME(ExampleRetainer)
71
 *     std::string MemoryInfoName() const override {
72
 *       return "ExampleRetainer";
73
 *     }
74
 *
75
 *     // Classes that only want to return its sizeof() value can use the
76
 *     // SET_SELF_SIZE(Class) macro instead.
77
 *     size_t SelfSize() const override {
78
 *       // We need to exclude the size of non_pointer_retainer so that
79
 *       // we can track it separately in ExampleRetainer::MemoryInfo().
80
 *       return sizeof(ExampleRetainer) - sizeof(NonPointerRetainerClass);
81
 *     }
82
 *
83
 *     // Note: no need to implement these two methods when implementing
84
 *     // a BaseObject or an AsyncWrap class
85
 *     bool IsRootNode() const override { return !wrapped_.IsWeak(); }
86
 *     v8::Local<v8::Object> WrappedObject() const override {
87
 *       return node::PersistentToLocal::Default(wrapped_);
88
 *     }
89
 *
90
 *   private:
91
 *     AnotherRetainerClass* another_retainer_;
92
 *     NonPointerRetainerClass non_pointer_retainer;
93
 *     InternalClass internal_member_;
94
 *     std::vector<uv_async_t> vector_;
95
 *     v8::Global<Object> target_;
96
 *
97
 *     v8::Global<Object> wrapped_;
98
 * }
99
 *
100
 * This creates the following graph:
101
 *   Node / ExampleRetainer
102
 *    |> another_retainer :: Node / AnotherRetainerClass
103
 *    |> internal_member :: Node / InternalClass
104
 *    |> vector :: Node / vector (elements will be grandchildren)
105
 *        |> [1] :: Node / uv_async_t (uv_async_t has predefined names)
106
 *        |> [2] :: Node / uv_async_t
107
 *        |> ...
108
 *    |> target :: TargetClass (JS class name of the target object)
109
 *    |> wrapped :: WrappedClass (JS class name of the wrapped object)
110
 *        |> wrapper :: Node / ExampleRetainer (back reference)
111
 */
112
class MemoryRetainer {
113
 public:
114
5762808
  virtual ~MemoryRetainer() = default;
115
116
  // Subclasses should implement these methods to provide information
117
  // for the V8 heap snapshot generator.
118
  // The MemoryInfo() method is assumed to be called within a context
119
  // where all the edges start from the node of the current retainer,
120
  // and point to the nodes as specified by tracker->Track* calls.
121
  virtual void MemoryInfo(MemoryTracker* tracker) const = 0;
122
  virtual std::string MemoryInfoName() const = 0;
123
  virtual size_t SelfSize() const = 0;
124
125
505
  virtual v8::Local<v8::Object> WrappedObject() const {
126
505
    return v8::Local<v8::Object>();
127
  }
128
129
860
  virtual bool IsRootNode() const { return false; }
130
505
  virtual v8::EmbedderGraph::Node::Detachedness GetDetachedness() const {
131
505
    return v8::EmbedderGraph::Node::Detachedness::kUnknown;
132
  }
133
};
134
135
class MemoryTracker {
136
 public:
137
  // Used to specify node name and size explicitly
138
  inline void TrackFieldWithSize(const char* edge_name,
139
                                 size_t size,
140
                                 const char* node_name = nullptr);
141
  inline void TrackInlineFieldWithSize(const char* edge_name,
142
                                       size_t size,
143
                                       const char* node_name = nullptr);
144
145
  // Shortcut to extract the underlying object out of the smart pointer
146
  template <typename T, typename D>
147
  inline void TrackField(const char* edge_name,
148
                         const std::unique_ptr<T, D>& value,
149
                         const char* node_name = nullptr);
150
151
  template <typename T>
152
  inline void TrackField(const char* edge_name,
153
                         const std::shared_ptr<T>& value,
154
                         const char* node_name = nullptr);
155
156
  template <typename T, bool kIsWeak>
157
  void TrackField(const char* edge_name,
158
                  const BaseObjectPtrImpl<T, kIsWeak>& value,
159
                  const char* node_name = nullptr);
160
161
  // For containers, the elements will be graphed as grandchildren nodes
162
  // if the container is not empty.
163
  // By default, we assume the parent count the stack size of the container
164
  // into its SelfSize so that will be subtracted from the parent size when we
165
  // spin off a new node for the container.
166
  // TODO(joyeecheung): use RTTI to retrieve the class name at runtime?
167
  template <typename T, typename Iterator = typename T::const_iterator>
168
  inline void TrackField(const char* edge_name,
169
                         const T& value,
170
                         const char* node_name = nullptr,
171
                         const char* element_name = nullptr,
172
                         bool subtract_from_self = true);
173
  template <typename T>
174
  inline void TrackField(const char* edge_name,
175
                         const std::queue<T>& value,
176
                         const char* node_name = nullptr,
177
                         const char* element_name = nullptr);
178
  template <typename T, typename U>
179
  inline void TrackField(const char* edge_name,
180
                         const std::pair<T, U>& value,
181
                         const char* node_name = nullptr);
182
183
  // For the following types, node_name will be ignored and predefined names
184
  // will be used instead. They are only in the signature for template
185
  // expansion.
186
  inline void TrackField(const char* edge_name,
187
                         const MemoryRetainer& value,
188
                         const char* node_name = nullptr);
189
  inline void TrackField(const char* edge_name,
190
                         const MemoryRetainer* value,
191
                         const char* node_name = nullptr);
192
  template <typename T>
193
  inline void TrackField(const char* edge_name,
194
                         const std::basic_string<T>& value,
195
                         const char* node_name = nullptr);
196
  template <typename T,
197
            typename test_for_number = typename std::
198
                enable_if<std::numeric_limits<T>::is_specialized, bool>::type,
199
            typename dummy = bool>
200
  inline void TrackField(const char* edge_name,
201
                         const T& value,
202
                         const char* node_name = nullptr);
203
  template <typename T>
204
  void TrackField(const char* edge_name,
205
                  const v8::Eternal<T>& value,
206
                  const char* node_name);
207
  template <typename T>
208
  inline void TrackField(const char* edge_name,
209
                         const v8::PersistentBase<T>& value,
210
                         const char* node_name = nullptr);
211
  template <typename T>
212
  inline void TrackField(const char* edge_name,
213
                         const v8::Local<T>& value,
214
                         const char* node_name = nullptr);
215
  template <typename T>
216
  inline void TrackField(const char* edge_name,
217
                         const MallocedBuffer<T>& value,
218
                         const char* node_name = nullptr);
219
  inline void TrackField(const char* edge_name,
220
                         const v8::BackingStore* value,
221
                         const char* node_name = nullptr);
222
  inline void TrackField(const char* edge_name,
223
                         const uv_buf_t& value,
224
                         const char* node_name = nullptr);
225
  inline void TrackField(const char* edge_name,
226
                         const uv_timer_t& value,
227
                         const char* node_name = nullptr);
228
  inline void TrackField(const char* edge_name,
229
                         const uv_async_t& value,
230
                         const char* node_name = nullptr);
231
  inline void TrackInlineField(const char* edge_name,
232
                               const uv_async_t& value,
233
                               const char* node_name = nullptr);
234
  template <class NativeT, class V8T>
235
  inline void TrackField(const char* edge_name,
236
                         const AliasedBufferBase<NativeT, V8T>& value,
237
                         const char* node_name = nullptr);
238
239
  // Put a memory container into the graph, create an edge from
240
  // the current node if there is one on the stack.
241
  inline void Track(const MemoryRetainer* retainer,
242
                    const char* edge_name = nullptr);
243
244
  // Useful for parents that do not wish to perform manual
245
  // adjustments to its `SelfSize()` when embedding retainer
246
  // objects inline.
247
  // Put a memory container into the graph, create an edge from
248
  // the current node if there is one on the stack - there should
249
  // be one, of the container object which the current field is part of.
250
  // Reduce the size of memory from the container so as to avoid
251
  // duplication in accounting.
252
  inline void TrackInlineField(const MemoryRetainer* retainer,
253
                               const char* edge_name = nullptr);
254
255
976
  inline v8::EmbedderGraph* graph() { return graph_; }
256
1481
  inline v8::Isolate* isolate() { return isolate_; }
257
258
37
  inline explicit MemoryTracker(v8::Isolate* isolate,
259
                                v8::EmbedderGraph* graph)
260
37
    : isolate_(isolate), graph_(graph) {}
261
262
 private:
263
  typedef std::unordered_map<const MemoryRetainer*, MemoryRetainerNode*>
264
      NodeMap;
265
266
  inline MemoryRetainerNode* CurrentNode() const;
267
  inline MemoryRetainerNode* AddNode(const MemoryRetainer* retainer,
268
                                     const char* edge_name = nullptr);
269
  inline MemoryRetainerNode* PushNode(const MemoryRetainer* retainer,
270
                                      const char* edge_name = nullptr);
271
  inline MemoryRetainerNode* AddNode(const char* node_name,
272
                                     size_t size,
273
                                     const char* edge_name = nullptr);
274
  inline MemoryRetainerNode* PushNode(const char* node_name,
275
                                      size_t size,
276
                                      const char* edge_name = nullptr);
277
  inline void PopNode();
278
279
  v8::Isolate* isolate_;
280
  v8::EmbedderGraph* graph_;
281
  std::stack<MemoryRetainerNode*> node_stack_;
282
  NodeMap seen_;
283
};
284
285
}  // namespace node
286
287
#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS