• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 namespace v8 {
16 class BackingStore;
17 }
18 
19 namespace node {
20 
21 template <typename T>
22 struct 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 
37 class MemoryTracker;
38 class MemoryRetainerNode;
39 template <typename T, bool kIsWeak>
40 class BaseObjectPtrImpl;
41 
42 namespace crypto {
43 class NodeBIO;
44 }
45 
46 class 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  */
118 class 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 
WrappedObject()131   virtual v8::Local<v8::Object> WrappedObject() const {
132     return v8::Local<v8::Object>();
133   }
134 
IsRootNode()135   virtual bool IsRootNode() const { return false; }
GetDetachedness()136   virtual v8::EmbedderGraph::Node::Detachedness GetDetachedness() const {
137     return v8::EmbedderGraph::Node::Detachedness::kUnknown;
138   }
139 };
140 
141 class 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 
graph()257   inline v8::EmbedderGraph* graph() { return graph_; }
isolate()258   inline v8::Isolate* isolate() { return isolate_; }
259 
MemoryTracker(v8::Isolate * isolate,v8::EmbedderGraph * graph)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