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