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