• 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 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   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 
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; }
130 };
131 
132 class MemoryTracker {
133  public:
134   // Used to specify node name and size explicitly
135   inline void TrackFieldWithSize(const char* edge_name,
136                                  size_t size,
137                                  const char* node_name = nullptr);
138   inline void TrackInlineFieldWithSize(const char* edge_name,
139                                        size_t size,
140                                        const char* node_name = nullptr);
141 
142   // Shortcut to extract the underlying object out of the smart pointer
143   template <typename T, typename D>
144   inline void TrackField(const char* edge_name,
145                          const std::unique_ptr<T, D>& value,
146                          const char* node_name = nullptr);
147 
148   template <typename T>
149   inline void TrackField(const char* edge_name,
150                          const std::shared_ptr<T>& value,
151                          const char* node_name = nullptr);
152 
153   template <typename T, bool kIsWeak>
154   void TrackField(const char* edge_name,
155                   const BaseObjectPtrImpl<T, kIsWeak>& value,
156                   const char* node_name = nullptr);
157 
158   // For containers, the elements will be graphed as grandchildren nodes
159   // if the container is not empty.
160   // By default, we assume the parent count the stack size of the container
161   // into its SelfSize so that will be subtracted from the parent size when we
162   // spin off a new node for the container.
163   // TODO(joyeecheung): use RTTI to retrieve the class name at runtime?
164   template <typename T, typename Iterator = typename T::const_iterator>
165   inline void TrackField(const char* edge_name,
166                          const T& value,
167                          const char* node_name = nullptr,
168                          const char* element_name = nullptr,
169                          bool subtract_from_self = true);
170   template <typename T>
171   inline void TrackField(const char* edge_name,
172                          const std::queue<T>& value,
173                          const char* node_name = nullptr,
174                          const char* element_name = nullptr);
175   template <typename T, typename U>
176   inline void TrackField(const char* edge_name,
177                          const std::pair<T, U>& value,
178                          const char* node_name = nullptr);
179 
180   // For the following types, node_name will be ignored and predefined names
181   // will be used instead. They are only in the signature for template
182   // expansion.
183   inline void TrackField(const char* edge_name,
184                          const MemoryRetainer& value,
185                          const char* node_name = nullptr);
186   inline void TrackField(const char* edge_name,
187                          const MemoryRetainer* value,
188                          const char* node_name = nullptr);
189   template <typename T>
190   inline void TrackField(const char* edge_name,
191                          const std::basic_string<T>& value,
192                          const char* node_name = nullptr);
193   template <typename T,
194             typename test_for_number = typename std::
195                 enable_if<std::numeric_limits<T>::is_specialized, bool>::type,
196             typename dummy = bool>
197   inline void TrackField(const char* edge_name,
198                          const T& value,
199                          const char* node_name = nullptr);
200   template <typename T>
201   void TrackField(const char* edge_name,
202                   const v8::Eternal<T>& value,
203                   const char* node_name);
204   template <typename T>
205   inline void TrackField(const char* edge_name,
206                          const v8::PersistentBase<T>& value,
207                          const char* node_name = nullptr);
208   template <typename T>
209   inline void TrackField(const char* edge_name,
210                          const v8::Local<T>& value,
211                          const char* node_name = nullptr);
212   template <typename T>
213   inline void TrackField(const char* edge_name,
214                          const MallocedBuffer<T>& value,
215                          const char* node_name = nullptr);
216   inline void TrackField(const char* edge_name,
217                          const v8::BackingStore* value,
218                          const char* node_name = nullptr);
219   inline void TrackField(const char* edge_name,
220                          const uv_buf_t& value,
221                          const char* node_name = nullptr);
222   inline void TrackField(const char* edge_name,
223                          const uv_timer_t& value,
224                          const char* node_name = nullptr);
225   inline void TrackField(const char* edge_name,
226                          const uv_async_t& value,
227                          const char* node_name = nullptr);
228   inline void TrackInlineField(const char* edge_name,
229                                const uv_async_t& value,
230                                const char* node_name = nullptr);
231   template <class NativeT, class V8T>
232   inline void TrackField(const char* edge_name,
233                          const AliasedBufferBase<NativeT, V8T>& value,
234                          const char* node_name = nullptr);
235 
236   // Put a memory container into the graph, create an edge from
237   // the current node if there is one on the stack.
238   inline void Track(const MemoryRetainer* retainer,
239                     const char* edge_name = nullptr);
240 
241   // Useful for parents that do not wish to perform manual
242   // adjustments to its `SelfSize()` when embedding retainer
243   // objects inline.
244   // Put a memory container into the graph, create an edge from
245   // the current node if there is one on the stack - there should
246   // be one, of the container object which the current field is part of.
247   // Reduce the size of memory from the container so as to avoid
248   // duplication in accounting.
249   inline void TrackInlineField(const MemoryRetainer* retainer,
250                                const char* edge_name = nullptr);
251 
graph()252   inline v8::EmbedderGraph* graph() { return graph_; }
isolate()253   inline v8::Isolate* isolate() { return isolate_; }
254 
MemoryTracker(v8::Isolate * isolate,v8::EmbedderGraph * graph)255   inline explicit MemoryTracker(v8::Isolate* isolate,
256                                 v8::EmbedderGraph* graph)
257     : isolate_(isolate), graph_(graph) {}
258 
259  private:
260   typedef std::unordered_map<const MemoryRetainer*, MemoryRetainerNode*>
261       NodeMap;
262 
263   inline MemoryRetainerNode* CurrentNode() const;
264   inline MemoryRetainerNode* AddNode(const MemoryRetainer* retainer,
265                                      const char* edge_name = nullptr);
266   inline MemoryRetainerNode* PushNode(const MemoryRetainer* retainer,
267                                       const char* edge_name = nullptr);
268   inline MemoryRetainerNode* AddNode(const char* node_name,
269                                      size_t size,
270                                      const char* edge_name = nullptr);
271   inline MemoryRetainerNode* PushNode(const char* node_name,
272                                       size_t size,
273                                       const char* edge_name = nullptr);
274   inline void PopNode();
275 
276   v8::Isolate* isolate_;
277   v8::EmbedderGraph* graph_;
278   std::stack<MemoryRetainerNode*> node_stack_;
279   NodeMap seen_;
280 };
281 
282 }  // namespace node
283 
284 #endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
285