diff options
author | Joyee Cheung <joyeec9h3@gmail.com> | 2018-09-24 00:52:09 +0300 |
---|---|---|
committer | Joyee Cheung <joyeec9h3@gmail.com> | 2018-10-04 16:32:30 +0300 |
commit | 92fa0fcdb76e2b6cb0040eede97fe3c167c31897 (patch) | |
tree | 5016ac6cbf7e5873421788387a5b2e011c31ac56 /src/memory_tracker.h | |
parent | c0c58d5660aeea93c492877894f66dd55771be2e (diff) |
src: name EmbededderGraph edges and use class names for nodes
This patch:
- Refactors the `MemoryRetainer` API so that the impementer no longer
calls `TrackThis()` that sets the size of node on the top of the
stack, which may be hard to understand. Instead now they implements
`SelfSize()` to provide their self sizes. Also documents
the API in the header.
- Refactors `MemoryTracker` so it calls `MemoryInfoName()` and
`SelfSize()` of `MemoryRetainer` to retrieve info about them, and
separate `node_names` and `edge_names` so the edges can be properly
named with reference names and the nodes can be named with class
names. (Previously the nodes are named with reference names while the
edges are all indexed and appear as array elements).
- Adds `SET_MEMORY_INFO_NAME()`, `SET_SELF_SIZE()` and
`SET_NO_MEMORY_INFO()` convenience macros
- Fixes a few `MemoryInfo` calls in some `MemoryRetainers` to track
their references properly.
- Refactors the heapdump tests to check both node names and edge names,
distinguishing between wrapped JS nodes (without prefixes)
and embedder wrappers (prefixed with `Node / `).
PR-URL: https://github.com/nodejs/node/pull/23072
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'src/memory_tracker.h')
-rw-r--r-- | src/memory_tracker.h | 196 |
1 files changed, 161 insertions, 35 deletions
diff --git a/src/memory_tracker.h b/src/memory_tracker.h index d0f9e0dcad8..17992792128 100644 --- a/src/memory_tracker.h +++ b/src/memory_tracker.h @@ -14,6 +14,19 @@ namespace node { +// Set the node name of a MemoryRetainer to klass +#define SET_MEMORY_INFO_NAME(Klass) \ + inline std::string MemoryInfoName() const override { return #Klass; } + +// Set the self size of a MemoryRetainer to the stack-allocated size of a +// certain class +#define SET_SELF_SIZE(Klass) \ + inline size_t SelfSize() const override { return sizeof(Klass); } + +// Used when there is no additional fields to track +#define SET_NO_MEMORY_INFO() \ + inline void MemoryInfo(node::MemoryTracker* tracker) const override {} + class MemoryTracker; class MemoryRetainerNode; @@ -21,61 +34,169 @@ namespace crypto { class NodeBIO; } +/* Example: + * + * class ExampleRetainer : public MemoryRetainer { + * public: + * // Or use SET_NO_MEMORY_INFO() when there is no additional fields + * // to track. + * void MemoryInfo(MemoryTracker* tracker) const override { + * // Node name and size comes from the MemoryInfoName and SelfSize of + * // AnotherRetainerClass + * tracker->TrackField("another_retainer", another_retainer_); + * // Specify node name and size explicitly + * tracker->TrackFieldWithSize("internal_member", + * internal_member_.size(), + * "InternalClass"); + * // Node name falls back to the edge name, + * // elements in the container appear as grandchildren nodes + * tracker->TrackField("vector", vector_); + * // Node name and size come from the JS object + * tracker->TrackField("target", target_); + * } + * + * // Or use SET_MEMORY_INFO_NAME(ExampleRetainer) + * std::string MemoryInfoName() const override { + * return "ExampleRetainer"; + * } + * + * // Or use SET_SELF_SIZE(ExampleRetainer) + * size_t SelfSize() const override { + * return sizeof(ExampleRetainer); + * } + * + * // Note: no need to implement these two methods when implementing + * // a BaseObject or an AsyncWrap class + * bool IsRootNode() const override { return !wrapped_.IsWeak(); } + * v8::Local<v8::Object> WrappedObject() const override { + * return node::PersistentToLocal(wrapped_); + * } + * private: + * AnotherRetainerClass another_retainer_; + * InternalClass internal_member_; + * std::vector<uv_async_t> vector_; + * node::Persistent<Object> target_; + * + * node::Persistent<Object> wrapped_; + * } + * + * This creates the following graph: + * Node / ExampleRetainer + * |> another_retainer :: Node / AnotherRetainerClass + * |> internal_member :: Node / InternalClass + * |> vector :: Node / vector (elements will be grandchildren) + * |> [1] :: Node / uv_async_t (uv_async_t has predefined names) + * |> [2] :: Node / uv_async_t + * |> ... + * |> target :: TargetClass (JS class name of the target object) + * |> wrapped :: WrappedClass (JS class name of the wrapped object) + * |> wrapper :: Node / ExampleRetainer (back reference) + */ class MemoryRetainer { public: virtual ~MemoryRetainer() {} - // Subclasses should implement this to provide information for heap snapshots. + // Subclasses should implement these methods to provide information + // for the V8 heap snapshot generator. + // The MemoryInfo() method is assumed to be called within a context + // where all the edges start from the node of the current retainer, + // and point to the nodes as specified by tracker->Track* calls. virtual void MemoryInfo(MemoryTracker* tracker) const = 0; + virtual std::string MemoryInfoName() const = 0; + virtual size_t SelfSize() const = 0; virtual v8::Local<v8::Object> WrappedObject() const { return v8::Local<v8::Object>(); } virtual bool IsRootNode() const { return false; } - - virtual std::string MemoryInfoName() const { return std::string(); } }; class MemoryTracker { public: + // Used to specify node name and size explicitly + inline void TrackFieldWithSize(const char* edge_name, + size_t size, + const char* node_name = nullptr); + // Shortcut to extract the underlying object out of the smart pointer template <typename T> - inline void TrackThis(const T* obj); - - inline void TrackFieldWithSize(const char* name, size_t size); - - inline void TrackField(const char* name, const MemoryRetainer& value); - inline void TrackField(const char* name, const MemoryRetainer* value); - template <typename T> - inline void TrackField(const char* name, const std::unique_ptr<T>& value); + inline void TrackField(const char* edge_name, + const std::unique_ptr<T>& value, + const char* node_name = nullptr); + + // For containers, the elements will be graphed as grandchildren nodes + // if the container is not empty. + // By default, we assume the parent count the stack size of the container + // into its SelfSize so that will be subtracted from the parent size when we + // spin off a new node for the container. + // TODO(joyeecheung): use RTTI to retrieve the class name at runtime? template <typename T, typename Iterator = typename T::const_iterator> - inline void TrackField(const char* name, const T& value); + inline void TrackField(const char* edge_name, + const T& value, + const char* node_name = nullptr, + const char* element_name = nullptr, + bool subtract_from_self = true); template <typename T> - inline void TrackField(const char* name, const std::queue<T>& value); - template <typename T> - inline void TrackField(const char* name, const std::basic_string<T>& value); - template <typename T, typename test_for_number = - typename std::enable_if< - std::numeric_limits<T>::is_specialized, bool>::type, - typename dummy = bool> - inline void TrackField(const char* name, const T& value); + inline void TrackField(const char* edge_name, + const std::queue<T>& value, + const char* node_name = nullptr, + const char* element_name = nullptr); template <typename T, typename U> - inline void TrackField(const char* name, const std::pair<T, U>& value); + inline void TrackField(const char* edge_name, + const std::pair<T, U>& value, + const char* node_name = nullptr); + + // For the following types, node_name will be ignored and predefined names + // will be used instead. They are only in the signature for template + // expansion. + inline void TrackField(const char* edge_name, + const MemoryRetainer& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const MemoryRetainer* value, + const char* node_name = nullptr); + template <typename T> + inline void TrackField(const char* edge_name, + const std::basic_string<T>& value, + const char* node_name = nullptr); + template <typename T, + typename test_for_number = typename std:: + enable_if<std::numeric_limits<T>::is_specialized, bool>::type, + typename dummy = bool> + inline void TrackField(const char* edge_name, + const T& value, + const char* node_name = nullptr); template <typename T, typename Traits> - inline void TrackField(const char* name, - const v8::Persistent<T, Traits>& value); + inline void TrackField(const char* edge_name, + const v8::Persistent<T, Traits>& value, + const char* node_name = nullptr); template <typename T> - inline void TrackField(const char* name, const v8::Local<T>& value); + inline void TrackField(const char* edge_name, + const v8::Local<T>& value, + const char* node_name = nullptr); template <typename T> - inline void TrackField(const char* name, const MallocedBuffer<T>& value); - inline void TrackField(const char* name, const uv_buf_t& value); + inline void TrackField(const char* edge_name, + const MallocedBuffer<T>& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const uv_buf_t& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const uv_timer_t& value, + const char* node_name = nullptr); + inline void TrackField(const char* edge_name, + const uv_async_t& value, + const char* node_name = nullptr); template <class NativeT, class V8T> - inline void TrackField(const char* name, - const AliasedBuffer<NativeT, V8T>& value); + inline void TrackField(const char* edge_name, + const AliasedBuffer<NativeT, V8T>& value, + const char* node_name = nullptr); - inline void Track(const MemoryRetainer* value, const char* name = nullptr); + // Put a memory container into the graph, create an edge from + // the current node if there is one on the stack. + inline void Track(const MemoryRetainer* retainer, + const char* edge_name = nullptr); - inline void set_track_only_self(bool value) { track_only_self_ = value; } inline v8::EmbedderGraph* graph() { return graph_; } inline v8::Isolate* isolate() { return isolate_; } @@ -88,13 +209,18 @@ class MemoryTracker { NodeMap; inline MemoryRetainerNode* CurrentNode() const; - inline MemoryRetainerNode* AddNode(const char* name, - const MemoryRetainer* retainer = nullptr); - inline MemoryRetainerNode* PushNode(const char* name, - const MemoryRetainer* retainer = nullptr); + inline MemoryRetainerNode* AddNode(const MemoryRetainer* retainer, + const char* edge_name = nullptr); + inline MemoryRetainerNode* PushNode(const MemoryRetainer* retainer, + const char* edge_name = nullptr); + inline MemoryRetainerNode* AddNode(const char* node_name, + size_t size, + const char* edge_name = nullptr); + inline MemoryRetainerNode* PushNode(const char* node_name, + size_t size, + const char* edge_name = nullptr); inline void PopNode(); - bool track_only_self_ = false; v8::Isolate* isolate_; v8::EmbedderGraph* graph_; std::stack<MemoryRetainerNode*> node_stack_; |