Revision: 2936
Author: [email protected]
Date: Fri Sep 18 05:05:18 2009
Log: Heap profiler: count the number of back references for objects.

Also, perform some refactoring to reuse common code between constructor and  
retainer profiles.

Review URL: http://codereview.chromium.org/209028
http://code.google.com/p/v8/source/detail?r=2936

Modified:
  /branches/bleeding_edge/src/heap-profiler.cc
  /branches/bleeding_edge/src/heap-profiler.h
  /branches/bleeding_edge/src/log-utils.cc
  /branches/bleeding_edge/src/log-utils.h
  /branches/bleeding_edge/src/log.cc
  /branches/bleeding_edge/src/log.h
  /branches/bleeding_edge/test/cctest/test-heap-profiler.cc

=======================================
--- /branches/bleeding_edge/src/heap-profiler.cc        Wed Sep 16 13:36:08 2009
+++ /branches/bleeding_edge/src/heap-profiler.cc        Fri Sep 18 05:05:18 2009
@@ -37,18 +37,70 @@
  #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 @@
  // 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 @@

    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 @@
  // 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 @@
  }


-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 @@
  }


-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 @@
  }


-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 @@

  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::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);
    }
  }

=======================================
--- /branches/bleeding_edge/src/heap-profiler.h Wed Sep 16 12:44:04 2009
+++ /branches/bleeding_edge/src/heap-profiler.h Fri Sep 18 05:05:18 2009
@@ -46,41 +46,11 @@
  };


-// ConstructorHeapProfile is responsible for gathering and logging
-// "constructor profile" of JS objects allocated on heap.
-// It is run during garbage collection cycle, thus it doesn't need
-// to use handles.
-class ConstructorHeapProfile BASE_EMBEDDED {
- public:
-  ConstructorHeapProfile();
-  virtual ~ConstructorHeapProfile() {}
-  void CollectStats(HeapObject* obj);
-  void PrintStats();
-  // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in  
tests.
-  virtual void Call(String* name, const NumberAndSizeInfo&  
number_and_size);
-
- private:
-  struct TreeConfig {
-    typedef String* Key;
-    typedef NumberAndSizeInfo Value;
-    static const Key kNoKey;
-    static const Value kNoValue;
-    static int Compare(const Key& a, const Key& b) {
-      // Strings are unique, so it is sufficient to compare their pointers.
-      return a == b ? 0 : (a < b ? -1 : 1);
-    }
-  };
-  typedef ZoneSplayTree<TreeConfig> JSObjectsInfoTree;
-
-  ZoneScope zscope_;
-  JSObjectsInfoTree js_objects_info_tree_;
-};
-
-
  // JSObjectsCluster describes a group of JS objects that are
