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