| Index: src/heap-profiler.cc
|
| diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
|
| index 46006fe6265bb31f3d6c3dabc88f80f97aff6339..5e945b49a0ca126c72eaba27ccaa746d00039d8b 100644
|
| --- a/src/heap-profiler.cc
|
| +++ b/src/heap-profiler.cc
|
| @@ -37,18 +37,70 @@ namespace internal {
|
| #ifdef ENABLE_LOGGING_AND_PROFILING
|
| namespace {
|
|
|
| -// JSStatsHelper provides service functions for examining
|
| -// JS objects allocated on heap. It is run during garbage
|
| -// collection cycle, thus it doesn't need to use handles.
|
| -class JSStatsHelper {
|
| +// Clusterizer is a set of helper functions for converting
|
| +// object references into clusters.
|
| +class Clusterizer : public AllStatic {
|
| public:
|
| - static int CalculateNetworkSize(JSObject* obj);
|
| + 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:
|
| - DISALLOW_IMPLICIT_CONSTRUCTORS(JSStatsHelper);
|
| + 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);
|
| };
|
|
|
|
|
| -int JSStatsHelper::CalculateNetworkSize(JSObject* obj) {
|
| +JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) {
|
| + if (obj->IsJSObject()) {
|
| + JSObject* js_obj = JSObject::cast(obj);
|
| + String* constructor = JSObject::cast(js_obj)->constructor_name();
|
| + // 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());
|
| + }
|
| + 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.
|
| @@ -65,8 +117,8 @@ int JSStatsHelper::CalculateNetworkSize(JSObject* obj) {
|
| // A helper class for recording back references.
|
| class ReferencesExtractor : public ObjectVisitor {
|
| public:
|
| - ReferencesExtractor(
|
| - const JSObjectsCluster& cluster, RetainerHeapProfile* profile)
|
| + ReferencesExtractor(const JSObjectsCluster& cluster,
|
| + RetainerHeapProfile* profile)
|
| : cluster_(cluster),
|
| profile_(profile),
|
| inside_array_(false) {
|
| @@ -74,7 +126,7 @@ class ReferencesExtractor : public ObjectVisitor {
|
|
|
| void VisitPointer(Object** o) {
|
| if ((*o)->IsJSObject() || (*o)->IsString()) {
|
| - profile_->StoreReference(cluster_, *o);
|
| + profile_->StoreReference(cluster_, HeapObject::cast(*o));
|
| } else 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,
|
| @@ -99,18 +151,47 @@ class ReferencesExtractor : public ObjectVisitor {
|
| // A printer interface implementation for the Retainers profile.
|
| class RetainersPrinter : public RetainerHeapProfile::Printer {
|
| public:
|
| - void PrintRetainers(const StringStream& retainers) {
|
| - LOG(HeapSampleJSRetainersEvent(*(retainers.ToCString())));
|
| + void PrintRetainers(const JSObjectsCluster& cluster,
|
| + const StringStream& retainers) {
|
| + HeapStringAllocator allocator;
|
| + StringStream stream(&allocator);
|
| + cluster.Print(&stream);
|
| + LOG(HeapSampleJSRetainersEvent(
|
| + *(stream.ToCString()), *(retainers.ToCString())));
|
| + }
|
| +};
|
| +
|
| +
|
| +class RetainerTreePrinter BASE_EMBEDDED {
|
| + public:
|
| + explicit RetainerTreePrinter(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& numNNber_and_size);
|
| +
|
| + private:
|
| + StringStream* stream_;
|
| };
|
|
|
| +
|
| +void RetainerTreePrinter::Print(StringStream* stream,
|
| + const JSObjectsCluster& cluster,
|
| + const NumberAndSizeInfo& number_and_size) {
|
| + stream->Put(',');
|
| + cluster.Print(stream);
|
| + stream->Add(";%d", number_and_size.number());
|
| +}
|
| +
|
| +
|
| } // namespace
|
|
|
|
|
| -const ConstructorHeapProfile::TreeConfig::Key
|
| - ConstructorHeapProfile::TreeConfig::kNoKey = NULL;
|
| -const ConstructorHeapProfile::TreeConfig::Value
|
| - ConstructorHeapProfile::TreeConfig::kNoValue;
|
| +const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
|
| +const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue;
|
|
|
|
|
| ConstructorHeapProfile::ConstructorHeapProfile()
|
| @@ -118,39 +199,19 @@ ConstructorHeapProfile::ConstructorHeapProfile()
|
| }
|
|
|
|
|
| -void ConstructorHeapProfile::Call(String* name,
|
| - const NumberAndSizeInfo& number_and_size) {
|
| - ASSERT(name != NULL);
|
| - SmartPointer<char> s_name(
|
| - name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL));
|
| - LOG(HeapSampleJSConstructorEvent(*s_name,
|
| +void ConstructorHeapProfile::Call(const JSObjectsCluster& cluster,
|
| + const NumberAndSizeInfo& number_and_size) {
|
| + HeapStringAllocator allocator;
|
| + StringStream stream(&allocator);
|
| + cluster.Print(&stream);
|
| + LOG(HeapSampleJSConstructorEvent(*(stream.ToCString()),
|
| number_and_size.number(),
|
| number_and_size.bytes()));
|
| }
|
|
|
|
|
| void ConstructorHeapProfile::CollectStats(HeapObject* obj) {
|
| - String* constructor = NULL;
|
| - int size;
|
| - if (obj->IsString()) {
|
| - constructor = Heap::String_symbol();
|
| - size = obj->Size();
|
| - } else if (obj->IsJSObject()) {
|
| - JSObject* js_obj = JSObject::cast(obj);
|
| - constructor = js_obj->constructor_name();
|
| - size = JSStatsHelper::CalculateNetworkSize(js_obj);
|
| - } else {
|
| - return;
|
| - }
|
| -
|
| - JSObjectsInfoTree::Locator loc;
|
| - if (!js_objects_info_tree_.Find(constructor, &loc)) {
|
| - js_objects_info_tree_.Insert(constructor, &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);
|
| + Clusterizer::InsertIntoTree(&js_objects_info_tree_, obj, false);
|
| }
|
|
|
|
|
| @@ -231,37 +292,37 @@ ClustersCoarser::ClustersCoarser()
|
| }
|
|
|
|
|
| -void ClustersCoarser::Call(
|
| - const JSObjectsCluster& cluster, JSObjectsClusterTree* tree) {
|
| - if (tree != NULL) {
|
| - // First level of retainer graph.
|
| - if (!cluster.can_be_coarsed()) return;
|
| - ClusterBackRefs pair(cluster);
|
| - ASSERT(current_pair_ == NULL);
|
| - current_pair_ = &pair;
|
| - current_set_ = new JSObjectsClusterTree();
|
| - tree->ForEach(this);
|
| - sim_list_.Add(pair);
|
| - current_pair_ = NULL;
|
| - current_set_ = 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();
|
| + tree->ForEach(this);
|
| + sim_list_.Add(pair);
|
| + current_pair_ = NULL;
|
| + current_set_ = NULL;
|
| +}
|
| +
|
| +
|
| +void ClustersCoarser::Call(const JSObjectsCluster& cluster,
|
| + const NumberAndSizeInfo& number_and_size) {
|
| + ASSERT(current_pair_ != NULL);
|
| + ASSERT(current_set_ != NULL);
|
| + JSObjectsCluster eq = GetCoarseEquivalent(cluster);
|
| + JSObjectsRetainerTree::Locator loc;
|
| + if (!eq.is_null()) {
|
| + if (current_set_->Find(eq, &loc)) return;
|
| + current_pair_->refs.Add(eq);
|
| + current_set_->Insert(eq, &loc);
|
| } else {
|
| - // Second level of retainer graph.
|
| - ASSERT(current_pair_ != NULL);
|
| - ASSERT(current_set_ != NULL);
|
| - JSObjectsCluster eq = GetCoarseEquivalent(cluster);
|
| - JSObjectsClusterTree::Locator loc;
|
| - 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);
|
| - }
|
| + current_pair_->refs.Add(cluster);
|
| }
|
| }
|
|
|
|
|
| -void ClustersCoarser::Process(JSObjectsClusterTree* tree) {
|
| +void ClustersCoarser::Process(JSObjectsRetainerTree* tree) {
|
| int last_eq_clusters = -1;
|
| for (int i = 0; i < kMaxPassesCount; ++i) {
|
| sim_list_.Clear();
|
| @@ -273,7 +334,7 @@ void ClustersCoarser::Process(JSObjectsClusterTree* tree) {
|
| }
|
|
|
|
|
| -int ClustersCoarser::DoProcess(JSObjectsClusterTree* tree) {
|
| +int ClustersCoarser::DoProcess(JSObjectsRetainerTree* tree) {
|
| tree->ForEach(this);
|
| // To sort similarity list properly, references list of a cluster is
|
| // required to be sorted, thus 'O1 <- A, B' and 'O2 <- B, A' would
|
| @@ -328,60 +389,37 @@ int ClustersCoarser::FillEqualityTree() {
|
|
|
| const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoKey;
|
| const JSObjectsCluster ClustersCoarser::ClusterEqualityConfig::kNoValue;
|
| -const JSObjectsClusterTreeConfig::Key JSObjectsClusterTreeConfig::kNoKey;
|
| -const JSObjectsClusterTreeConfig::Value JSObjectsClusterTreeConfig::kNoValue =
|
| +const JSObjectsRetainerTreeConfig::Key JSObjectsRetainerTreeConfig::kNoKey;
|
| +const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue =
|
| NULL;
|
|
|
|
|
| RetainerHeapProfile::RetainerHeapProfile()
|
| : zscope_(DELETE_ON_EXIT),
|
| coarse_cluster_tree_(NULL),
|
| - retainers_printed_(0),
|
| current_printer_(NULL),
|
| current_stream_(NULL) {
|
| JSObjectsCluster roots(JSObjectsCluster::ROOTS);
|
| - ReferencesExtractor extractor(
|
| - roots, this);
|
| + ReferencesExtractor extractor(roots, this);
|
| Heap::IterateRoots(&extractor);
|
| }
|
|
|
|
|
| -JSObjectsCluster RetainerHeapProfile::Clusterize(Object* obj) {
|
| - if (obj->IsJSObject()) {
|
| - String* constructor = JSObject::cast(obj)->constructor_name();
|
| - // Differentiate Object and Array instances.
|
| - if (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 {
|
| - UNREACHABLE();
|
| - return JSObjectsCluster();
|
| - }
|
| -}
|
| -
|
| -
|
| -void RetainerHeapProfile::StoreReference(
|
| - const JSObjectsCluster& cluster,
|
| - Object* ref) {
|
| - JSObjectsCluster ref_cluster = Clusterize(ref);
|
| - JSObjectsClusterTree::Locator ref_loc;
|
| +void RetainerHeapProfile::StoreReference(const JSObjectsCluster& cluster,
|
| + HeapObject* ref) {
|
| + JSObjectsCluster ref_cluster = Clusterizer::Clusterize(ref);
|
| + JSObjectsRetainerTree::Locator ref_loc;
|
| if (retainers_tree_.Insert(ref_cluster, &ref_loc)) {
|
| ref_loc.set_value(new JSObjectsClusterTree());
|
| }
|
| JSObjectsClusterTree* referenced_by = ref_loc.value();
|
| - JSObjectsClusterTree::Locator obj_loc;
|
| - referenced_by->Insert(cluster, &obj_loc);
|
| + Clusterizer::InsertReferenceIntoTree(referenced_by, cluster);
|
| }
|
|
|
|
|
| void RetainerHeapProfile::CollectStats(HeapObject* obj) {
|
| if (obj->IsJSObject()) {
|
| - const JSObjectsCluster cluster = Clusterize(JSObject::cast(obj));
|
| + const JSObjectsCluster cluster = Clusterizer::Clusterize(obj);
|
| ReferencesExtractor extractor(cluster, this);
|
| obj->Iterate(&extractor);
|
| } else if (obj->IsJSGlobalPropertyCell()) {
|
| @@ -408,50 +446,40 @@ void RetainerHeapProfile::PrintStats() {
|
| }
|
|
|
|
|
| -void RetainerHeapProfile::Call(
|
| - const JSObjectsCluster& cluster,
|
| - JSObjectsClusterTree* tree) {
|
| - ASSERT(current_printer_ != NULL);
|
| - if (tree != NULL) {
|
| - // First level of retainer graph.
|
| - if (coarser_.HasAnEquivalent(cluster)) return;
|
| - ASSERT(current_stream_ == NULL);
|
| - HeapStringAllocator allocator;
|
| - StringStream stream(&allocator);
|
| - current_stream_ = &stream;
|
| - cluster.Print(current_stream_);
|
| - ASSERT(coarse_cluster_tree_ == NULL);
|
| - coarse_cluster_tree_ = new JSObjectsClusterTree();
|
| - retainers_printed_ = 0;
|
| - tree->ForEach(this);
|
| - coarse_cluster_tree_ = NULL;
|
| - current_printer_->PrintRetainers(stream);
|
| - current_stream_ = NULL;
|
| +void RetainerHeapProfile::Call(const JSObjectsCluster& cluster,
|
| + JSObjectsClusterTree* tree) {
|
| + // First level of retainer graph.
|
| + if (coarser_.HasAnEquivalent(cluster)) return;
|
| + ASSERT(current_stream_ == NULL);
|
| + HeapStringAllocator allocator;
|
| + StringStream stream(&allocator);
|
| + current_stream_ = &stream;
|
| + ASSERT(coarse_cluster_tree_ == NULL);
|
| + coarse_cluster_tree_ = new JSObjectsClusterTree();
|
| + tree->ForEach(this);
|
| + // Print aggregated counts and sizes.
|
| + RetainerTreePrinter printer(current_stream_);
|
| + coarse_cluster_tree_->ForEach(&printer);
|
| + coarse_cluster_tree_ = NULL;
|
| + current_printer_->PrintRetainers(cluster, stream);
|
| + current_stream_ = NULL;
|
| +}
|
| +
|
| +
|
| +void RetainerHeapProfile::Call(const JSObjectsCluster& cluster,
|
| + const NumberAndSizeInfo& number_and_size) {
|
| + ASSERT(coarse_cluster_tree_ != NULL);
|
| + ASSERT(current_stream_ != NULL);
|
| + JSObjectsCluster eq = coarser_.GetCoarseEquivalent(cluster);
|
| + if (eq.is_null()) {
|
| + RetainerTreePrinter::Print(current_stream_, cluster, number_and_size);
|
| } else {
|
| - // Second level of retainer graph.
|
| - ASSERT(coarse_cluster_tree_ != NULL);
|
| - ASSERT(current_stream_ != NULL);
|
| - if (retainers_printed_ >= kMaxRetainersToPrint) {
|
| - if (retainers_printed_ == kMaxRetainersToPrint) {
|
| - // TODO(mnaganov): Print the exact count.
|
| - current_stream_->Add(",...");
|
| - ++retainers_printed_; // avoid printing ellipsis next time.
|
| - }
|
| - return;
|
| - }
|
| - JSObjectsCluster eq = coarser_.GetCoarseEquivalent(cluster);
|
| - if (eq.is_null()) {
|
| - current_stream_->Put(',');
|
| - cluster.Print(current_stream_);
|
| - ++retainers_printed_;
|
| - } else {
|
| - JSObjectsClusterTree::Locator loc;
|
| - if (coarse_cluster_tree_->Insert(eq, &loc)) {
|
| - current_stream_->Put(',');
|
| - eq.Print(current_stream_);
|
| - ++retainers_printed_;
|
| - }
|
| - }
|
| + // Aggregate counts and sizes for equivalent clusters.
|
| + JSObjectsClusterTree::Locator loc;
|
| + coarse_cluster_tree_->Insert(eq, &loc);
|
| + NumberAndSizeInfo eq_number_and_size = loc.value();
|
| + eq_number_and_size.increment_number(number_and_size.number());
|
| + loc.set_value(eq_number_and_size);
|
| }
|
| }
|
|
|
|
|