Distinguish weak references in heap snapshots, group GC roots.
Several changes to better organize snapshot data: 1. Provide information about weak references. 2. Group (GC roots) children. 3. Prettify debug snapshot printing. BUG=v8:1832 TEST=cctest/test-heap-profiler/*Weak* Review URL: http://codereview.chromium.org/8716009 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10158 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1bfa622043
commit
857aa09772
@ -219,8 +219,9 @@ class V8EXPORT HeapGraphEdge {
|
||||
// (e.g. parts of a ConsString).
|
||||
kHidden = 4, // A link that is needed for proper sizes
|
||||
// calculation, but may be hidden from user.
|
||||
kShortcut = 5 // A link that must not be followed during
|
||||
kShortcut = 5, // A link that must not be followed during
|
||||
// sizes calculation.
|
||||
kWeak = 6 // A weak reference (ignored by the GC).
|
||||
};
|
||||
|
||||
/** Returns edge type (see HeapGraphEdge::Type). */
|
||||
|
@ -299,7 +299,7 @@ class Genesis BASE_EMBEDDED {
|
||||
|
||||
void Bootstrapper::Iterate(ObjectVisitor* v) {
|
||||
extensions_cache_.Iterate(v);
|
||||
v->Synchronize("Extensions");
|
||||
v->Synchronize(VisitorSynchronization::kExtensions);
|
||||
}
|
||||
|
||||
|
||||
|
26
src/heap.cc
26
src/heap.cc
@ -5151,29 +5151,29 @@ void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
|
||||
void Heap::IterateWeakRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
v->VisitPointer(reinterpret_cast<Object**>(&roots_[kSymbolTableRootIndex]));
|
||||
v->Synchronize("symbol_table");
|
||||
v->Synchronize(VisitorSynchronization::kSymbolTable);
|
||||
if (mode != VISIT_ALL_IN_SCAVENGE &&
|
||||
mode != VISIT_ALL_IN_SWEEP_NEWSPACE) {
|
||||
// Scavenge collections have special processing for this.
|
||||
external_string_table_.Iterate(v);
|
||||
}
|
||||
v->Synchronize("external_string_table");
|
||||
v->Synchronize(VisitorSynchronization::kExternalStringsTable);
|
||||
}
|
||||
|
||||
|
||||
void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
v->VisitPointers(&roots_[0], &roots_[kStrongRootListLength]);
|
||||
v->Synchronize("strong_root_list");
|
||||
v->Synchronize(VisitorSynchronization::kStrongRootList);
|
||||
|
||||
v->VisitPointer(BitCast<Object**>(&hidden_symbol_));
|
||||
v->Synchronize("symbol");
|
||||
v->Synchronize(VisitorSynchronization::kSymbol);
|
||||
|
||||
isolate_->bootstrapper()->Iterate(v);
|
||||
v->Synchronize("bootstrapper");
|
||||
v->Synchronize(VisitorSynchronization::kBootstrapper);
|
||||
isolate_->Iterate(v);
|
||||
v->Synchronize("top");
|
||||
v->Synchronize(VisitorSynchronization::kTop);
|
||||
Relocatable::Iterate(v);
|
||||
v->Synchronize("relocatable");
|
||||
v->Synchronize(VisitorSynchronization::kRelocatable);
|
||||
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
isolate_->debug()->Iterate(v);
|
||||
@ -5181,13 +5181,13 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
isolate_->deoptimizer_data()->Iterate(v);
|
||||
}
|
||||
#endif
|
||||
v->Synchronize("debug");
|
||||
v->Synchronize(VisitorSynchronization::kDebug);
|
||||
isolate_->compilation_cache()->Iterate(v);
|
||||
v->Synchronize("compilationcache");
|
||||
v->Synchronize(VisitorSynchronization::kCompilationCache);
|
||||
|
||||
// Iterate over local handles in handle scopes.
|
||||
isolate_->handle_scope_implementer()->Iterate(v);
|
||||
v->Synchronize("handlescope");
|
||||
v->Synchronize(VisitorSynchronization::kHandleScope);
|
||||
|
||||
// Iterate over the builtin code objects and code stubs in the
|
||||
// heap. Note that it is not necessary to iterate over code objects
|
||||
@ -5195,7 +5195,7 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
if (mode != VISIT_ALL_IN_SCAVENGE) {
|
||||
isolate_->builtins()->IterateBuiltins(v);
|
||||
}
|
||||
v->Synchronize("builtins");
|
||||
v->Synchronize(VisitorSynchronization::kBuiltins);
|
||||
|
||||
// Iterate over global handles.
|
||||
switch (mode) {
|
||||
@ -5210,11 +5210,11 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) {
|
||||
isolate_->global_handles()->IterateAllRoots(v);
|
||||
break;
|
||||
}
|
||||
v->Synchronize("globalhandles");
|
||||
v->Synchronize(VisitorSynchronization::kGlobalHandles);
|
||||
|
||||
// Iterate over pointers being held by inactive threads.
|
||||
isolate_->thread_manager()->Iterate(v);
|
||||
v->Synchronize("threadmanager");
|
||||
v->Synchronize(VisitorSynchronization::kThreadManager);
|
||||
|
||||
// Iterate over the pointers the Serialization/Deserialization code is
|
||||
// holding.
|
||||
|
@ -7639,6 +7639,22 @@ void SharedFunctionInfo::CompleteInobjectSlackTracking() {
|
||||
}
|
||||
|
||||
|
||||
#define DECLARE_TAG(ignore1, name, ignore2) name,
|
||||
const char* const VisitorSynchronization::kTags[
|
||||
VisitorSynchronization::kNumberOfSyncTags] = {
|
||||
VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG)
|
||||
};
|
||||
#undef DECLARE_TAG
|
||||
|
||||
|
||||
#define DECLARE_TAG(ignore1, ignore2, name) name,
|
||||
const char* const VisitorSynchronization::kTagNames[
|
||||
VisitorSynchronization::kNumberOfSyncTags] = {
|
||||
VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_TAG)
|
||||
};
|
||||
#undef DECLARE_TAG
|
||||
|
||||
|
||||
void ObjectVisitor::VisitCodeTarget(RelocInfo* rinfo) {
|
||||
ASSERT(RelocInfo::IsCodeTarget(rinfo->rmode()));
|
||||
Object* target = Code::GetCodeFromTargetAddress(rinfo->target_address());
|
||||
|
@ -7862,6 +7862,34 @@ class BreakPointInfo: public Struct {
|
||||
#undef DECL_BOOLEAN_ACCESSORS
|
||||
#undef DECL_ACCESSORS
|
||||
|
||||
#define VISITOR_SYNCHRONIZATION_TAGS_LIST(V) \
|
||||
V(kSymbolTable, "symbol_table", "(Symbols)") \
|
||||
V(kExternalStringsTable, "external_strings_table", "(External strings)") \
|
||||
V(kStrongRootList, "strong_root_list", "(Strong roots)") \
|
||||
V(kSymbol, "symbol", "(Symbol)") \
|
||||
V(kBootstrapper, "bootstrapper", "(Bootstrapper)") \
|
||||
V(kTop, "top", "(Isolate)") \
|
||||
V(kRelocatable, "relocatable", "(Relocatable)") \
|
||||
V(kDebug, "debug", "(Debugger)") \
|
||||
V(kCompilationCache, "compilationcache", "(Compilation cache)") \
|
||||
V(kHandleScope, "handlescope", "(Handle scope)") \
|
||||
V(kBuiltins, "builtins", "(Builtins)") \
|
||||
V(kGlobalHandles, "globalhandles", "(Global handles)") \
|
||||
V(kThreadManager, "threadmanager", "(Thread manager)") \
|
||||
V(kExtensions, "Extensions", "(Extensions)")
|
||||
|
||||
class VisitorSynchronization : public AllStatic {
|
||||
public:
|
||||
#define DECLARE_ENUM(enum_item, ignore1, ignore2) enum_item,
|
||||
enum SyncTag {
|
||||
VISITOR_SYNCHRONIZATION_TAGS_LIST(DECLARE_ENUM)
|
||||
kNumberOfSyncTags
|
||||
};
|
||||
#undef DECLARE_ENUM
|
||||
|
||||
static const char* const kTags[kNumberOfSyncTags];
|
||||
static const char* const kTagNames[kNumberOfSyncTags];
|
||||
};
|
||||
|
||||
// Abstract base class for visiting, and optionally modifying, the
|
||||
// pointers contained in Objects. Used in GC and serialization/deserialization.
|
||||
@ -7917,13 +7945,10 @@ class ObjectVisitor BASE_EMBEDDED {
|
||||
// Visits a handle that has an embedder-assigned class ID.
|
||||
virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Intended for serialization/deserialization checking: insert, or
|
||||
// check for the presence of, a tag at this position in the stream.
|
||||
virtual void Synchronize(const char* tag) {}
|
||||
#else
|
||||
inline void Synchronize(const char* tag) {}
|
||||
#endif
|
||||
// Also used for marking up GC roots in heap snapshots.
|
||||
virtual void Synchronize(VisitorSynchronization::SyncTag tag) {}
|
||||
};
|
||||
|
||||
|
||||
|
@ -95,6 +95,25 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
|
||||
}
|
||||
|
||||
|
||||
uint64_t HeapObjectsMap::GetNthGcSubrootId(int delta) {
|
||||
return kGcRootsObjectId + delta * kObjectIdStep;
|
||||
}
|
||||
|
||||
|
||||
HeapObject* V8HeapExplorer::GetNthGcSubrootObject(int delta) {
|
||||
return reinterpret_cast<HeapObject*>(
|
||||
reinterpret_cast<char*>(kFirstGcSubrootObject) +
|
||||
delta * HeapObjectsMap::kObjectIdStep);
|
||||
}
|
||||
|
||||
|
||||
int V8HeapExplorer::GetGcSubrootOrder(HeapObject* subroot) {
|
||||
return (reinterpret_cast<char*>(subroot) -
|
||||
reinterpret_cast<char*>(kFirstGcSubrootObject)) /
|
||||
HeapObjectsMap::kObjectIdStep;
|
||||
}
|
||||
|
||||
|
||||
uint64_t HeapEntry::id() {
|
||||
union {
|
||||
Id stored_id;
|
||||
|
@ -938,7 +938,7 @@ void HeapGraphEdge::Init(
|
||||
|
||||
|
||||
void HeapGraphEdge::Init(int child_index, Type type, int index, HeapEntry* to) {
|
||||
ASSERT(type == kElement || type == kHidden);
|
||||
ASSERT(type == kElement || type == kHidden || type == kWeak);
|
||||
child_index_ = child_index;
|
||||
type_ = type;
|
||||
index_ = index;
|
||||
@ -1053,8 +1053,11 @@ void HeapEntry::PaintAllReachable() {
|
||||
}
|
||||
|
||||
|
||||
void HeapEntry::Print(int max_depth, int indent) {
|
||||
OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id());
|
||||
void HeapEntry::Print(
|
||||
const char* prefix, const char* edge_name, int max_depth, int indent) {
|
||||
OS::Print("%6d %7d @%6llu %*c %s%s: ",
|
||||
self_size(), RetainedSize(false), id(),
|
||||
indent, ' ', prefix, edge_name);
|
||||
if (type() != kString) {
|
||||
OS::Print("%s %.40s\n", TypeAsString(), name_);
|
||||
} else {
|
||||
@ -1073,29 +1076,40 @@ void HeapEntry::Print(int max_depth, int indent) {
|
||||
Vector<HeapGraphEdge> ch = children();
|
||||
for (int i = 0; i < ch.length(); ++i) {
|
||||
HeapGraphEdge& edge = ch[i];
|
||||
const char* edge_prefix = "";
|
||||
ScopedVector<char> index(64);
|
||||
const char* edge_name = index.start();
|
||||
switch (edge.type()) {
|
||||
case HeapGraphEdge::kContextVariable:
|
||||
OS::Print(" %*c #%s: ", indent, ' ', edge.name());
|
||||
edge_prefix = "#";
|
||||
edge_name = edge.name();
|
||||
break;
|
||||
case HeapGraphEdge::kElement:
|
||||
OS::Print(" %*c %d: ", indent, ' ', edge.index());
|
||||
OS::SNPrintF(index, "%d", edge.index());
|
||||
break;
|
||||
case HeapGraphEdge::kInternal:
|
||||
OS::Print(" %*c $%s: ", indent, ' ', edge.name());
|
||||
edge_prefix = "$";
|
||||
edge_name = edge.name();
|
||||
break;
|
||||
case HeapGraphEdge::kProperty:
|
||||
OS::Print(" %*c %s: ", indent, ' ', edge.name());
|
||||
edge_name = edge.name();
|
||||
break;
|
||||
case HeapGraphEdge::kHidden:
|
||||
OS::Print(" %*c $%d: ", indent, ' ', edge.index());
|
||||
edge_prefix = "$";
|
||||
OS::SNPrintF(index, "%d", edge.index());
|
||||
break;
|
||||
case HeapGraphEdge::kShortcut:
|
||||
OS::Print(" %*c ^%s: ", indent, ' ', edge.name());
|
||||
edge_prefix = "^";
|
||||
edge_name = edge.name();
|
||||
break;
|
||||
case HeapGraphEdge::kWeak:
|
||||
edge_prefix = "w";
|
||||
OS::SNPrintF(index, "%d", edge.index());
|
||||
break;
|
||||
default:
|
||||
OS::Print("!!! unknown edge type: %d ", edge.type());
|
||||
OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
|
||||
}
|
||||
edge.to()->Print(max_depth, indent + 2);
|
||||
edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1215,6 +1229,9 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
|
||||
STATIC_ASSERT(
|
||||
sizeof(HeapEntry) ==
|
||||
SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapEntrySize); // NOLINT
|
||||
for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) {
|
||||
gc_subroot_entries_[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HeapSnapshot::~HeapSnapshot() {
|
||||
@ -1270,6 +1287,21 @@ HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count,
|
||||
}
|
||||
|
||||
|
||||
HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag,
|
||||
int children_count,
|
||||
int retainers_count) {
|
||||
ASSERT(gc_subroot_entries_[tag] == NULL);
|
||||
ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
|
||||
return (gc_subroot_entries_[tag] = AddEntry(
|
||||
HeapEntry::kObject,
|
||||
VisitorSynchronization::kTagNames[tag],
|
||||
HeapObjectsMap::GetNthGcSubrootId(tag),
|
||||
0,
|
||||
children_count,
|
||||
retainers_count));
|
||||
}
|
||||
|
||||
|
||||
HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count,
|
||||
int retainers_count) {
|
||||
ASSERT(natives_root_entry_ == NULL);
|
||||
@ -1355,17 +1387,22 @@ List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
|
||||
|
||||
|
||||
void HeapSnapshot::Print(int max_depth) {
|
||||
root()->Print(max_depth, 0);
|
||||
root()->Print("", "", max_depth, 0);
|
||||
}
|
||||
|
||||
|
||||
// We split IDs on evens for embedder objects (see
|
||||
// HeapObjectsMap::GenerateId) and odds for native objects.
|
||||
const uint64_t HeapObjectsMap::kInternalRootObjectId = 1;
|
||||
const uint64_t HeapObjectsMap::kGcRootsObjectId = 3;
|
||||
const uint64_t HeapObjectsMap::kNativesRootObjectId = 5;
|
||||
// Increase kFirstAvailableObjectId if new 'special' objects appear.
|
||||
const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7;
|
||||
const uint64_t HeapObjectsMap::kGcRootsObjectId =
|
||||
HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
|
||||
const uint64_t HeapObjectsMap::kNativesRootObjectId =
|
||||
HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
|
||||
const uint64_t HeapObjectsMap::kGcRootsFirstSubrootId =
|
||||
HeapObjectsMap::kNativesRootObjectId + HeapObjectsMap::kObjectIdStep;
|
||||
const uint64_t HeapObjectsMap::kFirstAvailableObjectId =
|
||||
HeapObjectsMap::kGcRootsFirstSubrootId +
|
||||
VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
|
||||
|
||||
HeapObjectsMap::HeapObjectsMap()
|
||||
: initial_fill_mode_(true),
|
||||
@ -1391,7 +1428,7 @@ uint64_t HeapObjectsMap::FindObject(Address addr) {
|
||||
if (existing != 0) return existing;
|
||||
}
|
||||
uint64_t id = next_id_;
|
||||
next_id_ += 2;
|
||||
next_id_ += kObjectIdStep;
|
||||
AddEntry(addr, id);
|
||||
return id;
|
||||
}
|
||||
@ -1684,6 +1721,12 @@ HeapObject *const V8HeapExplorer::kInternalRootObject =
|
||||
HeapObject *const V8HeapExplorer::kGcRootsObject =
|
||||
reinterpret_cast<HeapObject*>(
|
||||
static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
|
||||
HeapObject *const V8HeapExplorer::kFirstGcSubrootObject =
|
||||
reinterpret_cast<HeapObject*>(
|
||||
static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId));
|
||||
HeapObject *const V8HeapExplorer::kLastGcSubrootObject =
|
||||
reinterpret_cast<HeapObject*>(
|
||||
static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId));
|
||||
|
||||
|
||||
V8HeapExplorer::V8HeapExplorer(
|
||||
@ -1716,6 +1759,11 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
|
||||
return snapshot_->AddRootEntry(children_count);
|
||||
} else if (object == kGcRootsObject) {
|
||||
return snapshot_->AddGcRootsEntry(children_count, retainers_count);
|
||||
} else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
|
||||
return snapshot_->AddGcSubrootEntry(
|
||||
GetGcSubrootOrder(object),
|
||||
children_count,
|
||||
retainers_count);
|
||||
} else if (object->IsJSGlobalObject()) {
|
||||
const char* tag = objects_tags_.GetTag(object);
|
||||
const char* name = collection_->names()->GetName(
|
||||
@ -1779,6 +1827,18 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
|
||||
: "",
|
||||
children_count,
|
||||
retainers_count);
|
||||
} else if (object->IsGlobalContext()) {
|
||||
return AddEntry(object,
|
||||
HeapEntry::kHidden,
|
||||
"system / GlobalContext",
|
||||
children_count,
|
||||
retainers_count);
|
||||
} else if (object->IsContext()) {
|
||||
return AddEntry(object,
|
||||
HeapEntry::kHidden,
|
||||
"system / Context",
|
||||
children_count,
|
||||
retainers_count);
|
||||
} else if (object->IsFixedArray() ||
|
||||
object->IsFixedDoubleArray() ||
|
||||
object->IsByteArray() ||
|
||||
@ -1818,9 +1878,38 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
|
||||
}
|
||||
|
||||
|
||||
class GcSubrootsEnumerator : public ObjectVisitor {
|
||||
public:
|
||||
GcSubrootsEnumerator(
|
||||
SnapshotFillerInterface* filler, V8HeapExplorer* explorer)
|
||||
: filler_(filler),
|
||||
explorer_(explorer),
|
||||
previous_object_count_(0),
|
||||
object_count_(0) {
|
||||
}
|
||||
void VisitPointers(Object** start, Object** end) {
|
||||
object_count_ += end - start;
|
||||
}
|
||||
void Synchronize(VisitorSynchronization::SyncTag tag) {
|
||||
// Skip empty subroots.
|
||||
if (previous_object_count_ != object_count_) {
|
||||
previous_object_count_ = object_count_;
|
||||
filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_);
|
||||
}
|
||||
}
|
||||
private:
|
||||
SnapshotFillerInterface* filler_;
|
||||
V8HeapExplorer* explorer_;
|
||||
intptr_t previous_object_count_;
|
||||
intptr_t object_count_;
|
||||
};
|
||||
|
||||
|
||||
void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
|
||||
filler->AddEntry(kInternalRootObject, this);
|
||||
filler->AddEntry(kGcRootsObject, this);
|
||||
GcSubrootsEnumerator enumerator(filler, this);
|
||||
heap_->IterateRoots(&enumerator, VISIT_ALL);
|
||||
}
|
||||
|
||||
|
||||
@ -1939,6 +2028,11 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
|
||||
"literals_or_bindings",
|
||||
js_fun->literals_or_bindings(),
|
||||
JSFunction::kLiteralsOffset);
|
||||
for (int i = JSFunction::kNonWeakFieldsEndOffset;
|
||||
i < JSFunction::kSize;
|
||||
i += kPointerSize) {
|
||||
SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i);
|
||||
}
|
||||
}
|
||||
TagObject(js_obj->properties(), "(object properties)");
|
||||
SetInternalReference(obj, entry,
|
||||
@ -1965,8 +2059,14 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
|
||||
"(context func. result caches)");
|
||||
TagObject(context->normalized_map_cache(), "(context norm. map cache)");
|
||||
TagObject(context->runtime_context(), "(runtime context)");
|
||||
TagObject(context->map_cache(), "(context map cache)");
|
||||
TagObject(context->data(), "(context data)");
|
||||
for (int i = Context::FIRST_WEAK_SLOT;
|
||||
i < Context::GLOBAL_CONTEXT_SLOTS;
|
||||
++i) {
|
||||
SetWeakReference(obj, entry,
|
||||
i, context->get(i),
|
||||
FixedArray::OffsetOfElementAt(i));
|
||||
}
|
||||
} else if (obj->IsMap()) {
|
||||
Map* map = Map::cast(obj);
|
||||
SetInternalReference(obj, entry,
|
||||
@ -2009,6 +2109,9 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
|
||||
SetInternalReference(obj, entry,
|
||||
"script", shared->script(),
|
||||
SharedFunctionInfo::kScriptOffset);
|
||||
SetWeakReference(obj, entry,
|
||||
1, shared->initial_map(),
|
||||
SharedFunctionInfo::kInitialMapOffset);
|
||||
} else if (obj->IsScript()) {
|
||||
Script* script = Script::cast(obj);
|
||||
SetInternalReference(obj, entry,
|
||||
@ -2235,15 +2338,66 @@ HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
|
||||
|
||||
|
||||
class RootsReferencesExtractor : public ObjectVisitor {
|
||||
public:
|
||||
explicit RootsReferencesExtractor(V8HeapExplorer* explorer)
|
||||
: explorer_(explorer) {
|
||||
}
|
||||
void VisitPointers(Object** start, Object** end) {
|
||||
for (Object** p = start; p < end; p++) explorer_->SetGcRootsReference(*p);
|
||||
}
|
||||
private:
|
||||
V8HeapExplorer* explorer_;
|
||||
struct IndexTag {
|
||||
IndexTag(int index, VisitorSynchronization::SyncTag tag)
|
||||
: index(index), tag(tag) { }
|
||||
int index;
|
||||
VisitorSynchronization::SyncTag tag;
|
||||
};
|
||||
|
||||
public:
|
||||
RootsReferencesExtractor()
|
||||
: collecting_all_references_(false),
|
||||
previous_reference_count_(0) {
|
||||
}
|
||||
|
||||
void VisitPointers(Object** start, Object** end) {
|
||||
if (collecting_all_references_) {
|
||||
for (Object** p = start; p < end; p++) all_references_.Add(*p);
|
||||
} else {
|
||||
for (Object** p = start; p < end; p++) strong_references_.Add(*p);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCollectingAllReferences() { collecting_all_references_ = true; }
|
||||
|
||||
void FillReferences(V8HeapExplorer* explorer) {
|
||||
ASSERT(strong_references_.length() <= all_references_.length());
|
||||
for (int i = 0; i < reference_tags_.length(); ++i) {
|
||||
explorer->SetGcRootsReference(reference_tags_[i].tag);
|
||||
}
|
||||
int strong_index = 0, all_index = 0, tags_index = 0;
|
||||
while (all_index < all_references_.length()) {
|
||||
if (strong_index < strong_references_.length() &&
|
||||
strong_references_[strong_index] == all_references_[all_index]) {
|
||||
explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
|
||||
false,
|
||||
all_references_[all_index++]);
|
||||
++strong_index;
|
||||
} else {
|
||||
explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
|
||||
true,
|
||||
all_references_[all_index++]);
|
||||
}
|
||||
if (reference_tags_[tags_index].index == all_index) ++tags_index;
|
||||
}
|
||||
}
|
||||
|
||||
void Synchronize(VisitorSynchronization::SyncTag tag) {
|
||||
if (collecting_all_references_ &&
|
||||
previous_reference_count_ != all_references_.length()) {
|
||||
previous_reference_count_ = all_references_.length();
|
||||
reference_tags_.Add(IndexTag(previous_reference_count_, tag));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool collecting_all_references_;
|
||||
List<Object*> strong_references_;
|
||||
List<Object*> all_references_;
|
||||
int previous_reference_count_;
|
||||
List<IndexTag> reference_tags_;
|
||||
};
|
||||
|
||||
|
||||
@ -2268,8 +2422,11 @@ bool V8HeapExplorer::IterateAndExtractReferences(
|
||||
return false;
|
||||
}
|
||||
SetRootGcRootsReference();
|
||||
RootsReferencesExtractor extractor(this);
|
||||
RootsReferencesExtractor extractor;
|
||||
heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG);
|
||||
extractor.SetCollectingAllReferences();
|
||||
heap_->IterateRoots(&extractor, VISIT_ALL);
|
||||
extractor.FillReferences(this);
|
||||
filler_ = NULL;
|
||||
return progress_->ProgressReport(false);
|
||||
}
|
||||
@ -2359,6 +2516,24 @@ void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
|
||||
}
|
||||
|
||||
|
||||
void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
|
||||
HeapEntry* parent_entry,
|
||||
int index,
|
||||
Object* child_obj,
|
||||
int field_offset) {
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry != NULL) {
|
||||
filler_->SetIndexedReference(HeapGraphEdge::kWeak,
|
||||
parent_obj,
|
||||
parent_entry,
|
||||
index,
|
||||
child_obj,
|
||||
child_entry);
|
||||
IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
|
||||
HeapEntry* parent_entry,
|
||||
String* reference_name,
|
||||
@ -2421,12 +2596,21 @@ void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) {
|
||||
}
|
||||
|
||||
|
||||
void V8HeapExplorer::SetGcRootsReference(Object* child_obj) {
|
||||
void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) {
|
||||
filler_->SetIndexedAutoIndexReference(
|
||||
HeapGraphEdge::kElement,
|
||||
kGcRootsObject, snapshot_->gc_roots(),
|
||||
GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag));
|
||||
}
|
||||
|
||||
|
||||
void V8HeapExplorer::SetGcSubrootReference(
|
||||
VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) {
|
||||
HeapEntry* child_entry = GetEntry(child_obj);
|
||||
if (child_entry != NULL) {
|
||||
filler_->SetIndexedAutoIndexReference(
|
||||
HeapGraphEdge::kElement,
|
||||
kGcRootsObject, snapshot_->gc_roots(),
|
||||
is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement,
|
||||
GetNthGcSubrootObject(tag), snapshot_->gc_subroot(tag),
|
||||
child_obj, child_entry);
|
||||
}
|
||||
}
|
||||
@ -3235,7 +3419,8 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) {
|
||||
writer_->AddNumber(edge->type());
|
||||
writer_->AddCharacter(',');
|
||||
if (edge->type() == HeapGraphEdge::kElement
|
||||
|| edge->type() == HeapGraphEdge::kHidden) {
|
||||
|| edge->type() == HeapGraphEdge::kHidden
|
||||
|| edge->type() == HeapGraphEdge::kWeak) {
|
||||
writer_->AddNumber(edge->index());
|
||||
} else {
|
||||
writer_->AddNumber(GetStringId(edge->name()));
|
||||
@ -3315,7 +3500,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() {
|
||||
"," JSON_S("property")
|
||||
"," JSON_S("internal")
|
||||
"," JSON_S("hidden")
|
||||
"," JSON_S("shortcut"))
|
||||
"," JSON_S("shortcut")
|
||||
"," JSON_S("weak"))
|
||||
"," JSON_S("string_or_number")
|
||||
"," JSON_S("node"))))));
|
||||
#undef JSON_S
|
||||
|
@ -455,7 +455,8 @@ class HeapGraphEdge BASE_EMBEDDED {
|
||||
kProperty = v8::HeapGraphEdge::kProperty,
|
||||
kInternal = v8::HeapGraphEdge::kInternal,
|
||||
kHidden = v8::HeapGraphEdge::kHidden,
|
||||
kShortcut = v8::HeapGraphEdge::kShortcut
|
||||
kShortcut = v8::HeapGraphEdge::kShortcut,
|
||||
kWeak = v8::HeapGraphEdge::kWeak
|
||||
};
|
||||
|
||||
HeapGraphEdge() { }
|
||||
@ -465,7 +466,7 @@ class HeapGraphEdge BASE_EMBEDDED {
|
||||
|
||||
Type type() { return static_cast<Type>(type_); }
|
||||
int index() {
|
||||
ASSERT(type_ == kElement || type_ == kHidden);
|
||||
ASSERT(type_ == kElement || type_ == kHidden || type_ == kWeak);
|
||||
return index_;
|
||||
}
|
||||
const char* name() {
|
||||
@ -588,7 +589,8 @@ class HeapEntry BASE_EMBEDDED {
|
||||
int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); }
|
||||
int RetainedSize(bool exact);
|
||||
|
||||
void Print(int max_depth, int indent);
|
||||
void Print(
|
||||
const char* prefix, const char* edge_name, int max_depth, int indent);
|
||||
|
||||
Handle<HeapObject> GetHeapObject();
|
||||
|
||||
@ -661,6 +663,7 @@ class HeapSnapshot {
|
||||
HeapEntry* root() { return root_entry_; }
|
||||
HeapEntry* gc_roots() { return gc_roots_entry_; }
|
||||
HeapEntry* natives_root() { return natives_root_entry_; }
|
||||
HeapEntry* gc_subroot(int index) { return gc_subroot_entries_[index]; }
|
||||
List<HeapEntry*>* entries() { return &entries_; }
|
||||
int raw_entries_size() { return raw_entries_size_; }
|
||||
|
||||
@ -674,6 +677,9 @@ class HeapSnapshot {
|
||||
int retainers_count);
|
||||
HeapEntry* AddRootEntry(int children_count);
|
||||
HeapEntry* AddGcRootsEntry(int children_count, int retainers_count);
|
||||
HeapEntry* AddGcSubrootEntry(int tag,
|
||||
int children_count,
|
||||
int retainers_count);
|
||||
HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
|
||||
void ClearPaint();
|
||||
HeapEntry* GetEntryById(uint64_t id);
|
||||
@ -695,6 +701,7 @@ class HeapSnapshot {
|
||||
HeapEntry* root_entry_;
|
||||
HeapEntry* gc_roots_entry_;
|
||||
HeapEntry* natives_root_entry_;
|
||||
HeapEntry* gc_subroot_entries_[VisitorSynchronization::kNumberOfSyncTags];
|
||||
char* raw_entries_;
|
||||
List<HeapEntry*> entries_;
|
||||
bool entries_sorted_;
|
||||
@ -716,10 +723,13 @@ class HeapObjectsMap {
|
||||
void MoveObject(Address from, Address to);
|
||||
|
||||
static uint64_t GenerateId(v8::RetainedObjectInfo* info);
|
||||
static inline uint64_t GetNthGcSubrootId(int delta);
|
||||
|
||||
static const int kObjectIdStep = 2;
|
||||
static const uint64_t kInternalRootObjectId;
|
||||
static const uint64_t kGcRootsObjectId;
|
||||
static const uint64_t kNativesRootObjectId;
|
||||
static const uint64_t kGcRootsFirstSubrootId;
|
||||
static const uint64_t kFirstAvailableObjectId;
|
||||
|
||||
private:
|
||||
@ -969,6 +979,11 @@ class V8HeapExplorer : public HeapEntriesAllocator {
|
||||
HeapEntry* parent,
|
||||
int index,
|
||||
Object* child);
|
||||
void SetWeakReference(HeapObject* parent_obj,
|
||||
HeapEntry* parent_entry,
|
||||
int index,
|
||||
Object* child_obj,
|
||||
int field_offset);
|
||||
void SetPropertyReference(HeapObject* parent_obj,
|
||||
HeapEntry* parent,
|
||||
String* reference_name,
|
||||
@ -981,11 +996,16 @@ class V8HeapExplorer : public HeapEntriesAllocator {
|
||||
Object* child);
|
||||
void SetRootShortcutReference(Object* child);
|
||||
void SetRootGcRootsReference();
|
||||
void SetGcRootsReference(Object* child);
|
||||
void SetGcRootsReference(VisitorSynchronization::SyncTag tag);
|
||||
void SetGcSubrootReference(
|
||||
VisitorSynchronization::SyncTag tag, bool is_weak, Object* child);
|
||||
void TagObject(Object* obj, const char* tag);
|
||||
|
||||
HeapEntry* GetEntry(Object* obj);
|
||||
|
||||
static inline HeapObject* GetNthGcSubrootObject(int delta);
|
||||
static inline int GetGcSubrootOrder(HeapObject* subroot);
|
||||
|
||||
Heap* heap_;
|
||||
HeapSnapshot* snapshot_;
|
||||
HeapSnapshotsCollection* collection_;
|
||||
@ -994,8 +1014,11 @@ class V8HeapExplorer : public HeapEntriesAllocator {
|
||||
HeapObjectsSet objects_tags_;
|
||||
|
||||
static HeapObject* const kGcRootsObject;
|
||||
static HeapObject* const kFirstGcSubrootObject;
|
||||
static HeapObject* const kLastGcSubrootObject;
|
||||
|
||||
friend class IndexedReferencesExtractor;
|
||||
friend class GcSubrootsEnumerator;
|
||||
friend class RootsReferencesExtractor;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(V8HeapExplorer);
|
||||
|
@ -1065,3 +1065,82 @@ TEST(FastCaseGetter) {
|
||||
GetProperty(obj1, v8::HeapGraphEdge::kProperty, "set-propWithSetter");
|
||||
CHECK_NE(NULL, setterFunction);
|
||||
}
|
||||
|
||||
|
||||
bool HasWeakEdge(const v8::HeapGraphNode* node) {
|
||||
for (int i = 0; i < node->GetChildrenCount(); ++i) {
|
||||
const v8::HeapGraphEdge* handle_edge = node->GetChild(i);
|
||||
if (handle_edge->GetType() == v8::HeapGraphEdge::kWeak) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool HasWeakGlobalHandle() {
|
||||
const v8::HeapSnapshot* snapshot =
|
||||
v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
|
||||
const v8::HeapGraphNode* gc_roots = GetNode(
|
||||
snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
|
||||
CHECK_NE(NULL, gc_roots);
|
||||
const v8::HeapGraphNode* global_handles = GetNode(
|
||||
gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
|
||||
CHECK_NE(NULL, global_handles);
|
||||
return HasWeakEdge(global_handles);
|
||||
}
|
||||
|
||||
|
||||
static void PersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) {
|
||||
handle.Dispose();
|
||||
}
|
||||
|
||||
|
||||
TEST(WeakGlobalHandle) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
CHECK(!HasWeakGlobalHandle());
|
||||
|
||||
v8::Persistent<v8::Object> handle =
|
||||
v8::Persistent<v8::Object>::New(v8::Object::New());
|
||||
handle.MakeWeak(NULL, PersistentHandleCallback);
|
||||
|
||||
CHECK(HasWeakGlobalHandle());
|
||||
}
|
||||
|
||||
|
||||
TEST(WeakGlobalContextRefs) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
const v8::HeapSnapshot* snapshot =
|
||||
v8::HeapProfiler::TakeSnapshot(v8_str("weaks"));
|
||||
const v8::HeapGraphNode* gc_roots = GetNode(
|
||||
snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(GC roots)");
|
||||
CHECK_NE(NULL, gc_roots);
|
||||
const v8::HeapGraphNode* global_handles = GetNode(
|
||||
gc_roots, v8::HeapGraphNode::kObject, "(Global handles)");
|
||||
CHECK_NE(NULL, global_handles);
|
||||
const v8::HeapGraphNode* global_context = GetNode(
|
||||
global_handles, v8::HeapGraphNode::kHidden, "system / GlobalContext");
|
||||
CHECK_NE(NULL, global_context);
|
||||
CHECK(HasWeakEdge(global_context));
|
||||
}
|
||||
|
||||
|
||||
TEST(SfiAndJsFunctionWeakRefs) {
|
||||
v8::HandleScope scope;
|
||||
LocalContext env;
|
||||
|
||||
CompileRun(
|
||||
"fun = (function (x) { return function () { return x + 1; } })(1);");
|
||||
const v8::HeapSnapshot* snapshot =
|
||||
v8::HeapProfiler::TakeSnapshot(v8_str("fun"));
|
||||
const v8::HeapGraphNode* global = GetGlobalObject(snapshot);
|
||||
CHECK_NE(NULL, global);
|
||||
const v8::HeapGraphNode* fun =
|
||||
GetProperty(global, v8::HeapGraphEdge::kShortcut, "fun");
|
||||
CHECK(HasWeakEdge(fun));
|
||||
const v8::HeapGraphNode* shared =
|
||||
GetProperty(fun, v8::HeapGraphEdge::kInternal, "shared");
|
||||
CHECK(HasWeakEdge(shared));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user