-// considered equivalent in terms of retainer profile.
+// considered equivalent in terms of a particular profile.
  class JSObjectsCluster BASE_EMBEDDED {
   public:
+  // These special cases are used in retainer profile.
    enum SpecialCase {
      ROOTS = 1,
      GLOBAL_PROPERTY = 2
@@ -94,8 +64,8 @@
    JSObjectsCluster(String* constructor, Object* instance)
        : constructor_(constructor), instance_(instance) {}

-  static int CompareConstructors(
-      const JSObjectsCluster& a, const JSObjectsCluster& b) {
+  static int CompareConstructors(const JSObjectsCluster& a,
+                                 const JSObjectsCluster& b) {
      // Strings are unique, so it is sufficient to compare their pointers.
      return a.constructor_ == b.constructor_ ? 0
          : (a.constructor_ < b.constructor_ ? -1 : 1);
@@ -110,6 +80,7 @@

    bool is_null() const { return constructor_ == NULL; }
    bool can_be_coarsed() const { return instance_ != NULL; }
+  String* constructor() const { return constructor_; }

    void Print(StringStream* accumulator) const;
    // Allows null clusters to be printed.
@@ -133,14 +104,47 @@
  };


-struct JSObjectsClusterTreeConfig;
+struct JSObjectsClusterTreeConfig {
+  typedef JSObjectsCluster Key;
+  typedef NumberAndSizeInfo Value;
+  static const Key kNoKey;
+  static const Value kNoValue;
+  static int Compare(const Key& a, const Key& b) {
+    return Key::Compare(a, b);
+  }
+};
  typedef ZoneSplayTree<JSObjectsClusterTreeConfig> JSObjectsClusterTree;

-// JSObjectsClusterTree is used to represent retainer graphs using
-// adjacency list form. That is, the first level maps JS object
-// clusters to adjacency lists, which in their turn are degenerate
-// JSObjectsClusterTrees (their values are NULLs.)
-struct JSObjectsClusterTreeConfig {
+
+// ConstructorHeapProfile is responsible for gathering and logging
+// "constructor profile" of JS objects allocated on heap.
+// It is run during garbage collection cycle, thus it doesn't need
+// to use handles.
+class ConstructorHeapProfile BASE_EMBEDDED {
+ public:
+  ConstructorHeapProfile();
+  virtual ~ConstructorHeapProfile() {}
+  void CollectStats(HeapObject* obj);
+  void PrintStats();
+  // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in  
tests.
+  virtual void Call(const JSObjectsCluster& cluster,
+                    const NumberAndSizeInfo& number_and_size);
+
+ private:
+  ZoneScope zscope_;
+  JSObjectsClusterTree js_objects_info_tree_;
+};
+
+
+// JSObjectsRetainerTree is used to represent retainer graphs using
+// adjacency list form:
+//
+//   Cluster -> (Cluster -> NumberAndSizeInfo)
+//
+// Subordinate splay trees are stored by pointer. They are zone-allocated,
+// so it isn't needed to manage their lifetime.
+//
+struct JSObjectsRetainerTreeConfig {
    typedef JSObjectsCluster Key;
    typedef JSObjectsClusterTree* Value;
    static const Key kNoKey;
@@ -149,6 +153,7 @@
      return Key::Compare(a, b);
    }
  };
+typedef ZoneSplayTree<JSObjectsRetainerTreeConfig> JSObjectsRetainerTree;


  class ClustersCoarser BASE_EMBEDDED {
@@ -156,7 +161,7 @@
    ClustersCoarser();

    // Processes a given retainer graph.
-  void Process(JSObjectsClusterTree* tree);
+  void Process(JSObjectsRetainerTree* tree);

    // Returns an equivalent cluster (can be the cluster itself).
    // If the given cluster doesn't have an equivalent, returns null cluster.
@@ -165,8 +170,10 @@
    // skipped in some cases.
    bool HasAnEquivalent(const JSObjectsCluster& cluster);

-  // Used by ZoneSplayTree::ForEach.
+  // Used by JSObjectsRetainerTree::ForEach.
    void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
+  void Call(const JSObjectsCluster& cluster,
+            const NumberAndSizeInfo& number_and_size);

   private:
    // Stores a list of back references for a cluster.
@@ -194,11 +201,11 @@
    };
    typedef ZoneSplayTree<ClusterEqualityConfig> EqualityTree;

-  static int ClusterBackRefsCmp(
-      const ClusterBackRefs* a, const ClusterBackRefs* b) {
+  static int ClusterBackRefsCmp(const ClusterBackRefs* a,
+                                const ClusterBackRefs* b) {
      return ClusterBackRefs::Compare(*a, *b);
    }
-  int DoProcess(JSObjectsClusterTree* tree);
+  int DoProcess(JSObjectsRetainerTree* tree);
    int FillEqualityTree();

    static const int kInitialBackrefsListCapacity = 2;
@@ -211,7 +218,7 @@
    SimilarityList sim_list_;
    EqualityTree eq_tree_;
    ClusterBackRefs* current_pair_;
-  JSObjectsClusterTree* current_set_;
+  JSObjectsRetainerTree* current_set_;
  };


@@ -224,31 +231,31 @@
    class Printer {
     public:
      virtual ~Printer() {}
-    virtual void PrintRetainers(const StringStream& retainers) = 0;
+    virtual void PrintRetainers(const JSObjectsCluster& cluster,
+                                const StringStream& retainers) = 0;
    };

    RetainerHeapProfile();
    void CollectStats(HeapObject* obj);
    void PrintStats();
    void DebugPrintStats(Printer* printer);
-  void StoreReference(const JSObjectsCluster& cluster, Object* ref);
+  void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);

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


=======================================
--- /branches/bleeding_edge/src/log-utils.cc    Wed Sep  9 03:49:40 2009
+++ /branches/bleeding_edge/src/log-utils.cc    Fri Sep 18 05:05:18 2009
@@ -308,6 +308,18 @@
      }
    }
  }
