Commit 466d157c authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[api] Add API for annotating retainer of a strong global handle.

This adds PersistentBase::AnnotateStrongRetainer(const char*) function.

The annotation is used by the heap snapshot generator to show the edges
from the (Global handles) root to the global handles.

Bug: chromium:811842
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I1a9e3e53a53aeaf2b590709fab8dd4ecf7e8f252
Reviewed-on: https://chromium-review.googlesource.com/916788
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarAlexei Filippov <alph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51358}
parent 8b53b9d9
...@@ -553,6 +553,14 @@ template <class T> class PersistentBase { ...@@ -553,6 +553,14 @@ template <class T> class PersistentBase {
// TODO(dcarney): remove this. // TODO(dcarney): remove this.
V8_INLINE void ClearWeak() { ClearWeak<void>(); } V8_INLINE void ClearWeak() { ClearWeak<void>(); }
/**
* Annotates the strong handle with the given label, which is then used by the
* heap snapshot generator as a name of the edge from the root to the handle.
* The function does not take ownership of the label and assumes that the
* label is valid as long as the handle is valid.
*/
V8_INLINE void AnnotateStrongRetainer(const char* label);
/** /**
* Allows the embedder to tell the v8 garbage collector that a certain object * Allows the embedder to tell the v8 garbage collector that a certain object
* is alive. Only allowed when the embedder is asked to trace its heap by * is alive. Only allowed when the embedder is asked to trace its heap by
...@@ -8018,6 +8026,8 @@ class V8_EXPORT V8 { ...@@ -8018,6 +8026,8 @@ class V8_EXPORT V8 {
WeakCallbackInfo<void>::Callback weak_callback); WeakCallbackInfo<void>::Callback weak_callback);
static void MakeWeak(internal::Object*** location_addr); static void MakeWeak(internal::Object*** location_addr);
static void* ClearWeak(internal::Object** location); static void* ClearWeak(internal::Object** location);
static void AnnotateStrongRetainer(internal::Object** location,
const char* label);
static Value* Eternalize(Isolate* isolate, Value* handle); static Value* Eternalize(Isolate* isolate, Value* handle);
static void RegisterExternallyReferencedObject(internal::Object** object, static void RegisterExternallyReferencedObject(internal::Object** object,
...@@ -9225,6 +9235,12 @@ P* PersistentBase<T>::ClearWeak() { ...@@ -9225,6 +9235,12 @@ P* PersistentBase<T>::ClearWeak() {
V8::ClearWeak(reinterpret_cast<internal::Object**>(this->val_))); V8::ClearWeak(reinterpret_cast<internal::Object**>(this->val_)));
} }
template <class T>
void PersistentBase<T>::AnnotateStrongRetainer(const char* label) {
V8::AnnotateStrongRetainer(reinterpret_cast<internal::Object**>(this->val_),
label);
}
template <class T> template <class T>
void PersistentBase<T>::RegisterExternalReference(Isolate* isolate) const { void PersistentBase<T>::RegisterExternalReference(Isolate* isolate) const {
if (IsEmpty()) return; if (IsEmpty()) return;
......
...@@ -1026,6 +1026,10 @@ void* V8::ClearWeak(i::Object** location) { ...@@ -1026,6 +1026,10 @@ void* V8::ClearWeak(i::Object** location) {
return i::GlobalHandles::ClearWeakness(location); return i::GlobalHandles::ClearWeakness(location);
} }
void V8::AnnotateStrongRetainer(i::Object** location, const char* label) {
i::GlobalHandles::AnnotateStrongRetainer(location, label);
}
void V8::DisposeGlobal(i::Object** location) { void V8::DisposeGlobal(i::Object** location) {
i::GlobalHandles::Destroy(location); i::GlobalHandles::Destroy(location);
} }
......
...@@ -54,7 +54,7 @@ class GlobalHandles::Node { ...@@ -54,7 +54,7 @@ class GlobalHandles::Node {
index_ = 0; index_ = 0;
set_active(false); set_active(false);
set_in_new_space_list(false); set_in_new_space_list(false);
parameter_or_next_free_.next_free = nullptr; data_.next_free = nullptr;
weak_callback_ = nullptr; weak_callback_ = nullptr;
} }
#endif #endif
...@@ -65,7 +65,7 @@ class GlobalHandles::Node { ...@@ -65,7 +65,7 @@ class GlobalHandles::Node {
DCHECK(static_cast<int>(index_) == index); DCHECK(static_cast<int>(index_) == index);
set_state(FREE); set_state(FREE);
set_in_new_space_list(false); set_in_new_space_list(false);
parameter_or_next_free_.next_free = *first_free; data_.next_free = *first_free;
*first_free = this; *first_free = this;
} }
...@@ -75,7 +75,7 @@ class GlobalHandles::Node { ...@@ -75,7 +75,7 @@ class GlobalHandles::Node {
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
set_active(false); set_active(false);
set_state(NORMAL); set_state(NORMAL);
parameter_or_next_free_.parameter = nullptr; data_.parameter = nullptr;
weak_callback_ = nullptr; weak_callback_ = nullptr;
IncreaseBlockUses(); IncreaseBlockUses();
} }
...@@ -100,6 +100,7 @@ class GlobalHandles::Node { ...@@ -100,6 +100,7 @@ class GlobalHandles::Node {
// Object slot accessors. // Object slot accessors.
Object* object() const { return object_; } Object* object() const { return object_; }
Object** location() { return &object_; } Object** location() { return &object_; }
const char* label() { return state() == NORMAL ? data_.label : nullptr; }
Handle<Object> handle() { return Handle<Object>(location()); } Handle<Object> handle() { return Handle<Object>(location()); }
// Wrapper class ID accessors. // Wrapper class ID accessors.
...@@ -185,21 +186,21 @@ class GlobalHandles::Node { ...@@ -185,21 +186,21 @@ class GlobalHandles::Node {
// Callback parameter accessors. // Callback parameter accessors.
void set_parameter(void* parameter) { void set_parameter(void* parameter) {
DCHECK(IsInUse()); DCHECK(IsInUse());
parameter_or_next_free_.parameter = parameter; data_.parameter = parameter;
} }
void* parameter() const { void* parameter() const {
DCHECK(IsInUse()); DCHECK(IsInUse());
return parameter_or_next_free_.parameter; return data_.parameter;
} }
// Accessors for next free node in the free list. // Accessors for next free node in the free list.
Node* next_free() { Node* next_free() {
DCHECK(state() == FREE); DCHECK(state() == FREE);
return parameter_or_next_free_.next_free; return data_.next_free;
} }
void set_next_free(Node* value) { void set_next_free(Node* value) {
DCHECK(state() == FREE); DCHECK(state() == FREE);
parameter_or_next_free_.next_free = value; data_.next_free = value;
} }
void MakeWeak(void* parameter, void MakeWeak(void* parameter,
...@@ -241,6 +242,11 @@ class GlobalHandles::Node { ...@@ -241,6 +242,11 @@ class GlobalHandles::Node {
return p; return p;
} }
void AnnotateStrongRetainer(const char* label) {
DCHECK_EQ(state(), NORMAL);
data_.label = label;
}
void CollectPhantomCallbackData( void CollectPhantomCallbackData(
Isolate* isolate, Isolate* isolate,
std::vector<PendingPhantomCallback>* pending_phantom_callbacks) { std::vector<PendingPhantomCallback>* pending_phantom_callbacks) {
...@@ -346,12 +352,15 @@ class GlobalHandles::Node { ...@@ -346,12 +352,15 @@ class GlobalHandles::Node {
// Handle specific callback - might be a weak reference in disguise. // Handle specific callback - might be a weak reference in disguise.
WeakCallbackInfo<void>::Callback weak_callback_; WeakCallbackInfo<void>::Callback weak_callback_;
// Provided data for callback. In FREE state, this is used for // The meaning of this field depends on node state:
// the free list link. // state == FREE: it stores the next free node pointer.
// state == NORMAL: it stores the strong retainer label.
// otherwise: it stores the parameter for the weak callback.
union { union {
void* parameter;
Node* next_free; Node* next_free;
} parameter_or_next_free_; const char* label;
void* parameter;
} data_;
DISALLOW_COPY_AND_ASSIGN(Node); DISALLOW_COPY_AND_ASSIGN(Node);
}; };
...@@ -447,7 +456,7 @@ void GlobalHandles::Node::IncreaseBlockUses() { ...@@ -447,7 +456,7 @@ void GlobalHandles::Node::IncreaseBlockUses() {
void GlobalHandles::Node::DecreaseBlockUses() { void GlobalHandles::Node::DecreaseBlockUses() {
NodeBlock* node_block = FindBlock(); NodeBlock* node_block = FindBlock();
GlobalHandles* global_handles = node_block->global_handles(); GlobalHandles* global_handles = node_block->global_handles();
parameter_or_next_free_.next_free = global_handles->first_free_; data_.next_free = global_handles->first_free_;
global_handles->first_free_ = this; global_handles->first_free_ = this;
node_block->DecreaseUses(); node_block->DecreaseUses();
global_handles->isolate()->counters()->global_handles()->Decrement(); global_handles->isolate()->counters()->global_handles()->Decrement();
...@@ -579,6 +588,11 @@ void* GlobalHandles::ClearWeakness(Object** location) { ...@@ -579,6 +588,11 @@ void* GlobalHandles::ClearWeakness(Object** location) {
return Node::FromLocation(location)->ClearWeakness(); return Node::FromLocation(location)->ClearWeakness();
} }
void GlobalHandles::AnnotateStrongRetainer(Object** location,
const char* label) {
Node::FromLocation(location)->AnnotateStrongRetainer(label);
}
bool GlobalHandles::IsNearDeath(Object** location) { bool GlobalHandles::IsNearDeath(Object** location) {
return Node::FromLocation(location)->IsNearDeath(); return Node::FromLocation(location)->IsNearDeath();
} }
...@@ -596,7 +610,8 @@ void GlobalHandles::IterateWeakRootsForFinalizers(RootVisitor* v) { ...@@ -596,7 +610,8 @@ void GlobalHandles::IterateWeakRootsForFinalizers(RootVisitor* v) {
DCHECK(!node->IsPhantomCallback()); DCHECK(!node->IsPhantomCallback());
DCHECK(!node->IsPhantomResetHandle()); DCHECK(!node->IsPhantomResetHandle());
// Finalizers need to survive. // Finalizers need to survive.
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location()); v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
} }
} }
} }
...@@ -635,7 +650,8 @@ void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(RootVisitor* v) { ...@@ -635,7 +650,8 @@ void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(RootVisitor* v) {
for (Node* node : new_space_nodes_) { for (Node* node : new_space_nodes_) {
if (node->IsStrongRetainer() || if (node->IsStrongRetainer() ||
(node->IsWeakRetainer() && node->is_active())) { (node->IsWeakRetainer() && node->is_active())) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location()); v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
} }
} }
} }
...@@ -649,7 +665,8 @@ void GlobalHandles::IterateNewSpaceStrongAndDependentRootsAndIdentifyUnmodified( ...@@ -649,7 +665,8 @@ void GlobalHandles::IterateNewSpaceStrongAndDependentRootsAndIdentifyUnmodified(
} }
if (node->IsStrongRetainer() || if (node->IsStrongRetainer() ||
(node->IsWeakRetainer() && node->is_active())) { (node->IsWeakRetainer() && node->is_active())) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location()); v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
} }
} }
} }
...@@ -685,7 +702,8 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForFinalizers( ...@@ -685,7 +702,8 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForFinalizers(
DCHECK(!node->IsPhantomCallback()); DCHECK(!node->IsPhantomCallback());
DCHECK(!node->IsPhantomResetHandle()); DCHECK(!node->IsPhantomResetHandle());
// Finalizers need to survive. // Finalizers need to survive.
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location()); v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
} }
} }
} }
...@@ -712,7 +730,8 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForPhantomHandles( ...@@ -712,7 +730,8 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForPhantomHandles(
} }
} else { } else {
// Node survived and needs to be visited. // Node survived and needs to be visited.
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location()); v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
} }
} }
} }
...@@ -902,7 +921,8 @@ int GlobalHandles::PostGarbageCollectionProcessing( ...@@ -902,7 +921,8 @@ int GlobalHandles::PostGarbageCollectionProcessing(
void GlobalHandles::IterateStrongRoots(RootVisitor* v) { void GlobalHandles::IterateStrongRoots(RootVisitor* v) {
for (NodeIterator it(this); !it.done(); it.Advance()) { for (NodeIterator it(this); !it.done(); it.Advance()) {
if (it.node()->IsStrongRetainer()) { if (it.node()->IsStrongRetainer()) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, it.node()->location()); v->VisitRootPointer(Root::kGlobalHandles, it.node()->label(),
it.node()->location());
} }
} }
} }
...@@ -910,7 +930,8 @@ void GlobalHandles::IterateStrongRoots(RootVisitor* v) { ...@@ -910,7 +930,8 @@ void GlobalHandles::IterateStrongRoots(RootVisitor* v) {
void GlobalHandles::IterateWeakRoots(RootVisitor* v) { void GlobalHandles::IterateWeakRoots(RootVisitor* v) {
for (NodeIterator it(this); !it.done(); it.Advance()) { for (NodeIterator it(this); !it.done(); it.Advance()) {
if (it.node()->IsWeak()) { if (it.node()->IsWeak()) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, it.node()->location()); v->VisitRootPointer(Root::kGlobalHandles, it.node()->label(),
it.node()->location());
} }
} }
} }
...@@ -919,7 +940,8 @@ DISABLE_CFI_PERF ...@@ -919,7 +940,8 @@ DISABLE_CFI_PERF
void GlobalHandles::IterateAllRoots(RootVisitor* v) { void GlobalHandles::IterateAllRoots(RootVisitor* v) {
for (NodeIterator it(this); !it.done(); it.Advance()) { for (NodeIterator it(this); !it.done(); it.Advance()) {
if (it.node()->IsRetainer()) { if (it.node()->IsRetainer()) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, it.node()->location()); v->VisitRootPointer(Root::kGlobalHandles, it.node()->label(),
it.node()->location());
} }
} }
} }
...@@ -928,7 +950,8 @@ DISABLE_CFI_PERF ...@@ -928,7 +950,8 @@ DISABLE_CFI_PERF
void GlobalHandles::IterateAllNewSpaceRoots(RootVisitor* v) { void GlobalHandles::IterateAllNewSpaceRoots(RootVisitor* v) {
for (Node* node : new_space_nodes_) { for (Node* node : new_space_nodes_) {
if (node->IsRetainer()) { if (node->IsRetainer()) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location()); v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
} }
} }
} }
...@@ -939,7 +962,8 @@ void GlobalHandles::IterateNewSpaceRoots(RootVisitor* v, size_t start, ...@@ -939,7 +962,8 @@ void GlobalHandles::IterateNewSpaceRoots(RootVisitor* v, size_t start,
for (size_t i = start; i < end; ++i) { for (size_t i = start; i < end; ++i) {
Node* node = new_space_nodes_[i]; Node* node = new_space_nodes_[i];
if (node->IsRetainer()) { if (node->IsRetainer()) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location()); v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
} }
} }
} }
......
...@@ -77,6 +77,8 @@ class GlobalHandles { ...@@ -77,6 +77,8 @@ class GlobalHandles {
static void MakeWeak(Object*** location_addr); static void MakeWeak(Object*** location_addr);
static void AnnotateStrongRetainer(Object** location, const char* label);
void RecordStats(HeapStats* stats); void RecordStats(HeapStats* stats);
// Returns the current number of handles to global objects. // Returns the current number of handles to global objects.
......
...@@ -2893,6 +2893,36 @@ TEST(EmbedderGraph) { ...@@ -2893,6 +2893,36 @@ TEST(EmbedderGraph) {
CheckEmbedderGraphSnapshot(env->GetIsolate(), snapshot); CheckEmbedderGraphSnapshot(env->GetIsolate(), snapshot);
} }
bool EndsWith(const char* a, const char* b) {
size_t length_a = strlen(a);
size_t length_b = strlen(b);
return (length_a >= length_b) && !strcmp(a + length_a - length_b, b);
}
TEST(StrongHandleAnnotation) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Persistent<v8::Object> handle1, handle2;
handle1.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
handle2.Reset(env->GetIsolate(), v8::Object::New(env->GetIsolate()));
handle1.AnnotateStrongRetainer("my_label");
handle2.AnnotateStrongRetainer("my_label");
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
const v8::HeapSnapshot* snapshot = heap_profiler->TakeHeapSnapshot();
const v8::HeapGraphNode* gc_roots = GetRootChild(snapshot, "(GC roots)");
CHECK(gc_roots);
const v8::HeapGraphNode* global_handles =
GetChildByName(gc_roots, "(Global handles)");
CHECK(global_handles);
int found = 0;
for (int i = 0, count = global_handles->GetChildrenCount(); i < count; ++i) {
const v8::HeapGraphEdge* edge = global_handles->GetChild(i);
v8::String::Utf8Value edge_name(CcTest::isolate(), edge->GetName());
if (EndsWith(*edge_name, "my_label")) ++found;
}
CHECK_EQ(2, found);
}
static inline i::Address ToAddress(int n) { static inline i::Address ToAddress(int n) {
return reinterpret_cast<i::Address>(n); return reinterpret_cast<i::Address>(n);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment