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