diff options
Diffstat (limited to 'src/3rdparty/v8/src/heap-profiler.cc')
-rw-r--r-- | src/3rdparty/v8/src/heap-profiler.cc | 1173 |
1 files changed, 1173 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/heap-profiler.cc b/src/3rdparty/v8/src/heap-profiler.cc new file mode 100644 index 0000000..4815f82 --- /dev/null +++ b/src/3rdparty/v8/src/heap-profiler.cc @@ -0,0 +1,1173 @@ +// Copyright 2009-2010 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "heap-profiler.h" +#include "frames-inl.h" +#include "global-handles.h" +#include "profile-generator.h" +#include "string-stream.h" + +namespace v8 { +namespace internal { + + +#ifdef ENABLE_LOGGING_AND_PROFILING +namespace { + +// Clusterizer is a set of helper functions for converting +// object references into clusters. +class Clusterizer : public AllStatic { + public: + static JSObjectsCluster Clusterize(HeapObject* obj) { + return Clusterize(obj, true); + } + static void InsertIntoTree(JSObjectsClusterTree* tree, + HeapObject* obj, bool fine_grain); + static void InsertReferenceIntoTree(JSObjectsClusterTree* tree, + const JSObjectsCluster& cluster) { + InsertIntoTree(tree, cluster, 0); + } + + private: + static JSObjectsCluster Clusterize(HeapObject* obj, bool fine_grain); + static int CalculateNetworkSize(JSObject* obj); + static int GetObjectSize(HeapObject* obj) { + return obj->IsJSObject() ? + CalculateNetworkSize(JSObject::cast(obj)) : obj->Size(); + } + static void InsertIntoTree(JSObjectsClusterTree* tree, + const JSObjectsCluster& cluster, int size); +}; + + +JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) { + if (obj->IsJSObject()) { + JSObject* js_obj = JSObject::cast(obj); + String* constructor = GetConstructorNameForHeapProfile( + JSObject::cast(js_obj)); + // Differentiate Object and Array instances. + if (fine_grain && (constructor == HEAP->Object_symbol() || + constructor == HEAP->Array_symbol())) { + return JSObjectsCluster(constructor, obj); + } else { + return JSObjectsCluster(constructor); + } + } else if (obj->IsString()) { + return JSObjectsCluster(HEAP->String_symbol()); + } else if (obj->IsJSGlobalPropertyCell()) { + return JSObjectsCluster(JSObjectsCluster::GLOBAL_PROPERTY); + } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) { + return JSObjectsCluster(JSObjectsCluster::CODE); + } + return JSObjectsCluster(); +} + + +void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree, + HeapObject* obj, bool fine_grain) { + JSObjectsCluster cluster = Clusterize(obj, fine_grain); + if (cluster.is_null()) return; + InsertIntoTree(tree, cluster, GetObjectSize(obj)); +} + + +void Clusterizer::InsertIntoTree(JSObjectsClusterTree* tree, + const JSObjectsCluster& cluster, int size) { + JSObjectsClusterTree::Locator loc; + tree->Insert(cluster, &loc); + NumberAndSizeInfo number_and_size = loc.value(); + number_and_size.increment_number(1); + number_and_size.increment_bytes(size); + loc.set_value(number_and_size); +} + + +int Clusterizer::CalculateNetworkSize(JSObject* obj) { + int size = obj->Size(); + // If 'properties' and 'elements' are non-empty (thus, non-shared), + // take their size into account. + if (obj->properties() != HEAP->empty_fixed_array()) { + size += obj->properties()->Size(); + } + if (obj->elements() != HEAP->empty_fixed_array()) { + size += obj->elements()->Size(); + } + // For functions, also account non-empty context and literals sizes. + if (obj->IsJSFunction()) { + JSFunction* f = JSFunction::cast(obj); + if (f->unchecked_context()->IsContext()) { + size += f->context()->Size(); + } + if (f->literals()->length() != 0) { + size += f->literals()->Size(); + } + } + return size; +} + + +// A helper class for recording back references. +class ReferencesExtractor : public ObjectVisitor { + public: + ReferencesExtractor(const JSObjectsCluster& cluster, + RetainerHeapProfile* profile) + : cluster_(cluster), + profile_(profile), + inside_array_(false) { + } + + void VisitPointer(Object** o) { + if ((*o)->IsFixedArray() && !inside_array_) { + // Traverse one level deep for data members that are fixed arrays. + // This covers the case of 'elements' and 'properties' of JSObject, + // and function contexts. + inside_array_ = true; + FixedArray::cast(*o)->Iterate(this); + inside_array_ = false; + } else if ((*o)->IsHeapObject()) { + profile_->StoreReference(cluster_, HeapObject::cast(*o)); + } + } + + void VisitPointers(Object** start, Object** end) { + for (Object** p = start; p < end; p++) VisitPointer(p); + } + + private: + const JSObjectsCluster& cluster_; + RetainerHeapProfile* profile_; + bool inside_array_; +}; + + +// A printer interface implementation for the Retainers profile. +class RetainersPrinter : public RetainerHeapProfile::Printer { + public: + void PrintRetainers(const JSObjectsCluster& cluster, + const StringStream& retainers) { + HeapStringAllocator allocator; + StringStream stream(&allocator); + cluster.Print(&stream); + LOG(ISOLATE, + HeapSampleJSRetainersEvent( + *(stream.ToCString()), *(retainers.ToCString()))); + } +}; + + +// Visitor for printing a cluster tree. +class ClusterTreePrinter BASE_EMBEDDED { + public: + explicit ClusterTreePrinter(StringStream* stream) : stream_(stream) {} + void Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + Print(stream_, cluster, number_and_size); + } + static void Print(StringStream* stream, + const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size); + + private: + StringStream* stream_; +}; + + +void ClusterTreePrinter::Print(StringStream* stream, + const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + stream->Put(','); + cluster.Print(stream); + stream->Add(";%d", number_and_size.number()); +} + + +// Visitor for printing a retainer tree. +class SimpleRetainerTreePrinter BASE_EMBEDDED { + public: + explicit SimpleRetainerTreePrinter(RetainerHeapProfile::Printer* printer) + : printer_(printer) {} + void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); + + private: + RetainerHeapProfile::Printer* printer_; +}; + + +void SimpleRetainerTreePrinter::Call(const JSObjectsCluster& cluster, + JSObjectsClusterTree* tree) { + HeapStringAllocator allocator; + StringStream stream(&allocator); + ClusterTreePrinter retainers_printer(&stream); + tree->ForEach(&retainers_printer); + printer_->PrintRetainers(cluster, stream); +} + + +// Visitor for aggregating references count of equivalent clusters. +class RetainersAggregator BASE_EMBEDDED { + public: + RetainersAggregator(ClustersCoarser* coarser, JSObjectsClusterTree* dest_tree) + : coarser_(coarser), dest_tree_(dest_tree) {} + void Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size); + + private: + ClustersCoarser* coarser_; + JSObjectsClusterTree* dest_tree_; +}; + + +void RetainersAggregator::Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster); + if (eq.is_null()) eq = cluster; + JSObjectsClusterTree::Locator loc; + dest_tree_->Insert(eq, &loc); + NumberAndSizeInfo aggregated_number = loc.value(); + aggregated_number.increment_number(number_and_size.number()); + loc.set_value(aggregated_number); +} + + +// Visitor for printing retainers tree. Aggregates equivalent retainer clusters. +class AggregatingRetainerTreePrinter BASE_EMBEDDED { + public: + AggregatingRetainerTreePrinter(ClustersCoarser* coarser, + RetainerHeapProfile::Printer* printer) + : coarser_(coarser), printer_(printer) {} + void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); + + private: + ClustersCoarser* coarser_; + RetainerHeapProfile::Printer* printer_; +}; + + +void AggregatingRetainerTreePrinter::Call(const JSObjectsCluster& cluster, + JSObjectsClusterTree* tree) { + if (!coarser_->GetCoarseEquivalent(cluster).is_null()) return; + JSObjectsClusterTree dest_tree_; + RetainersAggregator retainers_aggregator(coarser_, &dest_tree_); + tree->ForEach(&retainers_aggregator); + HeapStringAllocator allocator; + StringStream stream(&allocator); + ClusterTreePrinter retainers_printer(&stream); + dest_tree_.ForEach(&retainers_printer); + printer_->PrintRetainers(cluster, stream); +} + +} // namespace + + +// A helper class for building a retainers tree, that aggregates +// all equivalent clusters. +class RetainerTreeAggregator { + public: + explicit RetainerTreeAggregator(ClustersCoarser* coarser) + : coarser_(coarser) {} + void Process(JSObjectsRetainerTree* input_tree) { + input_tree->ForEach(this); + } + void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); + JSObjectsRetainerTree& output_tree() { return output_tree_; } + + private: + ClustersCoarser* coarser_; + JSObjectsRetainerTree output_tree_; +}; + + +void RetainerTreeAggregator::Call(const JSObjectsCluster& cluster, + JSObjectsClusterTree* tree) { + JSObjectsCluster eq = coarser_->GetCoarseEquivalent(cluster); + if (eq.is_null()) return; + JSObjectsRetainerTree::Locator loc; + if (output_tree_.Insert(eq, &loc)) { + loc.set_value(new JSObjectsClusterTree()); + } + RetainersAggregator retainers_aggregator(coarser_, loc.value()); + tree->ForEach(&retainers_aggregator); +} + + +HeapProfiler::HeapProfiler() + : snapshots_(new HeapSnapshotsCollection()), + next_snapshot_uid_(1) { +} + + +HeapProfiler::~HeapProfiler() { + delete snapshots_; +} + + +void HeapProfiler::ResetSnapshots() { + delete snapshots_; + snapshots_ = new HeapSnapshotsCollection(); +} + + +#endif // ENABLE_LOGGING_AND_PROFILING + +void HeapProfiler::Setup() { +#ifdef ENABLE_LOGGING_AND_PROFILING + Isolate* isolate = Isolate::Current(); + if (isolate->heap_profiler() == NULL) { + isolate->set_heap_profiler(new HeapProfiler()); + } +#endif +} + + +void HeapProfiler::TearDown() { +#ifdef ENABLE_LOGGING_AND_PROFILING + Isolate* isolate = Isolate::Current(); + delete isolate->heap_profiler(); + isolate->set_heap_profiler(NULL); +#endif +} + + +#ifdef ENABLE_LOGGING_AND_PROFILING + +HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name, + int type, + v8::ActivityControl* control) { + ASSERT(Isolate::Current()->heap_profiler() != NULL); + return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name, + type, + control); +} + + +HeapSnapshot* HeapProfiler::TakeSnapshot(String* name, + int type, + v8::ActivityControl* control) { + ASSERT(Isolate::Current()->heap_profiler() != NULL); + return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name, + type, + control); +} + + +void HeapProfiler::DefineWrapperClass( + uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) { + ASSERT(class_id != v8::HeapProfiler::kPersistentHandleNoClassId); + if (wrapper_callbacks_.length() <= class_id) { + wrapper_callbacks_.AddBlock( + NULL, class_id - wrapper_callbacks_.length() + 1); + } + wrapper_callbacks_[class_id] = callback; +} + + +v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback( + uint16_t class_id, Object** wrapper) { + if (wrapper_callbacks_.length() <= class_id) return NULL; + return wrapper_callbacks_[class_id]( + class_id, Utils::ToLocal(Handle<Object>(wrapper))); +} + + +HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, + int type, + v8::ActivityControl* control) { + HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type); + HeapSnapshot* result = + snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++); + bool generation_completed = true; + switch (s_type) { + case HeapSnapshot::kFull: { + HEAP->CollectAllGarbage(true); + HeapSnapshotGenerator generator(result, control); + generation_completed = generator.GenerateSnapshot(); + break; + } + case HeapSnapshot::kAggregated: { + HEAP->CollectAllGarbage(true); + AggregatedHeapSnapshot agg_snapshot; + AggregatedHeapSnapshotGenerator generator(&agg_snapshot); + generator.GenerateSnapshot(); + generator.FillHeapSnapshot(result); + break; + } + default: + UNREACHABLE(); + } + if (!generation_completed) { + delete result; + result = NULL; + } + snapshots_->SnapshotGenerationFinished(result); + return result; +} + + +HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name, + int type, + v8::ActivityControl* control) { + return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control); +} + + +int HeapProfiler::GetSnapshotsCount() { + HeapProfiler* profiler = Isolate::Current()->heap_profiler(); + ASSERT(profiler != NULL); + return profiler->snapshots_->snapshots()->length(); +} + + +HeapSnapshot* HeapProfiler::GetSnapshot(int index) { + HeapProfiler* profiler = Isolate::Current()->heap_profiler(); + ASSERT(profiler != NULL); + return profiler->snapshots_->snapshots()->at(index); +} + + +HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) { + HeapProfiler* profiler = Isolate::Current()->heap_profiler(); + ASSERT(profiler != NULL); + return profiler->snapshots_->GetSnapshot(uid); +} + + +void HeapProfiler::DeleteAllSnapshots() { + HeapProfiler* profiler = Isolate::Current()->heap_profiler(); + ASSERT(profiler != NULL); + profiler->ResetSnapshots(); +} + + +void HeapProfiler::ObjectMoveEvent(Address from, Address to) { + snapshots_->ObjectMoveEvent(from, to); +} + + +const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey; +const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue; + + +ConstructorHeapProfile::ConstructorHeapProfile() + : zscope_(DELETE_ON_EXIT) { +} + + +void ConstructorHeapProfile::Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + HeapStringAllocator allocator; + StringStream stream(&allocator); + cluster.Print(&stream); + LOG(ISOLATE, + HeapSampleJSConstructorEvent(*(stream.ToCString()), + number_and_size.number(), + number_and_size.bytes())); +} + + +void ConstructorHeapProfile::CollectStats(HeapObject* obj) { + Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false); +} + + +void ConstructorHeapProfile::PrintStats() { + js_objects_info_tree_.ForEach(this); +} + + +static const char* GetConstructorName(const char* name) { + return name[0] != '\0' ? name : "(anonymous)"; +} + + +const char* JSObjectsCluster::GetSpecialCaseName() const { + if (constructor_ == FromSpecialCase(ROOTS)) { + return "(roots)"; + } else if (constructor_ == FromSpecialCase(GLOBAL_PROPERTY)) { + return "(global property)"; + } else if (constructor_ == FromSpecialCase(CODE)) { + return "(code)"; + } else if (constructor_ == FromSpecialCase(SELF)) { + return "(self)"; + } + return NULL; +} + + +void JSObjectsCluster::Print(StringStream* accumulator) const { + ASSERT(!is_null()); + const char* special_case_name = GetSpecialCaseName(); + if (special_case_name != NULL) { + accumulator->Add(special_case_name); + } else { + SmartPointer<char> s_name( + constructor_->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); + accumulator->Add("%s", GetConstructorName(*s_name)); + if (instance_ != NULL) { + accumulator->Add(":%p", static_cast<void*>(instance_)); + } + } +} + + +void JSObjectsCluster::DebugPrint(StringStream* accumulator) const { + if (!is_null()) { + Print(accumulator); + } else { + accumulator->Add("(null cluster)"); + } +} + + +inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs( + const JSObjectsCluster& cluster_) + : cluster(cluster_), refs(kInitialBackrefsListCapacity) { +} + + +inline ClustersCoarser::ClusterBackRefs::ClusterBackRefs( + const ClustersCoarser::ClusterBackRefs& src) + : cluster(src.cluster), refs(src.refs.capacity()) { + refs.AddAll(src.refs); +} + + +inline ClustersCoarser::ClusterBackRefs& + ClustersCoarser::ClusterBackRefs::operator=( + const ClustersCoarser::ClusterBackRefs& src) { + if (this == &src) return *this; + cluster = src.cluster; + refs.Clear(); + refs.AddAll(src.refs); + return *this; +} + + +inline int ClustersCoarser::ClusterBackRefs::Compare( + const ClustersCoarser::ClusterBackRefs& a, + const ClustersCoarser::ClusterBackRefs& b) { + int cmp = JSObjectsCluster::CompareConstructors(a.cluster, b.cluster); + if (cmp != 0) return cmp; + if (a.refs.length() < b.refs.length()) return -1; + if (a.refs.length() > b.refs.length()) return 1; + for (int i = 0; i < a.refs.length(); ++i) { + int cmp = JSObjectsCluster::Compare(a.refs[i], b.refs[i]); + if (cmp != 0) return cmp; + } + return 0; +} + + +ClustersCoarser::ClustersCoarser() + : zscope_(DELETE_ON_EXIT), + sim_list_(ClustersCoarser::kInitialSimilarityListCapacity), + current_pair_(NULL), + current_set_(NULL), + self_(NULL) { +} + + +void ClustersCoarser::Call(const JSObjectsCluster& cluster, + JSObjectsClusterTree* tree) { + if (!cluster.can_be_coarsed()) return; + ClusterBackRefs pair(cluster); + ASSERT(current_pair_ == NULL); + current_pair_ = &pair; + current_set_ = new JSObjectsRetainerTree(); + self_ = &cluster; + tree->ForEach(this); + sim_list_.Add(pair); + current_pair_ = NULL; + current_set_ = NULL; + self_ = NULL; +} + + +void ClustersCoarser::Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + ASSERT(current_pair_ != NULL); + ASSERT(current_set_ != NULL); + ASSERT(self_ != NULL); + JSObjectsRetainerTree::Locator loc; + if (JSObjectsCluster::Compare(*self_, cluster) == 0) { + current_pair_->refs.Add(JSObjectsCluster(JSObjectsCluster::SELF)); + return; + } + JSObjectsCluster eq = GetCoarseEquivalent(cluster); + if (!eq.is_null()) { + if (current_set_->Find(eq, &loc)) return; + current_pair_->refs.Add(eq); + current_set_->Insert(eq, &loc); + } else { + current_pair_->refs.Add(cluster); + } +} + + +void ClustersCoarser::Process(JSObjectsRetainerTree* tree) { + int last_eq_clusters = -1; + for (int i = 0; i < kMaxPassesCount; ++i) { + sim_list_.Clear(); + const int curr_eq_clusters = DoProcess(tree); + // If no new cluster equivalents discovered, abort processing. + if (last_eq_clusters == curr_eq_clusters) break; + last_eq_clusters = curr_eq_clusters; + } +} + + +int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) { + tree->ForEach(this); + sim_list_.Iterate(ClusterBackRefs::SortRefsIterator); + sim_list_.Sort(ClusterBackRefsCmp); + return FillEqualityTree(); +} + + +JSObjectsCluster ClustersCoarser::GetCoarseEquivalent( + const JSObjectsCluster& cluster) { + if (!cluster.can_be_coarsed()) return JSObjectsCluster(); + EqualityTree::Locator loc; + return eq_tree_.Find(cluster, &loc) ? loc.value() : JSObjectsCluster(); +} + + +bool ClustersCoarser::HasAnEquivalent(const JSObjectsCluster& cluster) { + // Return true for coarsible clusters that have a non-identical equivalent. + if (!cluster.can_be_coarsed()) return false; + JSObjectsCluster eq = GetCoarseEquivalent(cluster); + return !eq.is_null() && JSObjectsCluster::Compare(cluster, eq) != 0; +} + + +int ClustersCoarser::FillEqualityTree() { + int eq_clusters_count = 0; + int eq_to = 0; + bool first_added = false; + for (int i = 1; i < sim_list_.length(); ++i) { + if (ClusterBackRefs::Compare(sim_list_[i], sim_list_[eq_to]) == 0) { + EqualityTree::Locator loc; + if (!first_added) { + // Add self-equivalence, if we have more than one item in this + // equivalence class. + eq_tree_.Insert(sim_list_[eq_to].cluster, &loc); + loc.set_value(sim_list_[eq_to].cluster); + first_added = true; + } + eq_tree_.Insert(sim_list_[i].cluster, &loc); + loc.set_value(sim_list_[eq_to].cluster); + ++eq_clusters_count; + } else { + eq_to = i; + first_added = false; + } + } + return eq_clusters_count; +} + + +const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey; +const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue; +const JSObjectsRetainerTreeConfig::Key JSObjectsRetainerTreeConfig::kNoKey; +const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue = + NULL; + + +RetainerHeapProfile::RetainerHeapProfile() + : zscope_(DELETE_ON_EXIT), + aggregator_(NULL) { + JSObjectsCluster roots(JSObjectsCluster::ROOTS); + ReferencesExtractor extractor(roots, this); + HEAP->IterateRoots(&extractor, VISIT_ONLY_STRONG); +} + + +RetainerHeapProfile::~RetainerHeapProfile() { + delete aggregator_; +} + + +void RetainerHeapProfile::StoreReference(const JSObjectsCluster& cluster, + HeapObject* ref) { + JSObjectsCluster ref_cluster = Clusterizer::Clusterize(ref); + if (ref_cluster.is_null()) return; + JSObjectsRetainerTree::Locator ref_loc; + if (retainers_tree_.Insert(ref_cluster, &ref_loc)) { + ref_loc.set_value(new JSObjectsClusterTree()); + } + JSObjectsClusterTree* referenced_by = ref_loc.value(); + Clusterizer::InsertReferenceIntoTree(referenced_by, cluster); +} + + +void RetainerHeapProfile::CollectStats(HeapObject* obj) { + const JSObjectsCluster cluster = Clusterizer::Clusterize(obj); + if (cluster.is_null()) return; + ReferencesExtractor extractor(cluster, this); + obj->Iterate(&extractor); +} + + +void RetainerHeapProfile::CoarseAndAggregate() { + coarser_.Process(&retainers_tree_); + ASSERT(aggregator_ == NULL); + aggregator_ = new RetainerTreeAggregator(&coarser_); + aggregator_->Process(&retainers_tree_); +} + + +void RetainerHeapProfile::DebugPrintStats( + RetainerHeapProfile::Printer* printer) { + // Print clusters that have no equivalents, aggregating their retainers. + AggregatingRetainerTreePrinter agg_printer(&coarser_, printer); + retainers_tree_.ForEach(&agg_printer); + // Print clusters that have equivalents. + SimpleRetainerTreePrinter s_printer(printer); + aggregator_->output_tree().ForEach(&s_printer); +} + + +void RetainerHeapProfile::PrintStats() { + RetainersPrinter printer; + DebugPrintStats(&printer); +} + + +// +// HeapProfiler class implementation. +// +static void StackWeakReferenceCallback(Persistent<Value> object, + void* trace) { + DeleteArray(static_cast<Address*>(trace)); + object.Dispose(); +} + + +static void PrintProducerStackTrace(Object* obj, void* trace) { + if (!obj->IsJSObject()) return; + String* constructor = GetConstructorNameForHeapProfile(JSObject::cast(obj)); + SmartPointer<char> s_name( + constructor->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); + LOG(ISOLATE, + HeapSampleJSProducerEvent(GetConstructorName(*s_name), + reinterpret_cast<Address*>(trace))); +} + + +void HeapProfiler::WriteSample() { + Isolate* isolate = Isolate::Current(); + LOG(isolate, HeapSampleBeginEvent("Heap", "allocated")); + LOG(isolate, + HeapSampleStats( + "Heap", "allocated", HEAP->CommittedMemory(), HEAP->SizeOfObjects())); + + AggregatedHeapSnapshot snapshot; + AggregatedHeapSnapshotGenerator generator(&snapshot); + generator.GenerateSnapshot(); + + HistogramInfo* info = snapshot.info(); + for (int i = FIRST_NONSTRING_TYPE; + i <= AggregatedHeapSnapshotGenerator::kAllStringsType; + ++i) { + if (info[i].bytes() > 0) { + LOG(isolate, + HeapSampleItemEvent(info[i].name(), info[i].number(), + info[i].bytes())); + } + } + + snapshot.js_cons_profile()->PrintStats(); + snapshot.js_retainer_profile()->PrintStats(); + + isolate->global_handles()->IterateWeakRoots(PrintProducerStackTrace, + StackWeakReferenceCallback); + + LOG(isolate, HeapSampleEndEvent("Heap", "allocated")); +} + + +AggregatedHeapSnapshot::AggregatedHeapSnapshot() + : info_(NewArray<HistogramInfo>( + AggregatedHeapSnapshotGenerator::kAllStringsType + 1)) { +#define DEF_TYPE_NAME(name) info_[name].set_name(#name); + INSTANCE_TYPE_LIST(DEF_TYPE_NAME); +#undef DEF_TYPE_NAME + info_[AggregatedHeapSnapshotGenerator::kAllStringsType].set_name( + "STRING_TYPE"); +} + + +AggregatedHeapSnapshot::~AggregatedHeapSnapshot() { + DeleteArray(info_); +} + + +AggregatedHeapSnapshotGenerator::AggregatedHeapSnapshotGenerator( + AggregatedHeapSnapshot* agg_snapshot) + : agg_snapshot_(agg_snapshot) { +} + + +void AggregatedHeapSnapshotGenerator::CalculateStringsStats() { + HistogramInfo* info = agg_snapshot_->info(); + HistogramInfo& strings = info[kAllStringsType]; + // Lump all the string types together. +#define INCREMENT_SIZE(type, size, name, camel_name) \ + strings.increment_number(info[type].number()); \ + strings.increment_bytes(info[type].bytes()); + STRING_TYPE_LIST(INCREMENT_SIZE); +#undef INCREMENT_SIZE +} + + +void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) { + InstanceType type = obj->map()->instance_type(); + ASSERT(0 <= type && type <= LAST_TYPE); + agg_snapshot_->info()[type].increment_number(1); + agg_snapshot_->info()[type].increment_bytes(obj->Size()); +} + + +void AggregatedHeapSnapshotGenerator::GenerateSnapshot() { + HeapIterator iterator(HeapIterator::kFilterUnreachable); + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { + CollectStats(obj); + agg_snapshot_->js_cons_profile()->CollectStats(obj); + agg_snapshot_->js_retainer_profile()->CollectStats(obj); + } + CalculateStringsStats(); + agg_snapshot_->js_retainer_profile()->CoarseAndAggregate(); +} + + +class CountingConstructorHeapProfileIterator { + public: + CountingConstructorHeapProfileIterator() + : entities_count_(0), children_count_(0) { + } + + void Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + ++entities_count_; + children_count_ += number_and_size.number(); + } + + int entities_count() { return entities_count_; } + int children_count() { return children_count_; } + + private: + int entities_count_; + int children_count_; +}; + + +static HeapEntry* AddEntryFromAggregatedSnapshot(HeapSnapshot* snapshot, + int* root_child_index, + HeapEntry::Type type, + const char* name, + int count, + int size, + int children_count, + int retainers_count) { + HeapEntry* entry = snapshot->AddEntry( + type, name, count, size, children_count, retainers_count); + ASSERT(entry != NULL); + snapshot->root()->SetUnidirElementReference(*root_child_index, + *root_child_index + 1, + entry); + *root_child_index = *root_child_index + 1; + return entry; +} + + +class AllocatingConstructorHeapProfileIterator { + public: + AllocatingConstructorHeapProfileIterator(HeapSnapshot* snapshot, + int* root_child_index) + : snapshot_(snapshot), + root_child_index_(root_child_index) { + } + + void Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + const char* name = cluster.GetSpecialCaseName(); + if (name == NULL) { + name = snapshot_->collection()->names()->GetFunctionName( + cluster.constructor()); + } + AddEntryFromAggregatedSnapshot(snapshot_, + root_child_index_, + HeapEntry::kObject, + name, + number_and_size.number(), + number_and_size.bytes(), + 0, + 0); + } + + private: + HeapSnapshot* snapshot_; + int* root_child_index_; +}; + + +static HeapObject* ClusterAsHeapObject(const JSObjectsCluster& cluster) { + return cluster.can_be_coarsed() ? + reinterpret_cast<HeapObject*>(cluster.instance()) : cluster.constructor(); +} + + +static JSObjectsCluster HeapObjectAsCluster(HeapObject* object) { + if (object->IsString()) { + return JSObjectsCluster(String::cast(object)); + } else { + JSObject* js_obj = JSObject::cast(object); + String* constructor = GetConstructorNameForHeapProfile( + JSObject::cast(js_obj)); + return JSObjectsCluster(constructor, object); + } +} + + +class CountingRetainersIterator { + public: + CountingRetainersIterator(const JSObjectsCluster& child_cluster, + HeapEntriesAllocator* allocator, + HeapEntriesMap* map) + : child_(ClusterAsHeapObject(child_cluster)), + allocator_(allocator), + map_(map) { + if (map_->Map(child_) == NULL) + map_->Pair(child_, allocator_, HeapEntriesMap::kHeapEntryPlaceholder); + } + + void Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + if (map_->Map(ClusterAsHeapObject(cluster)) == NULL) + map_->Pair(ClusterAsHeapObject(cluster), + allocator_, + HeapEntriesMap::kHeapEntryPlaceholder); + map_->CountReference(ClusterAsHeapObject(cluster), child_); + } + + private: + HeapObject* child_; + HeapEntriesAllocator* allocator_; + HeapEntriesMap* map_; +}; + + +class AllocatingRetainersIterator { + public: + AllocatingRetainersIterator(const JSObjectsCluster& child_cluster, + HeapEntriesAllocator*, + HeapEntriesMap* map) + : child_(ClusterAsHeapObject(child_cluster)), map_(map) { + child_entry_ = map_->Map(child_); + ASSERT(child_entry_ != NULL); + } + + void Call(const JSObjectsCluster& cluster, + const NumberAndSizeInfo& number_and_size) { + int child_index, retainer_index; + map_->CountReference(ClusterAsHeapObject(cluster), + child_, + &child_index, + &retainer_index); + map_->Map(ClusterAsHeapObject(cluster))->SetIndexedReference( + HeapGraphEdge::kElement, + child_index, + number_and_size.number(), + child_entry_, + retainer_index); + } + + private: + HeapObject* child_; + HeapEntriesMap* map_; + HeapEntry* child_entry_; +}; + + +template<class RetainersIterator> +class AggregatingRetainerTreeIterator { + public: + explicit AggregatingRetainerTreeIterator(ClustersCoarser* coarser, + HeapEntriesAllocator* allocator, + HeapEntriesMap* map) + : coarser_(coarser), allocator_(allocator), map_(map) { + } + + void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree) { + if (coarser_ != NULL && + !coarser_->GetCoarseEquivalent(cluster).is_null()) return; + JSObjectsClusterTree* tree_to_iterate = tree; + ZoneScope zs(DELETE_ON_EXIT); + JSObjectsClusterTree dest_tree_; + if (coarser_ != NULL) { + RetainersAggregator retainers_aggregator(coarser_, &dest_tree_); + tree->ForEach(&retainers_aggregator); + tree_to_iterate = &dest_tree_; + } + RetainersIterator iterator(cluster, allocator_, map_); + tree_to_iterate->ForEach(&iterator); + } + + private: + ClustersCoarser* coarser_; + HeapEntriesAllocator* allocator_; + HeapEntriesMap* map_; +}; + + +class AggregatedRetainerTreeAllocator : public HeapEntriesAllocator { + public: + AggregatedRetainerTreeAllocator(HeapSnapshot* snapshot, + int* root_child_index) + : snapshot_(snapshot), root_child_index_(root_child_index) { + } + ~AggregatedRetainerTreeAllocator() { } + + HeapEntry* AllocateEntry( + HeapThing ptr, int children_count, int retainers_count) { + HeapObject* obj = reinterpret_cast<HeapObject*>(ptr); + JSObjectsCluster cluster = HeapObjectAsCluster(obj); + const char* name = cluster.GetSpecialCaseName(); + if (name == NULL) { + name = snapshot_->collection()->names()->GetFunctionName( + cluster.constructor()); + } + return AddEntryFromAggregatedSnapshot( + snapshot_, root_child_index_, HeapEntry::kObject, name, + 0, 0, children_count, retainers_count); + } + + private: + HeapSnapshot* snapshot_; + int* root_child_index_; +}; + + +template<class Iterator> +void AggregatedHeapSnapshotGenerator::IterateRetainers( + HeapEntriesAllocator* allocator, HeapEntriesMap* entries_map) { + RetainerHeapProfile* p = agg_snapshot_->js_retainer_profile(); + AggregatingRetainerTreeIterator<Iterator> agg_ret_iter_1( + p->coarser(), allocator, entries_map); + p->retainers_tree()->ForEach(&agg_ret_iter_1); + AggregatingRetainerTreeIterator<Iterator> agg_ret_iter_2( + NULL, allocator, entries_map); + p->aggregator()->output_tree().ForEach(&agg_ret_iter_2); +} + + +void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { + // Count the number of entities. + int histogram_entities_count = 0; + int histogram_children_count = 0; + int histogram_retainers_count = 0; + for (int i = FIRST_NONSTRING_TYPE; i <= kAllStringsType; ++i) { + if (agg_snapshot_->info()[i].bytes() > 0) { + ++histogram_entities_count; + } + } + CountingConstructorHeapProfileIterator counting_cons_iter; + agg_snapshot_->js_cons_profile()->ForEach(&counting_cons_iter); + histogram_entities_count += counting_cons_iter.entities_count(); + HeapEntriesMap entries_map; + int root_child_index = 0; + AggregatedRetainerTreeAllocator allocator(snapshot, &root_child_index); + IterateRetainers<CountingRetainersIterator>(&allocator, &entries_map); + histogram_entities_count += entries_map.entries_count(); + histogram_children_count += entries_map.total_children_count(); + histogram_retainers_count += entries_map.total_retainers_count(); + + // Root entry references all other entries. + histogram_children_count += histogram_entities_count; + int root_children_count = histogram_entities_count; + ++histogram_entities_count; + + // Allocate and fill entries in the snapshot, allocate references. + snapshot->AllocateEntries(histogram_entities_count, + histogram_children_count, + histogram_retainers_count); + snapshot->AddRootEntry(root_children_count); + for (int i = FIRST_NONSTRING_TYPE; i <= kAllStringsType; ++i) { + if (agg_snapshot_->info()[i].bytes() > 0) { + AddEntryFromAggregatedSnapshot(snapshot, + &root_child_index, + HeapEntry::kHidden, + agg_snapshot_->info()[i].name(), + agg_snapshot_->info()[i].number(), + agg_snapshot_->info()[i].bytes(), + 0, + 0); + } + } + AllocatingConstructorHeapProfileIterator alloc_cons_iter( + snapshot, &root_child_index); + agg_snapshot_->js_cons_profile()->ForEach(&alloc_cons_iter); + entries_map.AllocateEntries(); + + // Fill up references. + IterateRetainers<AllocatingRetainersIterator>(&allocator, &entries_map); + + snapshot->SetDominatorsToSelf(); +} + + +void ProducerHeapProfile::Setup() { + can_log_ = true; +} + +void ProducerHeapProfile::DoRecordJSObjectAllocation(Object* obj) { + ASSERT(FLAG_log_producers); + if (!can_log_) return; + int framesCount = 0; + for (JavaScriptFrameIterator it; !it.done(); it.Advance()) { + ++framesCount; + } + if (framesCount == 0) return; + ++framesCount; // Reserve place for the terminator item. + Vector<Address> stack(NewArray<Address>(framesCount), framesCount); + int i = 0; + for (JavaScriptFrameIterator it; !it.done(); it.Advance()) { + stack[i++] = it.frame()->pc(); + } + stack[i] = NULL; + Handle<Object> handle = isolate_->global_handles()->Create(obj); + isolate_->global_handles()->MakeWeak(handle.location(), + static_cast<void*>(stack.start()), + StackWeakReferenceCallback); +} + + +#endif // ENABLE_LOGGING_AND_PROFILING + + +} } // namespace v8::internal |