| Index: src/heap-profiler.cc
|
| diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
|
| index ec078ed1f70ece6477e85efcfebf4628aa838f53..fb1ea8a641e926bf3e108cf715366e46979586bb 100644
|
| --- a/src/heap-profiler.cc
|
| +++ b/src/heap-profiler.cc
|
| @@ -28,294 +28,13 @@
|
| #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) {
|
| @@ -409,14 +128,6 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
|
| 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();
|
| }
|
| @@ -468,705 +179,6 @@ void HeapProfiler::ObjectMoveEvent(Address from, Address to) {
|
| snapshots_->ObjectMoveEvent(from, to);
|
| }
|
|
|
| -
|
| -const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
|
| -const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
|
| -
|
| -
|
| -ConstructorHeapProfile::ConstructorHeapProfile()
|
| - : zscope_(Isolate::Current(), 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_(Isolate::Current(), 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_(Isolate::Current(), 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(Isolate::Current(), 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
|
|
|
|
|
|
|