Heap profiler: aggregate retainers count of equivalent clusters.

Also perform some refactoring.

Review URL: http://codereview.chromium.org/247001

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2971 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mikhail.naganov@gmail.com 2009-09-28 07:12:39 +00:00
parent 445bc1ee56
commit f1a89f3379
3 changed files with 134 additions and 66 deletions

View File

@ -162,31 +162,138 @@ class RetainersPrinter : public RetainerHeapProfile::Printer {
};
class RetainerTreePrinter BASE_EMBEDDED {
// Visitor for printing a cluster tree.
class ClusterTreePrinter BASE_EMBEDDED {
public:
explicit RetainerTreePrinter(StringStream* stream) : stream_(stream) {}
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& numNNber_and_size);
const NumberAndSizeInfo& number_and_size);
private:
StringStream* stream_;
};
void RetainerTreePrinter::Print(StringStream* stream,
const JSObjectsCluster& cluster,
const NumberAndSizeInfo& number_and_size) {
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);
}
// A helper class for building a retainers tree, that aggregates
// all equivalent clusters.
class RetainerTreeAggregator BASE_EMBEDDED {
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);
}
} // namespace
@ -356,8 +463,9 @@ JSObjectsCluster ClustersCoarser::GetCoarseEquivalent(
bool ClustersCoarser::HasAnEquivalent(const JSObjectsCluster& cluster) {
// Return true for coarsible clusters that have a non-identical equivalent.
return cluster.can_be_coarsed() &&
JSObjectsCluster::Compare(cluster, GetCoarseEquivalent(cluster)) != 0;
if (!cluster.can_be_coarsed()) return false;
JSObjectsCluster eq = GetCoarseEquivalent(cluster);
return !eq.is_null() && JSObjectsCluster::Compare(cluster, eq) != 0;
}
@ -395,10 +503,7 @@ const JSObjectsRetainerTreeConfig::Value JSObjectsRetainerTreeConfig::kNoValue =
RetainerHeapProfile::RetainerHeapProfile()
: zscope_(DELETE_ON_EXIT),
coarse_cluster_tree_(NULL),
current_printer_(NULL),
current_stream_(NULL) {
: zscope_(DELETE_ON_EXIT) {
JSObjectsCluster roots(JSObjectsCluster::ROOTS);
ReferencesExtractor extractor(roots, this);
Heap::IterateRoots(&extractor);
@ -433,10 +538,15 @@ void RetainerHeapProfile::CollectStats(HeapObject* obj) {
void RetainerHeapProfile::DebugPrintStats(
RetainerHeapProfile::Printer* printer) {
coarser_.Process(&retainers_tree_);
ASSERT(current_printer_ == NULL);
current_printer_ = printer;
retainers_tree_.ForEach(this);
current_printer_ = NULL;
// Print clusters that have no equivalents, aggregating their retainers.
AggregatingRetainerTreePrinter agg_printer(&coarser_, printer);
retainers_tree_.ForEach(&agg_printer);
// Now aggregate clusters that have equivalents...
RetainerTreeAggregator aggregator(&coarser_);
aggregator.Process(&retainers_tree_);
// ...and print them.
SimpleRetainerTreePrinter s_printer(printer);
aggregator.output_tree().ForEach(&s_printer);
}
@ -446,44 +556,6 @@ void RetainerHeapProfile::PrintStats() {
}
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 {
// 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);
}
}
//
// HeapProfiler class implementation.
//

View File

@ -242,20 +242,9 @@ class RetainerHeapProfile BASE_EMBEDDED {
void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);
private:
// Limit on the number of retainers to be printed per cluster.
static const int kMaxRetainersToPrint = 50;
ZoneScope zscope_;
JSObjectsRetainerTree retainers_tree_;
ClustersCoarser coarser_;
// TODO(mnaganov): Use some helper class to hold these state variables.
JSObjectsClusterTree* coarse_cluster_tree_;
Printer* current_printer_;
StringStream* current_stream_;
public:
// Used by JSObjectsRetainerTree::ForEach.
void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
void Call(const JSObjectsCluster& cluster,
const NumberAndSizeInfo& number_and_size);
};

View File

@ -322,7 +322,14 @@ TEST(RetainerProfile) {
}
RetainerProfilePrinter printer;
ret_profile.DebugPrintStats(&printer);
CHECK_EQ("(global property);1,B;2,C;2", printer.GetRetainers("A"));
const char* retainers_of_a = printer.GetRetainers("A");
// The order of retainers is unspecified, so we check string length, and
// verify each retainer separately.
CHECK_EQ(static_cast<int>(strlen("(global property);1,B;2,C;2")),
static_cast<int>(strlen(retainers_of_a)));
CHECK(strstr(retainers_of_a, "(global property);1") != NULL);
CHECK(strstr(retainers_of_a, "B;2") != NULL);
CHECK(strstr(retainers_of_a, "C;2") != NULL);
CHECK_EQ("(global property);2", printer.GetRetainers("B"));
CHECK_EQ("(global property);1", printer.GetRetainers("C"));
}