diff options
Diffstat (limited to 'deps/v8/src/profile-generator.cc')
-rw-r--r-- | deps/v8/src/profile-generator.cc | 261 |
1 files changed, 170 insertions, 91 deletions
diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index c6e6131a71..1ba68a1667 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -1,4 +1,4 @@ -// Copyright 2011 the V8 project authors. All rights reserved. +// Copyright 2012 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -1083,7 +1083,7 @@ void HeapEntry::Print( for (int i = 0; i < ch.length(); ++i) { HeapGraphEdge& edge = ch[i]; const char* edge_prefix = ""; - ScopedVector<char> index(64); + EmbeddedVector<char, 64> index; const char* edge_name = index.start(); switch (edge.type()) { case HeapGraphEdge::kContextVariable: @@ -1164,6 +1164,7 @@ class RetainedSizeCalculator { int retained_size_; }; + void HeapEntry::CalculateExactRetainedSize() { // To calculate retained size, first we paint all reachable nodes in // one color, then we paint (or re-paint) all nodes reachable from @@ -1208,11 +1209,13 @@ template <size_t ptr_size> struct SnapshotSizeConstants; template <> struct SnapshotSizeConstants<4> { static const int kExpectedHeapGraphEdgeSize = 12; static const int kExpectedHeapEntrySize = 36; + static const int kMaxSerializableSnapshotRawSize = 256 * MB; }; template <> struct SnapshotSizeConstants<8> { static const int kExpectedHeapGraphEdgeSize = 24; static const int kExpectedHeapEntrySize = 48; + static const int kMaxSerializableSnapshotRawSize = 768 * MB; }; } // namespace @@ -1241,6 +1244,7 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, } } + HeapSnapshot::~HeapSnapshot() { DeleteArray(raw_entries_); } @@ -1266,6 +1270,7 @@ static void HeapEntryClearPaint(HeapEntry** entry_ptr) { (*entry_ptr)->clear_paint(); } + void HeapSnapshot::ClearPaint() { entries_.Iterate(HeapEntryClearPaint); } @@ -1371,6 +1376,7 @@ static int SortByIds(const T* entry1_ptr, return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1; } + List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() { if (!entries_sorted_) { entries_.Sort(SortByIds); @@ -1775,9 +1781,11 @@ HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, } else if (object->IsJSFunction()) { JSFunction* func = JSFunction::cast(object); SharedFunctionInfo* shared = func->shared(); + const char* name = shared->bound() ? "native_bind" : + collection_->names()->GetName(String::cast(shared->name())); return AddEntry(object, HeapEntry::kClosure, - collection_->names()->GetName(String::cast(shared->name())), + name, children_count, retainers_count); } else if (object->IsJSRegExp()) { @@ -2011,19 +2019,22 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { heap_->prototype_symbol(), js_fun->prototype()); } } + SharedFunctionInfo* shared_info = js_fun->shared(); + // JSFunction has either bindings or literals and never both. + bool bound = shared_info->bound(); + TagObject(js_fun->literals_or_bindings(), + bound ? "(function bindings)" : "(function literals)"); SetInternalReference(js_fun, entry, - "shared", js_fun->shared(), + bound ? "bindings" : "literals", + js_fun->literals_or_bindings(), + JSFunction::kLiteralsOffset); + SetInternalReference(js_fun, entry, + "shared", shared_info, JSFunction::kSharedFunctionInfoOffset); TagObject(js_fun->unchecked_context(), "(context)"); SetInternalReference(js_fun, entry, "context", js_fun->unchecked_context(), JSFunction::kContextOffset); - TagObject(js_fun->literals_or_bindings(), - "(function literals_or_bindings)"); - SetInternalReference(js_fun, entry, - "literals_or_bindings", - js_fun->literals_or_bindings(), - JSFunction::kLiteralsOffset); for (int i = JSFunction::kNonWeakFieldsEndOffset; i < JSFunction::kSize; i += kPointerSize) { @@ -2126,17 +2137,6 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { SetInternalReference(obj, entry, "line_ends", script->line_ends(), Script::kLineEndsOffset); - } else if (obj->IsDescriptorArray()) { - DescriptorArray* desc_array = DescriptorArray::cast(obj); - if (desc_array->length() > DescriptorArray::kContentArrayIndex) { - Object* content_array = - desc_array->get(DescriptorArray::kContentArrayIndex); - TagObject(content_array, "(map descriptor content)"); - SetInternalReference(obj, entry, - "content", content_array, - FixedArray::OffsetOfElementAt( - DescriptorArray::kContentArrayIndex)); - } } else if (obj->IsCodeCache()) { CodeCache* code_cache = CodeCache::cast(obj); TagObject(code_cache->default_cache(), "(default code cache)"); @@ -2162,11 +2162,27 @@ void V8HeapExplorer::ExtractReferences(HeapObject* obj) { void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry) { - if (js_obj->IsJSFunction()) { - JSFunction* func = JSFunction::cast(js_obj); - Context* context = func->context(); - ScopeInfo* scope_info = context->closure()->shared()->scope_info(); - + if (!js_obj->IsJSFunction()) return; + + JSFunction* func = JSFunction::cast(js_obj); + Context* context = func->context(); + ScopeInfo* scope_info = context->closure()->shared()->scope_info(); + + if (func->shared()->bound()) { + FixedArray* bindings = func->function_bindings(); + SetNativeBindReference(js_obj, entry, "bound_this", + bindings->get(JSFunction::kBoundThisIndex)); + SetNativeBindReference(js_obj, entry, "bound_function", + bindings->get(JSFunction::kBoundFunctionIndex)); + for (int i = JSFunction::kBoundArgumentsStartIndex; + i < bindings->length(); i++) { + const char* reference_name = collection_->names()->GetFormatted( + "bound_argument_%d", + i - JSFunction::kBoundArgumentsStartIndex); + SetNativeBindReference(js_obj, entry, reference_name, + bindings->get(i)); + } + } else { // Add context allocated locals. int context_locals = scope_info->ContextLocalCount(); for (int i = 0; i < context_locals; ++i) { @@ -2444,6 +2460,22 @@ void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj, } +void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + const char* reference_name, + Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetNamedReference(HeapGraphEdge::kShortcut, + parent_obj, + parent_entry, + reference_name, + child_obj, + child_entry); + } +} + + void V8HeapExplorer::SetElementReference(HeapObject* parent_obj, HeapEntry* parent_entry, int index, @@ -2617,7 +2649,6 @@ void V8HeapExplorer::TagObject(Object* obj, const char* tag) { !obj->IsOddball() && obj != heap_->raw_unchecked_empty_byte_array() && obj != heap_->raw_unchecked_empty_fixed_array() && - obj != heap_->raw_unchecked_empty_fixed_double_array() && obj != heap_->raw_unchecked_empty_descriptor_array()) { objects_tags_.SetTag(obj, tag); } @@ -3240,57 +3271,77 @@ void HeapSnapshotGenerator::FillReversePostorderIndexes( } -static int Intersect(int i1, int i2, const Vector<HeapEntry*>& dominators) { +static int Intersect(int i1, int i2, const Vector<int>& dominators) { int finger1 = i1, finger2 = i2; while (finger1 != finger2) { - while (finger1 < finger2) finger1 = dominators[finger1]->ordered_index(); - while (finger2 < finger1) finger2 = dominators[finger2]->ordered_index(); + while (finger1 < finger2) finger1 = dominators[finger1]; + while (finger2 < finger1) finger2 = dominators[finger2]; } return finger1; } + // The algorithm is based on the article: // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" // Softw. Pract. Exper. 4 (2001), pp. 1-10. bool HeapSnapshotGenerator::BuildDominatorTree( const Vector<HeapEntry*>& entries, - Vector<HeapEntry*>* dominators) { + Vector<int>* dominators) { if (entries.length() == 0) return true; const int entries_length = entries.length(), root_index = entries_length - 1; - for (int i = 0; i < root_index; ++i) (*dominators)[i] = NULL; - (*dominators)[root_index] = entries[root_index]; + static const int kNoDominator = -1; + for (int i = 0; i < root_index; ++i) (*dominators)[i] = kNoDominator; + (*dominators)[root_index] = root_index; + + // We use time_stamps array to stamp entries with the iteration number + // when the dominance for the entry has been updated. + ScopedVector<int> time_stamps(entries_length); + for (int i = 0; i < entries_length; ++i) time_stamps[i] = -1; + Vector<HeapGraphEdge> children = entries[root_index]->children(); + for (int i = 0; i < children.length(); ++i) { + // Mark the root direct children as affected on iteration zero. + time_stamps[children[i].to()->ordered_index()] = 0; + } + int changed = 1; + int iteration = 0; const int base_progress_counter = progress_counter_; while (changed != 0) { + ++iteration; changed = 0; for (int i = root_index - 1; i >= 0; --i) { - HeapEntry* new_idom = NULL; + // If dominator of the entry has already been set to root, + // then it can't propagate any further. + if ((*dominators)[i] == root_index) continue; + // If no retainers of the entry had been updated on current + // or previous iteration, then this entry is not affected. + if (time_stamps[i] < iteration - 1) continue; + int new_idom_index = kNoDominator; Vector<HeapGraphEdge*> rets = entries[i]->retainers(); - int j = 0; - for (; j < rets.length(); ++j) { + for (int j = 0; j < rets.length(); ++j) { if (rets[j]->type() == HeapGraphEdge::kShortcut) continue; - HeapEntry* ret = rets[j]->From(); - if (dominators->at(ret->ordered_index()) != NULL) { - new_idom = ret; - break; + int ret_index = rets[j]->From()->ordered_index(); + if (dominators->at(ret_index) != kNoDominator) { + new_idom_index = new_idom_index == kNoDominator + ? ret_index + : Intersect(ret_index, new_idom_index, *dominators); + // If idom has already reached the root, it doesn't make sense + // to check other retainers. + if (new_idom_index == root_index) break; } } - for (++j; j < rets.length(); ++j) { - if (rets[j]->type() == HeapGraphEdge::kShortcut) continue; - HeapEntry* ret = rets[j]->From(); - if (dominators->at(ret->ordered_index()) != NULL) { - new_idom = entries[Intersect(ret->ordered_index(), - new_idom->ordered_index(), - *dominators)]; - } - } - if (new_idom != NULL && dominators->at(i) != new_idom) { - (*dominators)[i] = new_idom; + if (new_idom_index != kNoDominator + && dominators->at(i) != new_idom_index) { + (*dominators)[i] = new_idom_index; ++changed; + Vector<HeapGraphEdge> children = entries[i]->children(); + for (int j = 0; j < children.length(); ++j) { + time_stamps[children[j].to()->ordered_index()] = iteration; + } } } int remaining = entries_length - changed; - if (remaining < 0) remaining = 0; + ASSERT(remaining >= 0); progress_counter_ = base_progress_counter + remaining; if (!ProgressReport(true)) return false; } @@ -3302,11 +3353,11 @@ bool HeapSnapshotGenerator::SetEntriesDominators() { // This array is used for maintaining reverse postorder of nodes. ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries()->length()); FillReversePostorderIndexes(&ordered_entries); - ScopedVector<HeapEntry*> dominators(ordered_entries.length()); + ScopedVector<int> dominators(ordered_entries.length()); if (!BuildDominatorTree(ordered_entries, &dominators)) return false; for (int i = 0; i < ordered_entries.length(); ++i) { - ASSERT(dominators[i] != NULL); - ordered_entries[i]->set_dominator(dominators[i]); + ASSERT(dominators[i] >= 0); + ordered_entries[i]->set_dominator(ordered_entries[dominators[i]]); } return true; } @@ -3336,6 +3387,17 @@ bool HeapSnapshotGenerator::ApproximateRetainedSizes() { } +template<int bytes> struct MaxDecimalDigitsIn; +template<> struct MaxDecimalDigitsIn<4> { + static const int kSigned = 11; + static const int kUnsigned = 10; +}; +template<> struct MaxDecimalDigitsIn<8> { + static const int kSigned = 20; + static const int kUnsigned = 20; +}; + + class OutputStreamWriter { public: explicit OutputStreamWriter(v8::OutputStream* stream) @@ -3385,23 +3447,34 @@ class OutputStreamWriter { private: template<typename T> void AddNumberImpl(T n, const char* format) { - ScopedVector<char> buffer(32); - int result = OS::SNPrintF(buffer, format, n); - USE(result); - ASSERT(result != -1); - AddString(buffer.start()); + // Buffer for the longest value plus trailing \0 + static const int kMaxNumberSize = + MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1; + if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) { + int result = OS::SNPrintF( + chunk_.SubVector(chunk_pos_, chunk_size_), format, n); + ASSERT(result != -1); + chunk_pos_ += result; + MaybeWriteChunk(); + } else { + EmbeddedVector<char, kMaxNumberSize> buffer; + int result = OS::SNPrintF(buffer, format, n); + USE(result); + ASSERT(result != -1); + AddString(buffer.start()); + } } void MaybeWriteChunk() { ASSERT(chunk_pos_ <= chunk_size_); if (chunk_pos_ == chunk_size_) { WriteChunk(); - chunk_pos_ = 0; } } void WriteChunk() { if (aborted_) return; if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) == v8::OutputStream::kAbort) aborted_ = true; + chunk_pos_ = 0; } v8::OutputStream* stream_; @@ -3411,15 +3484,14 @@ class OutputStreamWriter { bool aborted_; }; -const int HeapSnapshotJSONSerializer::kMaxSerializableSnapshotRawSize = - 256 * MB; void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) { ASSERT(writer_ == NULL); writer_ = new OutputStreamWriter(stream); HeapSnapshot* original_snapshot = NULL; - if (snapshot_->raw_entries_size() >= kMaxSerializableSnapshotRawSize) { + if (snapshot_->raw_entries_size() >= + SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize) { // The snapshot is too big. Serialize a fake snapshot. original_snapshot = snapshot_; snapshot_ = CreateFakeSnapshot(); @@ -3446,8 +3518,14 @@ HeapSnapshot* HeapSnapshotJSONSerializer::CreateFakeSnapshot() { snapshot_->uid()); result->AllocateEntries(2, 1, 0); HeapEntry* root = result->AddRootEntry(1); + const char* text = snapshot_->collection()->names()->GetFormatted( + "The snapshot is too big. " + "Maximum snapshot size is %d MB. " + "Actual snapshot size is %d MB.", + SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB, + (snapshot_->raw_entries_size() + MB - 1) / MB); HeapEntry* message = result->AddEntry( - HeapEntry::kString, "The snapshot is too big", 0, 4, 0, 0); + HeapEntry::kString, text, 0, 4, 0, 0); root->SetUnidirElementReference(0, 1, message); result->SetDominatorsToSelf(); return result; @@ -3512,38 +3590,39 @@ int HeapSnapshotJSONSerializer::GetStringId(const char* s) { void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) { - writer_->AddCharacter(','); - writer_->AddNumber(edge->type()); - writer_->AddCharacter(','); - if (edge->type() == HeapGraphEdge::kElement + // The buffer needs space for 3 ints, 3 commas and \0 + static const int kBufferSize = + MaxDecimalDigitsIn<sizeof(int)>::kSigned * 3 + 3 + 1; // NOLINT + EmbeddedVector<char, kBufferSize> buffer; + int edge_name_or_index = edge->type() == HeapGraphEdge::kElement || edge->type() == HeapGraphEdge::kHidden - || edge->type() == HeapGraphEdge::kWeak) { - writer_->AddNumber(edge->index()); - } else { - writer_->AddNumber(GetStringId(edge->name())); - } - writer_->AddCharacter(','); - writer_->AddNumber(GetNodeId(edge->to())); + || edge->type() == HeapGraphEdge::kWeak + ? edge->index() : GetStringId(edge->name()); + int result = OS::SNPrintF(buffer, ",%d,%d,%d", + edge->type(), edge_name_or_index, GetNodeId(edge->to())); + USE(result); + ASSERT(result != -1); + writer_->AddString(buffer.start()); } void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { - writer_->AddCharacter('\n'); - writer_->AddCharacter(','); - writer_->AddNumber(entry->type()); - writer_->AddCharacter(','); - writer_->AddNumber(GetStringId(entry->name())); - writer_->AddCharacter(','); - writer_->AddNumber(entry->id()); - writer_->AddCharacter(','); - writer_->AddNumber(entry->self_size()); - writer_->AddCharacter(','); - writer_->AddNumber(entry->RetainedSize(false)); - writer_->AddCharacter(','); - writer_->AddNumber(GetNodeId(entry->dominator())); + // The buffer needs space for 7 ints, 7 commas, \n and \0 + static const int kBufferSize = + MaxDecimalDigitsIn<sizeof(int)>::kSigned * 7 + 7 + 1 + 1; // NOLINT + EmbeddedVector<char, kBufferSize> buffer; Vector<HeapGraphEdge> children = entry->children(); - writer_->AddCharacter(','); - writer_->AddNumber(children.length()); + int result = OS::SNPrintF(buffer, "\n,%d,%d,%d,%d,%d,%d,%d", + entry->type(), + GetStringId(entry->name()), + entry->id(), + entry->self_size(), + entry->RetainedSize(false), + GetNodeId(entry->dominator()), + children.length()); + USE(result); + ASSERT(result != -1); + writer_->AddString(buffer.start()); for (int i = 0; i < children.length(); ++i) { SerializeEdge(&children[i]); if (writer_->aborted()) return; |