+
+
+void LogMessageBuilder::AppendStringPart(const char* str, int len) {
+  if (pos_ + len > Log::kMessageBufferSize) {
+    len = Log::kMessageBufferSize - pos_;
+    ASSERT(len >= 0);
+    if (len == 0) return;
+  }
+  strncpy(Log::message_buffer_ + pos_, str, len);
+  pos_ += len;
+  ASSERT(pos_ <= Log::kMessageBufferSize);
+}


  bool LogMessageBuilder::StoreInCompressor(LogRecordCompressor* compressor)  
{
=======================================
--- /branches/bleeding_edge/src/log-utils.h     Wed Sep  9 03:49:40 2009
+++ /branches/bleeding_edge/src/log-utils.h     Fri Sep 18 05:05:18 2009
@@ -113,6 +113,9 @@
    static bool IsEnabled() {
      return !is_stopped_ && (output_handle_ != NULL || output_buffer_ !=  
NULL);
    }
+
+  // Size of buffer used for formatting log messages.
+  static const int kMessageBufferSize = 2048;

   private:
    typedef int (*WritePtr)(const char* msg, int length);
@@ -162,9 +165,6 @@
    // access to the formatting buffer and the log file or log memory buffer.
    static Mutex* mutex_;

-  // Size of buffer used for formatting log messages.
-  static const int kMessageBufferSize = 2048;
-
    // Buffer used for formatting log messages. This is a singleton buffer  
and
    // mutex_ should be acquired before using it.
    static char* message_buffer_;
@@ -247,6 +247,9 @@

    void AppendDetailed(String* str, bool show_impl_info);

+  // Append a portion of a string.
+  void AppendStringPart(const char* str, int len);
+
    // Stores log message into compressor, returns true if the message
    // was stored (i.e. doesn't repeat the previous one).
    bool StoreInCompressor(LogRecordCompressor* compressor);
=======================================
--- /branches/bleeding_edge/src/log.cc  Wed Sep 16 06:41:24 2009
+++ /branches/bleeding_edge/src/log.cc  Fri Sep 18 05:05:18 2009
@@ -889,20 +889,47 @@
  #ifdef ENABLE_LOGGING_AND_PROFILING
    if (!Log::IsEnabled() || !FLAG_log_gc) return;
    LogMessageBuilder msg;
-  msg.Append("heap-js-cons-item,%s,%d,%d\n",
-             constructor[0] != '\0' ? constructor : "(anonymous)",
-             number, bytes);
+  msg.Append("heap-js-cons-item,%s,%d,%d\n", constructor, number, bytes);
    msg.WriteToLogFile();
  #endif
  }


