• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 const char* 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  *     const char* 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   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 const char* MemoryInfoName() const = 0;
123   virtual size_t SelfSize() const = 0;
124 
WrappedObject()125   virtual v8::Local<v8::Object> WrappedObject() const {
126     return v8::Local<v8::Object>();
127   }
128 
IsRootNode()129   virtual bool IsRootNode() const { return false; }
GetDetachedness()130   virtual v8::EmbedderGraph::Node::Detachedness GetDetachedness() const {
131     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 
graph()255   inline v8::EmbedderGraph* graph() { return graph_; }
isolate()256   inline v8::Isolate* isolate() { return isolate_; }
257 
MemoryTracker(v8::Isolate * isolate,v8::EmbedderGraph * graph)258   inline explicit MemoryTracker(v8::Isolate* isolate,
259                                 v8::EmbedderGraph* graph)
260     : 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
288