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