-void Logger::HeapSampleJSRetainersEvent(const char* event) {
+void Logger::HeapSampleJSRetainersEvent(
+    const char* constructor, const char* event) {
  #ifdef ENABLE_LOGGING_AND_PROFILING
    if (!Log::IsEnabled() || !FLAG_log_gc) return;
-  LogMessageBuilder msg;
-  msg.Append("heap-js-ret-item,%s\n", event);
-  msg.WriteToLogFile();
+  // Event starts with comma, so we don't have it in the format string.
+  static const char* event_text = "heap-js-ret-item,%s";
+  // We take placeholder strings into account, but it's OK to be  
conservative.
+  static const int event_text_len = strlen(event_text);
+  const int cons_len = strlen(constructor), event_len = strlen(event);
+  int pos = 0;
+  // Retainer lists can be long. We may need to split them into multiple  
events.
+  do {
+    LogMessageBuilder msg;
+    msg.Append(event_text, constructor);
+    int to_write = event_len - pos;
+    if (to_write > Log::kMessageBufferSize - (cons_len + event_text_len)) {
+      int cut_pos = pos + Log::kMessageBufferSize - (cons_len +  
event_text_len);
+      ASSERT(cut_pos < event_len);
+      while (cut_pos > pos && event[cut_pos] != ',') --cut_pos;
+      if (event[cut_pos] != ',') {
+        // Crash in debug mode, skip in release mode.
+        ASSERT(false);
+        return;
+      }
+      // Append a piece of event that fits, without trailing comma.
+      msg.AppendStringPart(event + pos, cut_pos - pos);
+      // Start next piece with comma.
+      pos = cut_pos;
+    } else {
+      msg.Append("%s", event + pos);
+      pos += event_len;
+    }
+    msg.Append('\n');
+    msg.WriteToLogFile();
+  } while (pos < event_len);
  #endif
  }

=======================================
--- /branches/bleeding_edge/src/log.h   Wed Sep 16 06:41:24 2009
+++ /branches/bleeding_edge/src/log.h   Fri Sep 18 05:05:18 2009
@@ -221,7 +221,8 @@
    static void HeapSampleItemEvent(const char* type, int number, int bytes);
    static void HeapSampleJSConstructorEvent(const char* constructor,
                                             int number, int bytes);
-  static void HeapSampleJSRetainersEvent(const char* event);
+  static void HeapSampleJSRetainersEvent(const char* constructor,
+                                         const char* event);
    static void HeapSampleStats(const char* space, const char* kind,
                                int capacity, int used);

=======================================
--- /branches/bleeding_edge/test/cctest/test-heap-profiler.cc   Wed Sep 16  
06:41:24 2009
+++ /branches/bleeding_edge/test/cctest/test-heap-profiler.cc   Fri Sep 18  
05:05:18 2009
@@ -12,6 +12,7 @@
  namespace i = v8::internal;
  using i::ClustersCoarser;
  using i::JSObjectsCluster;
+using i::JSObjectsRetainerTree;
  using i::JSObjectsClusterTree;
  using i::RetainerHeapProfile;

@@ -31,9 +32,9 @@
        f_count_(0) {
    }

-  void Call(i::String* name, const i::NumberAndSizeInfo& number_and_size) {
-    CHECK(name != NULL);
-    if (f_name_->Equals(name)) {
+  void Call(const JSObjectsCluster& cluster,
+            const i::NumberAndSizeInfo& number_and_size) {
+    if (f_name_->Equals(cluster.constructor())) {
        CHECK_EQ(f_count_, 0);
        f_count_ = number_and_size.number();
        CHECK_GT(f_count_, 0);
@@ -74,7 +75,7 @@


  static JSObjectsCluster AddHeapObjectToTree(
-    JSObjectsClusterTree* tree,
+    JSObjectsRetainerTree* tree,
      i::String* constructor,
      int instance,
      JSObjectsCluster* ref1 = NULL,
@@ -82,10 +83,11 @@
      JSObjectsCluster* ref3 = NULL) {
    JSObjectsCluster o(constructor, reinterpret_cast<i::Object*>(instance));
    JSObjectsClusterTree* o_tree = new JSObjectsClusterTree();
-  JSObjectsClusterTree::Locator loc;
-  if (ref1 != NULL) o_tree->Insert(*ref1, &loc);
-  if (ref2 != NULL) o_tree->Insert(*ref2, &loc);
-  if (ref3 != NULL) o_tree->Insert(*ref3, &loc);
+  JSObjectsClusterTree::Locator o_loc;
+  if (ref1 != NULL) o_tree->Insert(*ref1, &o_loc);
+  if (ref2 != NULL) o_tree->Insert(*ref2, &o_loc);
+  if (ref3 != NULL) o_tree->Insert(*ref3, &o_loc);
+  JSObjectsRetainerTree::Locator loc;
    tree->Insert(o, &loc);
    loc.set_value(o_tree);
    return o;
@@ -137,7 +139,7 @@

    i::ZoneScope zn_scope(i::DELETE_ON_EXIT);

-  JSObjectsClusterTree tree;
+  JSObjectsRetainerTree tree;
    JSObjectsCluster function(i::Heap::function_class_symbol());
    JSObjectsCluster a(*i::Factory::NewStringFromAscii(i::CStrVector("A")));
    JSObjectsCluster b(*i::Factory::NewStringFromAscii(i::CStrVector("B")));
@@ -176,7 +178,7 @@

    i::ZoneScope zn_scope(i::DELETE_ON_EXIT);

-  JSObjectsClusterTree tree;
+  JSObjectsRetainerTree tree;
    JSObjectsCluster function(i::Heap::function_class_symbol());

    // o1 <- Function
@@ -207,7 +209,7 @@

    i::ZoneScope zn_scope(i::DELETE_ON_EXIT);

-  JSObjectsClusterTree tree;
+  JSObjectsRetainerTree tree;

    // On the following graph:
    //
@@ -257,7 +259,9 @@
   public:
    RetainerProfilePrinter() : stream_(&allocator_), lines_(100) {}

-  void PrintRetainers(const i::StringStream& retainers) {
+  void PrintRetainers(const JSObjectsCluster& cluster,
+                      const i::StringStream& retainers) {
+    cluster.Print(&stream_);
      stream_.Add("%s", *(retainers.ToCString()));
      stream_.Put('\0');
    }
@@ -304,8 +308,10 @@
    CompileAndRunScript(
        "function A() {}\n"
        "function B(x) { this.x = x; }\n"
+      "function C(x) { this.x1 = x; this.x2 = x; }\n"
        "var a = new A();\n"
-      "var b = new B(a);\n");
+      "var b1 = new B(a), b2 = new B(a);\n"
+      "var c = new C(a);");

    RetainerHeapProfile ret_profile;
    i::AssertNoAllocation no_alloc;
@@ -316,8 +322,9 @@
    }
    RetainerProfilePrinter printer;
    ret_profile.DebugPrintStats(&printer);
-  CHECK_EQ("(global property),B", printer.GetRetainers("A"));
-  CHECK_EQ("(global property)", printer.GetRetainers("B"));
+  CHECK_EQ("(global property);1,B;2,C;2", printer.GetRetainers("A"));
+  CHECK_EQ("(global property);2", printer.GetRetainers("B"));
+  CHECK_EQ("(global property);1", printer.GetRetainers("C"));
  }

  #endif  // ENABLE_LOGGING_AND_PROFILING

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to