summaryrefslogtreecommitdiff
path: root/src/memory_tracker-inl.h
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2018-09-23 17:52:09 -0400
committerJoyee Cheung <joyeec9h3@gmail.com>2018-10-04 15:32:30 +0200
commit92fa0fcdb76e2b6cb0040eede97fe3c167c31897 (patch)
tree5016ac6cbf7e5873421788387a5b2e011c31ac56 /src/memory_tracker-inl.h
parentc0c58d5660aeea93c492877894f66dd55771be2e (diff)
downloadandroid-node-v8-92fa0fcdb76e2b6cb0040eede97fe3c167c31897.tar.gz
android-node-v8-92fa0fcdb76e2b6cb0040eede97fe3c167c31897.tar.bz2
android-node-v8-92fa0fcdb76e2b6cb0040eede97fe3c167c31897.zip
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-inl.h')
-rw-r--r--src/memory_tracker-inl.h251
1 files changed, 178 insertions, 73 deletions
diff --git a/src/memory_tracker-inl.h b/src/memory_tracker-inl.h
index 568a4364f9..65bcbccdc0 100644
--- a/src/memory_tracker-inl.h
+++ b/src/memory_tracker-inl.h
@@ -7,23 +7,40 @@
namespace node {
+// Fallback edge_name if node_name is not available, or "" if edge_name
+// is not available either.
+inline const char* GetNodeName(const char* node_name, const char* edge_name) {
+ if (node_name != nullptr) {
+ return node_name;
+ }
+ if (edge_name != nullptr) {
+ return edge_name;
+ }
+ return "";
+}
+
class MemoryRetainerNode : public v8::EmbedderGraph::Node {
public:
- explicit inline MemoryRetainerNode(MemoryTracker* tracker,
- const MemoryRetainer* retainer,
- const char* name)
- : retainer_(retainer) {
- if (retainer_ != nullptr) {
- v8::HandleScope handle_scope(tracker->isolate());
- v8::Local<v8::Object> obj = retainer_->WrappedObject();
- if (!obj.IsEmpty())
- wrapper_node_ = tracker->graph()->V8Node(obj);
+ inline MemoryRetainerNode(MemoryTracker* tracker,
+ const MemoryRetainer* retainer)
+ : retainer_(retainer) {
+ CHECK_NOT_NULL(retainer_);
+ v8::HandleScope handle_scope(tracker->isolate());
+ v8::Local<v8::Object> obj = retainer_->WrappedObject();
+ if (!obj.IsEmpty()) wrapper_node_ = tracker->graph()->V8Node(obj);
+
+ name_ = retainer_->MemoryInfoName();
+ size_ = retainer_->SelfSize();
+ }
- name_ = retainer_->MemoryInfoName();
- }
- if (name_.empty() && name != nullptr) {
- name_ = name;
- }
+ inline MemoryRetainerNode(MemoryTracker* tracker,
+ const char* name,
+ size_t size,
+ bool is_root_node = false)
+ : retainer_(nullptr) {
+ name_ = name;
+ size_ = size;
+ is_root_node_ = is_root_node;
}
const char* Name() override { return name_.c_str(); }
@@ -35,60 +52,90 @@ class MemoryRetainerNode : public v8::EmbedderGraph::Node {
Node* JSWrapperNode() { return wrapper_node_; }
bool IsRootNode() override {
- return retainer_ != nullptr && retainer_->IsRootNode();
+ if (retainer_ != nullptr) {
+ return retainer_->IsRootNode();
+ }
+ return is_root_node_;
}
private:
friend class MemoryTracker;
- Node* wrapper_node_ = nullptr;
+ // If retainer_ is not nullptr, then it must have a wrapper_node_,
+ // and we have
+ // name_ == retainer_->MemoryInfoName()
+ // size_ == retainer_->SelfSize()
+ // is_root_node_ == retainer_->IsRootNode()
const MemoryRetainer* retainer_;
+ Node* wrapper_node_ = nullptr;
+
+ // Otherwise (retainer == nullptr), we set these fields in an ad-hoc way
+ bool is_root_node_ = false;
std::string name_;
size_t size_ = 0;
};
-template <typename T>
-void MemoryTracker::TrackThis(const T* obj) {
- CurrentNode()->size_ = sizeof(T);
-}
-
-void MemoryTracker::TrackFieldWithSize(const char* name, size_t size) {
- if (size > 0)
- AddNode(name)->size_ = size;
+void MemoryTracker::TrackFieldWithSize(const char* edge_name,
+ size_t size,
+ const char* node_name) {
+ if (size > 0) AddNode(GetNodeName(node_name, edge_name), size, edge_name);
}
-void MemoryTracker::TrackField(const char* name, const MemoryRetainer& value) {
- TrackField(name, &value);
+void MemoryTracker::TrackField(const char* edge_name,
+ const MemoryRetainer& value,
+ const char* node_name) {
+ TrackField(edge_name, &value);
}
-void MemoryTracker::TrackField(const char* name, const MemoryRetainer* value) {
- if (track_only_self_ || value == nullptr) return;
+void MemoryTracker::TrackField(const char* edge_name,
+ const MemoryRetainer* value,
+ const char* node_name) {
+ if (value == nullptr) return;
auto it = seen_.find(value);
if (it != seen_.end()) {
- graph_->AddEdge(CurrentNode(), it->second);
+ graph_->AddEdge(CurrentNode(), it->second, edge_name);
} else {
- Track(value, name);
+ Track(value, edge_name);
}
}
template <typename T>
-void MemoryTracker::TrackField(const char* name,
- const std::unique_ptr<T>& value) {
- TrackField(name, value.get());
+void MemoryTracker::TrackField(const char* edge_name,
+ const std::unique_ptr<T>& value,
+ const char* node_name) {
+ if (value.get() == nullptr) {
+ return;
+ }
+ TrackField(edge_name, value.get(), node_name);
}
template <typename T, typename Iterator>
-void MemoryTracker::TrackField(const char* name, const T& value) {
+void MemoryTracker::TrackField(const char* edge_name,
+ const T& value,
+ const char* node_name,
+ const char* element_name,
+ bool subtract_from_self) {
+ // If the container is empty, the size has been accounted into the parent's
+ // self size
if (value.begin() == value.end()) return;
- size_t index = 0;
- PushNode(name);
- for (Iterator it = value.begin(); it != value.end(); ++it)
- TrackField(std::to_string(index++).c_str(), *it);
+ // Fall back to edge name if node names are not provided
+ if (CurrentNode() != nullptr && subtract_from_self) {
+ // Shift the self size of this container out to a separate node
+ CurrentNode()->size_ -= sizeof(T);
+ }
+ PushNode(GetNodeName(node_name, edge_name), sizeof(T), edge_name);
+ for (Iterator it = value.begin(); it != value.end(); ++it) {
+ // Use nullptr as edge names so the elements appear as indexed properties
+ TrackField(nullptr, *it, element_name);
+ }
PopNode();
}
template <typename T>
-void MemoryTracker::TrackField(const char* name, const std::queue<T>& value) {
+void MemoryTracker::TrackField(const char* edge_name,
+ const std::queue<T>& value,
+ const char* node_name,
+ const char* element_name) {
struct ContainerGetter : public std::queue<T> {
static const typename std::queue<T>::container_type& Get(
const std::queue<T>& value) {
@@ -97,61 +144,98 @@ void MemoryTracker::TrackField(const char* name, const std::queue<T>& value) {
};
const auto& container = ContainerGetter::Get(value);
- TrackField(name, container);
+ TrackField(edge_name, container, node_name, element_name);
}
template <typename T, typename test_for_number, typename dummy>
-void MemoryTracker::TrackField(const char* name, const T& value) {
+void MemoryTracker::TrackField(const char* edge_name,
+ const T& value,
+ const char* node_name) {
// For numbers, creating new nodes is not worth the overhead.
CurrentNode()->size_ += sizeof(T);
}
template <typename T, typename U>
-void MemoryTracker::TrackField(const char* name, const std::pair<T, U>& value) {
- PushNode(name);
+void MemoryTracker::TrackField(const char* edge_name,
+ const std::pair<T, U>& value,
+ const char* node_name) {
+ PushNode(node_name == nullptr ? "pair" : node_name,
+ sizeof(const std::pair<T, U>),
+ edge_name);
+ // TODO(joyeecheung): special case if one of these is a number type
+ // that meets the test_for_number trait so that their sizes don't get
+ // merged into the pair node
TrackField("first", value.first);
TrackField("second", value.second);
PopNode();
}
template <typename T>
-void MemoryTracker::TrackField(const char* name,
- const std::basic_string<T>& value) {
- TrackFieldWithSize(name, value.size() * sizeof(T));
+void MemoryTracker::TrackField(const char* edge_name,
+ const std::basic_string<T>& value,
+ const char* node_name) {
+ TrackFieldWithSize(edge_name, value.size() * sizeof(T), "std::basic_string");
}
template <typename T, typename Traits>
-void MemoryTracker::TrackField(const char* name,
- const v8::Persistent<T, Traits>& value) {
- TrackField(name, value.Get(isolate_));
+void MemoryTracker::TrackField(const char* edge_name,
+ const v8::Persistent<T, Traits>& value,
+ const char* node_name) {
+ TrackField(edge_name, value.Get(isolate_));
}
template <typename T>
-void MemoryTracker::TrackField(const char* name, const v8::Local<T>& value) {
+void MemoryTracker::TrackField(const char* edge_name,
+ const v8::Local<T>& value,
+ const char* node_name) {
if (!value.IsEmpty())
- graph_->AddEdge(CurrentNode(), graph_->V8Node(value));
+ graph_->AddEdge(CurrentNode(), graph_->V8Node(value), edge_name);
}
template <typename T>
+void MemoryTracker::TrackField(const char* edge_name,
+ const MallocedBuffer<T>& value,
+ const char* node_name) {
+ TrackFieldWithSize(edge_name, value.size, "MallocedBuffer");
+}
+
+void MemoryTracker::TrackField(const char* name,
+ const uv_buf_t& value,
+ const char* node_name) {
+ TrackFieldWithSize(name, value.len, "uv_buf_t");
+}
+
void MemoryTracker::TrackField(const char* name,
- const MallocedBuffer<T>& value) {
- TrackFieldWithSize(name, value.size);
+ const uv_timer_t& value,
+ const char* node_name) {
+ TrackFieldWithSize(name, sizeof(value), "uv_timer_t");
}
-void MemoryTracker::TrackField(const char* name, const uv_buf_t& value) {
- TrackFieldWithSize(name, value.len);
+void MemoryTracker::TrackField(const char* name,
+ const uv_async_t& value,
+ const char* node_name) {
+ TrackFieldWithSize(name, sizeof(value), "uv_async_t");
}
template <class NativeT, class V8T>
void MemoryTracker::TrackField(const char* name,
- const AliasedBuffer<NativeT, V8T>& value) {
- TrackField(name, value.GetJSArray());
+ const AliasedBuffer<NativeT, V8T>& value,
+ const char* node_name) {
+ TrackField(name, value.GetJSArray(), "AliasedBuffer");
}
-void MemoryTracker::Track(const MemoryRetainer* value, const char* name) {
+void MemoryTracker::Track(const MemoryRetainer* retainer,
+ const char* edge_name) {
v8::HandleScope handle_scope(isolate_);
- MemoryRetainerNode* n = PushNode(name, value);
- value->MemoryInfo(this);
+ auto it = seen_.find(retainer);
+ if (it != seen_.end()) {
+ if (CurrentNode() != nullptr) {
+ graph_->AddEdge(CurrentNode(), it->second, edge_name);
+ }
+ return; // It has already been tracked, no need to call MemoryInfo again
+ }
+ MemoryRetainerNode* n = PushNode(retainer, edge_name);
+ retainer->MemoryInfo(this);
CHECK_EQ(CurrentNode(), n);
CHECK_NE(n->size_, 0);
PopNode();
@@ -162,27 +246,48 @@ MemoryRetainerNode* MemoryTracker::CurrentNode() const {
return node_stack_.top();
}
-MemoryRetainerNode* MemoryTracker::AddNode(
- const char* name, const MemoryRetainer* retainer) {
- MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer, name);
- graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n));
- if (retainer != nullptr)
- seen_[retainer] = n;
+MemoryRetainerNode* MemoryTracker::AddNode(const MemoryRetainer* retainer,
+ const char* edge_name) {
+ auto it = seen_.find(retainer);
+ if (it != seen_.end()) {
+ return it->second;
+ }
- if (CurrentNode() != nullptr)
- graph_->AddEdge(CurrentNode(), n);
+ MemoryRetainerNode* n = new MemoryRetainerNode(this, retainer);
+ graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n));
+ seen_[retainer] = n;
+ if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name);
if (n->JSWrapperNode() != nullptr) {
- graph_->AddEdge(n, n->JSWrapperNode());
- graph_->AddEdge(n->JSWrapperNode(), n);
+ graph_->AddEdge(n, n->JSWrapperNode(), "wrapped");
+ graph_->AddEdge(n->JSWrapperNode(), n, "wrapper");
}
return n;
}
-MemoryRetainerNode* MemoryTracker::PushNode(
- const char* name, const MemoryRetainer* retainer) {
- MemoryRetainerNode* n = AddNode(name, retainer);
+MemoryRetainerNode* MemoryTracker::AddNode(const char* node_name,
+ size_t size,
+ const char* edge_name) {
+ MemoryRetainerNode* n = new MemoryRetainerNode(this, node_name, size);
+ graph_->AddNode(std::unique_ptr<v8::EmbedderGraph::Node>(n));
+
+ if (CurrentNode() != nullptr) graph_->AddEdge(CurrentNode(), n, edge_name);
+
+ return n;
+}
+
+MemoryRetainerNode* MemoryTracker::PushNode(const MemoryRetainer* retainer,
+ const char* edge_name) {
+ MemoryRetainerNode* n = AddNode(retainer, edge_name);
+ node_stack_.push(n);
+ return n;
+}
+
+MemoryRetainerNode* MemoryTracker::PushNode(const char* node_name,
+ size_t size,
+ const char* edge_name) {
+ MemoryRetainerNode* n = AddNode(node_name, size, edge_name);
node_stack_.push(n);
return n;
}