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 {
// TODO(dcarney): remove this.
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
* is alive. Only allowed when the embedder is asked to trace its heap by
......@@ -8018,6 +8026,8 @@ class V8_EXPORT V8 {
WeakCallbackInfo<void>::Callback weak_callback);
static void MakeWeak(internal::Object*** location_addr);
static void* ClearWeak(internal::Object** location);
static void AnnotateStrongRetainer(internal::Object** location,
const char* label);
static Value* Eternalize(Isolate* isolate, Value* handle);
static void RegisterExternallyReferencedObject(internal::Object** object,
......@@ -9225,6 +9235,12 @@ P* PersistentBase<T>::ClearWeak() {
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>
void PersistentBase<T>::RegisterExternalReference(Isolate* isolate) const {
if (IsEmpty()) return;
......
......@@ -1026,6 +1026,10 @@ void* V8::ClearWeak(i::Object** 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) {
i::GlobalHandles::Destroy(location);
}
......
......@@ -54,7 +54,7 @@ class GlobalHandles::Node {
index_ = 0;
set_active(false);
set_in_new_space_list(false);
parameter_or_next_free_.next_free = nullptr;
data_.next_free = nullptr;
weak_callback_ = nullptr;
}
#endif
......@@ -65,7 +65,7 @@ class GlobalHandles::Node {
DCHECK(static_cast<int>(index_) == index);
set_state(FREE);
set_in_new_space_list(false);
parameter_or_next_free_.next_free = *first_free;
data_.next_free = *first_free;
*first_free = this;
}
......@@ -75,7 +75,7 @@ class GlobalHandles::Node {
class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
set_active(false);
set_state(NORMAL);
parameter_or_next_free_.parameter = nullptr;
data_.parameter = nullptr;
weak_callback_ = nullptr;
IncreaseBlockUses();
}
......@@ -100,6 +100,7 @@ class GlobalHandles::Node {
// Object slot accessors.
Object* object() const { return object_; }
Object** location() { return &object_; }
const char* label() { return state() == NORMAL ? data_.label : nullptr; }
Handle<Object> handle() { return Handle<Object>(location()); }
// Wrapper class ID accessors.
......@@ -185,21 +186,21 @@ class GlobalHandles::Node {
// Callback parameter accessors.
void set_parameter(void* parameter) {
DCHECK(IsInUse());
parameter_or_next_free_.parameter = parameter;
data_.parameter = parameter;
}
void* parameter() const {
DCHECK(IsInUse());
return parameter_or_next_free_.parameter;
return data_.parameter;
}
// Accessors for next free node in the free list.
Node* next_free() {
DCHECK(state() == FREE);
return parameter_or_next_free_.next_free;
return data_.next_free;
}
void set_next_free(Node* value) {
DCHECK(state() == FREE);
parameter_or_next_free_.next_free = value;
data_.next_free = value;
}
void MakeWeak(void* parameter,
......@@ -241,6 +242,11 @@ class GlobalHandles::Node {
return p;
}
void AnnotateStrongRetainer(const char* label) {
DCHECK_EQ(state(), NORMAL);
data_.label = label;
}
void CollectPhantomCallbackData(
Isolate* isolate,
std::vector<PendingPhantomCallback>* pending_phantom_callbacks) {
......@@ -346,12 +352,15 @@ class GlobalHandles::Node {
// Handle specific callback - might be a weak reference in disguise.
WeakCallbackInfo<void>::Callback weak_callback_;
// Provided data for callback. In FREE state, this is used for
// the free list link.
// The meaning of this field depends on node state:
// 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 {
void* parameter;
Node* next_free;
} parameter_or_next_free_;
const char* label;
void* parameter;
} data_;
DISALLOW_COPY_AND_ASSIGN(Node);
};
......@@ -447,7 +456,7 @@ void GlobalHandles::Node::IncreaseBlockUses() {
void GlobalHandles::Node::DecreaseBlockUses() {
NodeBlock* node_block = FindBlock();
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;
node_block->DecreaseUses();
global_handles->isolate()->counters()->global_handles()->Decrement();
......@@ -579,6 +588,11 @@ void* GlobalHandles::ClearWeakness(Object** location) {
return Node::FromLocation(location)->ClearWeakness();
}
void GlobalHandles::AnnotateStrongRetainer(Object** location,
const char* label) {
Node::FromLocation(location)->AnnotateStrongRetainer(label);
}
bool GlobalHandles::IsNearDeath(Object** location) {
return Node::FromLocation(location)->IsNearDeath();
}
......@@ -596,7 +610,8 @@ void GlobalHandles::IterateWeakRootsForFinalizers(RootVisitor* v) {
DCHECK(!node->IsPhantomCallback());
DCHECK(!node->IsPhantomResetHandle());
// 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) {
for (Node* node : new_space_nodes_) {
if (node->IsStrongRetainer() ||
(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(
}
if (node->IsStrongRetainer() ||
(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(
DCHECK(!node->IsPhantomCallback());
DCHECK(!node->IsPhantomResetHandle());
// 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(
}
} else {
// 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(
void GlobalHandles::IterateStrongRoots(RootVisitor* v) {
for (NodeIterator it(this); !it.done(); it.Advance()) {
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) {
void GlobalHandles::IterateWeakRoots(RootVisitor* v) {
for (NodeIterator it(this); !it.done(); it.Advance()) {
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
void GlobalHandles::IterateAllRoots(RootVisitor* v) {
for (NodeIterator it(this); !it.done(); it.Advance()) {
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
void GlobalHandles::IterateAllNewSpaceRoots(RootVisitor* v) {
for (Node* node : new_space_nodes_) {
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,
for (size_t i = start; i < end; ++i) {
Node* node = new_space_nodes_[i];
if (node->IsRetainer()) {
v->VisitRootPointer(Root::kGlobalHandles, nullptr, node->location());
v->VisitRootPointer(Root::kGlobalHandles, node->label(),
node->location());
}
}
}
......
......@@ -77,6 +77,8 @@ class GlobalHandles {
static void MakeWeak(Object*** location_addr);
static void AnnotateStrongRetainer(Object** location, const char* label);
void RecordStats(HeapStats* stats);
// Returns the current number of handles to global objects.
......
......@@ -2893,6 +2893,36 @@ TEST(EmbedderGraph) {
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) {
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