summaryrefslogtreecommitdiff
path: root/src/3rdparty/v8/src/heap-profiler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/v8/src/heap-profiler.cc')
-rw-r--r--src/3rdparty/v8/src/heap-profiler.cc1173
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