Revision: 5297
Author: mikhail.naga...@gmail.com
Date: Wed Aug 18 01:19:29 2010
Log: Heap profiler: allow returning aggregated snapshots via the new API.

This is intended for smoother migration to the new API in Chromium.
Also, aggregated heap snapshots can be used for cheaply obtaining
heap statistics, e.g. in tests.

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

Modified:
 /branches/bleeding_edge/include/v8-profiler.h
 /branches/bleeding_edge/src/api.cc
 /branches/bleeding_edge/src/heap-profiler.cc
 /branches/bleeding_edge/src/heap-profiler.h
 /branches/bleeding_edge/src/profile-generator-inl.h
 /branches/bleeding_edge/src/profile-generator.cc
 /branches/bleeding_edge/src/profile-generator.h
 /branches/bleeding_edge/test/cctest/test-heap-profiler.cc

=======================================
--- /branches/bleeding_edge/include/v8-profiler.h       Mon Aug  9 04:37:24 2010
+++ /branches/bleeding_edge/include/v8-profiler.h       Wed Aug 18 01:19:29 2010
@@ -260,10 +260,17 @@

   /**
    * Returns node id. For the same heap object, the id remains the same
-   * across all snapshots.
+   * across all snapshots. Not applicable to aggregated heap snapshots
+   * as they only contain aggregated instances.
    */
   uint64_t GetId() const;

+  /**
+   * Returns the number of instances. Only applicable to aggregated
+   * heap snapshots.
+   */
+  int GetInstancesCount() const;
+
   /** Returns node's own size, in bytes. */
   int GetSelfSize() const;

@@ -313,6 +320,15 @@
  */
 class V8EXPORT HeapSnapshot {
  public:
+  enum Type {
+    kFull = 0,       // Heap snapshot with all instances and references.
+    kAggregated = 1  // Snapshot doesn't contain individual heap entries,
+                     //instead they are grouped by constructor name.
+  };
+
+  /** Returns heap snapshot type. */
+  Type GetType() const;
+
   /** Returns heap snapshot UID (assigned by the profiler.) */
   unsigned GetUid() const;

@@ -322,7 +338,10 @@
   /** Returns the root node of the heap graph. */
   const HeapGraphNode* GetRoot() const;

-  /** Returns a diff between this snapshot and another one. */
+  /**
+   * Returns a diff between this snapshot and another one. Only snapshots
+   * of the same type can be compared.
+   */
   const HeapSnapshotsDiff* CompareWith(const HeapSnapshot* snapshot) const;
 };

@@ -341,8 +360,13 @@
   /** Returns a profile by uid. */
   static const HeapSnapshot* FindSnapshot(unsigned uid);

- /** Takes a heap snapshot and returns it. Title may be an empty string. */
-  static const HeapSnapshot* TakeSnapshot(Handle<String> title);
+  /**
+   * Takes a heap snapshot and returns it. Title may be an empty string.
+   * See HeapSnapshot::Type for types description.
+   */
+  static const HeapSnapshot* TakeSnapshot(
+      Handle<String> title,
+      HeapSnapshot::Type type = HeapSnapshot::kFull);
 };


=======================================
--- /branches/bleeding_edge/src/api.cc  Mon Aug 16 04:53:52 2010
+++ /branches/bleeding_edge/src/api.cc  Wed Aug 18 01:19:29 2010
@@ -4592,8 +4592,16 @@

 uint64_t HeapGraphNode::GetId() const {
   IsDeadCheck("v8::HeapGraphNode::GetId");
+ ASSERT(ToInternal(this)->snapshot()->type() != i::HeapSnapshot::kAggregated);
   return ToInternal(this)->id();
 }
+
+
+int HeapGraphNode::GetInstancesCount() const {
+  IsDeadCheck("v8::HeapGraphNode::GetInstancesCount");
+ ASSERT(ToInternal(this)->snapshot()->type() == i::HeapSnapshot::kAggregated);
+  return static_cast<int>(ToInternal(this)->id());
+}


 int HeapGraphNode::GetSelfSize() const {
@@ -4675,6 +4683,12 @@
   return const_cast<i::HeapSnapshot*>(
       reinterpret_cast<const i::HeapSnapshot*>(snapshot));
 }
+
+
+HeapSnapshot::Type HeapSnapshot::GetType() const {
+  IsDeadCheck("v8::HeapSnapshot::GetType");
+  return static_cast<HeapSnapshot::Type>(ToInternal(this)->type());
+}


 unsigned HeapSnapshot::GetUid() const {
@@ -4724,10 +4738,22 @@
 }


-const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title) {
+const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title,
+                                               HeapSnapshot::Type type) {
   IsDeadCheck("v8::HeapProfiler::TakeSnapshot");
+  i::HeapSnapshot::Type internal_type = i::HeapSnapshot::kFull;
+  switch (type) {
+    case HeapSnapshot::kFull:
+      internal_type = i::HeapSnapshot::kFull;
+      break;
+    case HeapSnapshot::kAggregated:
+      internal_type = i::HeapSnapshot::kAggregated;
+      break;
+    default:
+      UNREACHABLE();
+  }
   return reinterpret_cast<const HeapSnapshot*>(
-      i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title)));
+ i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title), internal_type));
 }

 #endif  // ENABLE_LOGGING_AND_PROFILING
=======================================
--- /branches/bleeding_edge/src/heap-profiler.cc        Wed Aug 11 02:54:30 2010
+++ /branches/bleeding_edge/src/heap-profiler.cc        Wed Aug 18 01:19:29 2010
@@ -280,10 +280,12 @@
   printer_->PrintRetainers(cluster, stream);
 }

+}  // namespace
+

 // A helper class for building a retainers tree, that aggregates
 // all equivalent clusters.
-class RetainerTreeAggregator BASE_EMBEDDED {
+class RetainerTreeAggregator {
  public:
   explicit RetainerTreeAggregator(ClustersCoarser* coarser)
       : coarser_(coarser) {}
@@ -311,8 +313,6 @@
   tree->ForEach(&retainers_aggregator);
 }

-}  // namespace
-

 HeapProfiler* HeapProfiler::singleton_ = NULL;

@@ -347,30 +347,46 @@

 #ifdef ENABLE_LOGGING_AND_PROFILING

-HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name) {
+HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name, int type) {
   ASSERT(singleton_ != NULL);
-  return singleton_->TakeSnapshotImpl(name);
+  return singleton_->TakeSnapshotImpl(name, type);
 }


-HeapSnapshot* HeapProfiler::TakeSnapshot(String* name) {
+HeapSnapshot* HeapProfiler::TakeSnapshot(String* name, int type) {
   ASSERT(singleton_ != NULL);
-  return singleton_->TakeSnapshotImpl(name);
+  return singleton_->TakeSnapshotImpl(name, type);
 }


-HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name) {
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, int type) {
   Heap::CollectAllGarbage(true);
- HeapSnapshot* result = snapshots_->NewSnapshot(name, next_snapshot_uid_++);
-  HeapSnapshotGenerator generator(result);
-  generator.GenerateSnapshot();
+  HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type);
+  HeapSnapshot* result =
+      snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++);
+  switch (s_type) {
+    case HeapSnapshot::kFull: {
+      HeapSnapshotGenerator generator(result);
+      generator.GenerateSnapshot();
+      break;
+    }
+    case HeapSnapshot::kAggregated: {
+      AggregatedHeapSnapshot agg_snapshot;
+      AggregatedHeapSnapshotGenerator generator(&agg_snapshot);
+      generator.GenerateSnapshot();
+      generator.FillHeapSnapshot(result);
+      break;
+    }
+    default:
+      UNREACHABLE();
+  }
   snapshots_->SnapshotGenerationFinished();
   return result;
 }


-HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name) {
-  return TakeSnapshotImpl(snapshots_->GetName(name));
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name, int type) {
+  return TakeSnapshotImpl(snapshots_->GetName(name), type);
 }


@@ -433,16 +449,25 @@
 }


-void JSObjectsCluster::Print(StringStream* accumulator) const {
-  ASSERT(!is_null());
+const char* JSObjectsCluster::GetSpecialCaseName() const {
   if (constructor_ == FromSpecialCase(ROOTS)) {
-    accumulator->Add("(roots)");
+    return "(roots)";
   } else if (constructor_ == FromSpecialCase(GLOBAL_PROPERTY)) {
-    accumulator->Add("(global property)");
+    return "(global property)";
   } else if (constructor_ == FromSpecialCase(CODE)) {
-    accumulator->Add("(code)");
+    return "(code)";
   } else if (constructor_ == FromSpecialCase(SELF)) {
-    accumulator->Add("(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));
@@ -618,11 +643,17 @@


 RetainerHeapProfile::RetainerHeapProfile()
-    : zscope_(DELETE_ON_EXIT) {
+    : 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,
@@ -644,20 +675,24 @@
   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) {
-  coarser_.Process(&retainers_tree_);
   // 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.
+  // Print clusters that have equivalents.
   SimpleRetainerTreePrinter s_printer(printer);
-  aggregator.output_tree().ForEach(&s_printer);
+  aggregator_->output_tree().ForEach(&s_printer);
 }


@@ -670,16 +705,6 @@
 //
 // HeapProfiler class implementation.
 //
-void HeapProfiler::CollectStats(HeapObject* obj, HistogramInfo* info) {
-  InstanceType type = obj->map()->instance_type();
-  ASSERT(0 <= type && type <= LAST_TYPE);
-  if (!FreeListNode::IsFreeListNode(obj)) {
-    info[type].increment_number(1);
-    info[type].increment_bytes(obj->Size());
-  }
-}
-
-
 static void StackWeakReferenceCallback(Persistent<Value> object,
                                        void* trace) {
   DeleteArray(static_cast<Address*>(trace));
@@ -702,46 +727,339 @@
   LOG(HeapSampleStats(
"Heap", "allocated", Heap::CommittedMemory(), Heap::SizeOfObjects()));

-  HistogramInfo info[LAST_TYPE+1];
-#define DEF_TYPE_NAME(name) info[name].set_name(#name);
-  INSTANCE_TYPE_LIST(DEF_TYPE_NAME)
-#undef DEF_TYPE_NAME
-
-  ConstructorHeapProfile js_cons_profile;
-  RetainerHeapProfile js_retainer_profile;
-  HeapIterator iterator;
- for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
-    CollectStats(obj, info);
-    js_cons_profile.CollectStats(obj);
-    js_retainer_profile.CollectStats(obj);
+  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(HeapSampleItemEvent(info[i].name(), info[i].number(),
+                              info[i].bytes()));
+    }
   }

+  snapshot.js_cons_profile()->PrintStats();
+  snapshot.js_retainer_profile()->PrintStats();
+
+  GlobalHandles::IterateWeakRoots(PrintProducerStackTrace,
+                                  StackWeakReferenceCallback);
+
+  LOG(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.
-  int string_number = 0;
-  int string_bytes = 0;
 #define INCREMENT_SIZE(type, size, name, camel_name)   \
-    string_number += info[type].number();              \
-    string_bytes += info[type].bytes();
-  STRING_TYPE_LIST(INCREMENT_SIZE)
+  strings.increment_number(info[type].number());       \
+  strings.increment_bytes(info[type].bytes());
+  STRING_TYPE_LIST(INCREMENT_SIZE);
 #undef INCREMENT_SIZE
-  if (string_bytes > 0) {
-    LOG(HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes));
-  }
-
-  for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) {
-    if (info[i].bytes() > 0) {
-      LOG(HeapSampleItemEvent(info[i].name(), info[i].number(),
-                              info[i].bytes()));
-    }
+}
+
+
+void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) {
+  InstanceType type = obj->map()->instance_type();
+  ASSERT(0 <= type && type <= LAST_TYPE);
+  if (!FreeListNode::IsFreeListNode(obj)) {
+    agg_snapshot_->info()[type].increment_number(1);
+    agg_snapshot_->info()[type].increment_bytes(obj->Size());
+  }
+}
+
+
+void AggregatedHeapSnapshotGenerator::GenerateSnapshot() {
+  HeapIterator iterator;
+ 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()->GetFunctionName(cluster.constructor());
+    }
+    AddEntryFromAggregatedSnapshot(snapshot_,
+                                   root_child_index_,
+                                   HeapEntry::kObject,
+                                   name,
+                                   number_and_size.number(),
+                                   number_and_size.bytes(),
+                                   0,
+                                   0);
   }

-  js_cons_profile.PrintStats();
-  js_retainer_profile.PrintStats();
-
-  GlobalHandles::IterateWeakRoots(PrintProducerStackTrace,
-                                  StackWeakReferenceCallback);
-
-  LOG(HeapSampleEndEvent("Heap", "allocated"));
+ 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 = JSObject::cast(js_obj)->constructor_name();
+    return JSObjectsCluster(constructor, object);
+  }
+}
+
+
+class CountingRetainersIterator {
+ public:
+  CountingRetainersIterator(const JSObjectsCluster& child_cluster,
+                            HeapEntriesMap* map)
+      : child_(ClusterAsHeapObject(child_cluster)), map_(map) {
+    if (map_->Map(child_) == NULL)
+      map_->Pair(child_, HeapEntriesMap::kHeapEntryPlaceholder);
+  }
+
+  void Call(const JSObjectsCluster& cluster,
+            const NumberAndSizeInfo& number_and_size) {
+    if (map_->Map(ClusterAsHeapObject(cluster)) == NULL)
+      map_->Pair(ClusterAsHeapObject(cluster),
+                 HeapEntriesMap::kHeapEntryPlaceholder);
+    map_->CountReference(ClusterAsHeapObject(cluster), child_);
+  }
+
+ private:
+  HeapObject* child_;
+  HeapEntriesMap* map_;
+};
+
+
+class AllocatingRetainersIterator {
+ public:
+  AllocatingRetainersIterator(const JSObjectsCluster& child_cluster,
+                              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))->SetElementReference(
+ 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,
+                                           HeapEntriesMap* map)
+      : coarser_(coarser), 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, map_);
+    tree_to_iterate->ForEach(&iterator);
+  }
+
+ private:
+  ClustersCoarser* coarser_;
+  HeapEntriesMap* map_;
+};
+
+
+class AggregatedRetainerTreeAllocator {
+ public:
+  AggregatedRetainerTreeAllocator(HeapSnapshot* snapshot,
+                                  int* root_child_index)
+      : snapshot_(snapshot), root_child_index_(root_child_index) {
+  }
+
+  HeapEntry* GetEntry(
+      HeapObject* obj, int children_count, int retainers_count) {
+    JSObjectsCluster cluster = HeapObjectAsCluster(obj);
+    const char* name = cluster.GetSpecialCaseName();
+    if (name == NULL) {
+ name = snapshot_->collection()->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(
+    HeapEntriesMap* entries_map) {
+  RetainerHeapProfile* p = agg_snapshot_->js_retainer_profile();
+  AggregatingRetainerTreeIterator<Iterator> agg_ret_iter_1(
+      p->coarser(), entries_map);
+  p->retainers_tree()->ForEach(&agg_ret_iter_1);
+ AggregatingRetainerTreeIterator<Iterator> agg_ret_iter_2(NULL, 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;
+  IterateRetainers<CountingRetainersIterator>(&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->AddEntry(HeapSnapshot::kInternalRootObject,
+                     root_children_count,
+                     0);
+  int root_child_index = 0;
+  for (int i = FIRST_NONSTRING_TYPE; i <= kAllStringsType; ++i) {
+    if (agg_snapshot_->info()[i].bytes() > 0) {
+      AddEntryFromAggregatedSnapshot(snapshot,
+                                     &root_child_index,
+                                     HeapEntry::kInternal,
+                                     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);
+  AggregatedRetainerTreeAllocator allocator(snapshot, &root_child_index);
+  entries_map.UpdateEntries(&allocator);
+
+  // Fill up references.
+  IterateRetainers<AllocatingRetainersIterator>(&entries_map);
 }


=======================================
--- /branches/bleeding_edge/src/heap-profiler.h Thu Jul 15 06:21:50 2010
+++ /branches/bleeding_edge/src/heap-profiler.h Wed Aug 18 01:19:29 2010
@@ -56,8 +56,8 @@
   static void TearDown();

 #ifdef ENABLE_LOGGING_AND_PROFILING
-  static HeapSnapshot* TakeSnapshot(const char* name);
-  static HeapSnapshot* TakeSnapshot(String* name);
+  static HeapSnapshot* TakeSnapshot(const char* name, int type);
+  static HeapSnapshot* TakeSnapshot(String* name, int type);
   static int GetSnapshotsCount();
   static HeapSnapshot* GetSnapshot(int index);
   static HeapSnapshot* FindSnapshot(unsigned uid);
@@ -75,12 +75,8 @@
  private:
   HeapProfiler();
   ~HeapProfiler();
-  HeapSnapshot* TakeSnapshotImpl(const char* name);
-  HeapSnapshot* TakeSnapshotImpl(String* name);
-
-  // Obsolete interface.
-  // Update the array info with stats from obj.
-  static void CollectStats(HeapObject* obj, HistogramInfo* info);
+  HeapSnapshot* TakeSnapshotImpl(const char* name, int type);
+  HeapSnapshot* TakeSnapshotImpl(String* name, int type);

   HeapSnapshotsCollection* snapshots_;
   unsigned next_snapshot_uid_;
@@ -132,7 +128,9 @@
   bool is_null() const { return constructor_ == NULL; }
   bool can_be_coarsed() const { return instance_ != NULL; }
   String* constructor() const { return constructor_; }
-
+  Object* instance() const { return instance_; }
+
+  const char* GetSpecialCaseName() const;
   void Print(StringStream* accumulator) const;
   // Allows null clusters to be printed.
   void DebugPrint(StringStream* accumulator) const;
@@ -179,6 +177,9 @@
   virtual ~ConstructorHeapProfile() {}
   void CollectStats(HeapObject* obj);
   void PrintStats();
+
+  template<class Callback>
+ void ForEach(Callback* callback) { js_objects_info_tree_.ForEach(callback); } // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests.
   virtual void Call(const JSObjectsCluster& cluster,
                     const NumberAndSizeInfo& number_and_size);
@@ -282,6 +283,8 @@
 // "retainer profile" of JS objects allocated on heap.
 // It is run during garbage collection cycle, thus it doesn't need
 // to use handles.
+class RetainerTreeAggregator;
+
 class RetainerHeapProfile BASE_EMBEDDED {
  public:
   class Printer {
@@ -292,7 +295,14 @@
   };

   RetainerHeapProfile();
+  ~RetainerHeapProfile();
+
+  RetainerTreeAggregator* aggregator() { return aggregator_; }
+  ClustersCoarser* coarser() { return &coarser_; }
+  JSObjectsRetainerTree* retainers_tree() { return &retainers_tree_; }
+
   void CollectStats(HeapObject* obj);
+  void CoarseAndAggregate();
   void PrintStats();
   void DebugPrintStats(Printer* printer);
   void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);
@@ -301,6 +311,44 @@
   ZoneScope zscope_;
   JSObjectsRetainerTree retainers_tree_;
   ClustersCoarser coarser_;
+  RetainerTreeAggregator* aggregator_;
+};
+
+
+class AggregatedHeapSnapshot {
+ public:
+  AggregatedHeapSnapshot();
+  ~AggregatedHeapSnapshot();
+
+  HistogramInfo* info() { return info_; }
+  ConstructorHeapProfile* js_cons_profile() { return &js_cons_profile_; }
+ RetainerHeapProfile* js_retainer_profile() { return &js_retainer_profile_; }
+
+ private:
+  HistogramInfo* info_;
+  ConstructorHeapProfile js_cons_profile_;
+  RetainerHeapProfile js_retainer_profile_;
+};
+
+
+class HeapEntriesMap;
+class HeapSnapshot;
+
+class AggregatedHeapSnapshotGenerator {
+ public:
+ explicit AggregatedHeapSnapshotGenerator(AggregatedHeapSnapshot* snapshot);
+  void GenerateSnapshot();
+  void FillHeapSnapshot(HeapSnapshot* snapshot);
+
+  static const int kAllStringsType = LAST_TYPE + 1;
+
+ private:
+  void CalculateStringsStats();
+  void CollectStats(HeapObject* obj);
+  template<class Iterator>
+  void IterateRetainers(HeapEntriesMap* entries_map);
+
+  AggregatedHeapSnapshot* agg_snapshot_;
 };


=======================================
--- /branches/bleeding_edge/src/profile-generator-inl.h Tue Aug 10 05:06:42 2010 +++ /branches/bleeding_edge/src/profile-generator-inl.h Wed Aug 18 01:19:29 2010
@@ -35,6 +35,16 @@
 namespace v8 {
 namespace internal {

+const char* StringsStorage::GetFunctionName(String* name) {
+  return GetFunctionName(GetName(name));
+}
+
+
+const char* StringsStorage::GetFunctionName(const char* name) {
+ return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName;
+}
+
+
 CodeEntry::CodeEntry(int security_token_id)
     : call_uid_(0),
       tag_(Logger::FUNCTION_TAG),
@@ -97,13 +107,21 @@
 }


-const char* CpuProfilesCollection::GetFunctionName(String* name) {
-  return GetFunctionName(GetName(name));
-}
-
-
-const char* CpuProfilesCollection::GetFunctionName(const char* name) {
- return strlen(name) > 0 ? name : ProfileGenerator::kAnonymousFunctionName;
+template<class Visitor>
+void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
+  for (HashMap::Entry* p = entries_.Start();
+       p != NULL;
+       p = entries_.Next(p)) {
+    if (!IsAlias(p->value)) {
+      EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
+      entry_info->entry = visitor->GetEntry(
+          reinterpret_cast<HeapObject*>(p->key),
+          entry_info->children_count,
+          entry_info->retainers_count);
+      entry_info->children_count = 0;
+      entry_info->retainers_count = 0;
+    }
+  }
 }


=======================================
--- /branches/bleeding_edge/src/profile-generator.cc Wed Aug 11 02:03:23 2010 +++ /branches/bleeding_edge/src/profile-generator.cc Wed Aug 18 01:19:29 2010
@@ -821,13 +821,6 @@
 HeapEntry* HeapGraphEdge::From() {
   return reinterpret_cast<HeapEntry*>(this - child_index_) - 1;
 }
-
-
-void HeapEntry::Init(HeapSnapshot* snapshot,
-                     int children_count,
-                     int retainers_count) {
-  Init(snapshot, kInternal, "", 0, 0, children_count, retainers_count);
-}


 void HeapEntry::Init(HeapSnapshot* snapshot,
@@ -1210,9 +1203,11 @@
 }  // namespace

 HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
+                           HeapSnapshot::Type type,
                            const char* title,
                            unsigned uid)
     : collection_(collection),
+      type_(type),
       title_(title),
       uid_(uid),
       root_entry_index_(-1),
@@ -1243,6 +1238,10 @@
   ASSERT(raw_entries_ == NULL);
   raw_entries_ = NewArray<char>(
HeapEntry::EntriesSize(entries_count, children_count, retainers_count));
+#ifdef DEBUG
+  raw_entries_size_ =
+ HeapEntry::EntriesSize(entries_count, children_count, retainers_count);
+#endif
 }


@@ -1252,9 +1251,9 @@
   if (object == kInternalRootObject) {
     ASSERT(root_entry_index_ == -1);
     root_entry_index_ = entries_.length();
-    HeapEntry* entry = GetNextEntryToInit();
-    entry->Init(this, children_count, retainers_count);
-    return entry;
+    ASSERT(retainers_count == 0);
+    return AddEntry(
+        HeapEntry::kInternal, "", 0, 0, children_count, retainers_count);
   } else if (object->IsJSFunction()) {
     JSFunction* func = JSFunction::cast(object);
     SharedFunctionInfo* shared = func->shared();
@@ -1262,7 +1261,7 @@
         String::cast(shared->name()) : shared->inferred_name();
     return AddEntry(object,
                     HeapEntry::kClosure,
-                    collection_->GetName(name),
+                    collection_->GetFunctionName(name),
                     children_count,
                     retainers_count);
   } else if (object->IsJSObject()) {
@@ -1290,7 +1289,7 @@
         String::cast(shared->name()) : shared->inferred_name();
     return AddEntry(object,
                     HeapEntry::kCode,
-                    collection_->GetName(name),
+                    collection_->GetFunctionName(name),
                     children_count,
                     retainers_count);
   } else if (object->IsScript()) {
@@ -1343,16 +1342,25 @@
 HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
                                   HeapEntry::Type type,
                                   const char* name,
+                                  int children_count,
+                                  int retainers_count) {
+  return AddEntry(type,
+                  name,
+                  collection_->GetObjectId(object->address()),
+                  GetObjectSize(object),
+                  children_count,
+                  retainers_count);
+}
+
+
+HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
+                                  const char* name,
+                                  uint64_t id,
+                                  int size,
                                   int children_count,
                                   int retainers_count) {
   HeapEntry* entry = GetNextEntryToInit();
-  entry->Init(this,
-              type,
-              name,
-              collection_->GetObjectId(object->address()),
-              GetObjectSize(object),
-              children_count,
-              retainers_count);
+  entry->Init(this, type, name, id, size, children_count, retainers_count);
   return entry;
 }

@@ -1365,6 +1373,8 @@
   } else {
     entries_.Add(reinterpret_cast<HeapEntry*>(raw_entries_));
   }
+  ASSERT(reinterpret_cast<char*>(entries_.last()) <
+         (raw_entries_ + raw_entries_size_));
   return entries_.last();
 }

@@ -1534,10 +1544,11 @@
 }


-HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
+HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(HeapSnapshot::Type type,
+                                                   const char* name,
                                                    unsigned uid) {
   is_tracking_objects_ = true;  // Start watching for heap objects moves.
-  HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid);
+  HeapSnapshot* snapshot = new HeapSnapshot(this, type, name, uid);
   snapshots_.Add(snapshot);
   HashMap::Entry* entry =
       snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
@@ -1564,6 +1575,9 @@
 }


+HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder =
+    reinterpret_cast<HeapEntry*>(1);
+
 HeapEntriesMap::HeapEntriesMap()
     : entries_(HeapObjectsMatch),
       entries_count_(0),
@@ -1612,7 +1626,7 @@
 void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to,
                                     int* prev_children_count,
                                     int* prev_retainers_count) {
- HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), true); + HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), false);
   HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false);
   ASSERT(from_cache_entry != NULL);
   ASSERT(to_cache_entry != NULL);
@@ -1629,24 +1643,6 @@
   ++total_children_count_;
   ++total_retainers_count_;
 }
-
-
-template<class Visitor>
-void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
-  for (HashMap::Entry* p = entries_.Start();
-       p != NULL;
-       p = entries_.Next(p)) {
-    if (!IsAlias(p->value)) {
-      EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value);
-      entry_info->entry = visitor->GetEntry(
-          reinterpret_cast<HeapObject*>(p->key),
-          entry_info->children_count,
-          entry_info->retainers_count);
-      entry_info->children_count = 0;
-      entry_info->retainers_count = 0;
-    }
-  }
-}


 HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
@@ -1654,19 +1650,14 @@
       collection_(snapshot->collection()),
       filler_(NULL) {
 }
-
-
-HeapEntry *const
-HeapSnapshotGenerator::SnapshotFillerInterface::kHeapEntryPlaceholder =
-    reinterpret_cast<HeapEntry*>(1);

class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface {
  public:
   explicit SnapshotCounter(HeapEntriesMap* entries)
       : entries_(entries) { }
   HeapEntry* AddEntry(HeapObject* obj) {
-    entries_->Pair(obj, kHeapEntryPlaceholder);
-    return kHeapEntryPlaceholder;
+    entries_->Pair(obj, HeapEntriesMap::kHeapEntryPlaceholder);
+    return HeapEntriesMap::kHeapEntryPlaceholder;
   }
   void SetElementReference(HeapObject* parent_obj,
                            HeapEntry*,
@@ -2057,10 +2048,12 @@
void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) {
   raw_additions_root_ =
       NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0));
-  additions_root()->Init(snapshot2_, additions_count, 0);
+  additions_root()->Init(
+      snapshot2_, HeapEntry::kInternal, "", 0, 0, additions_count, 0);
   raw_deletions_root_ =
       NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0));
-  deletions_root()->Init(snapshot1_, deletions_count, 0);
+  deletions_root()->Init(
+      snapshot1_, HeapEntry::kInternal, "", 0, 0, deletions_count, 0);
 }


=======================================
--- /branches/bleeding_edge/src/profile-generator.h     Wed Aug 11 02:03:23 2010
+++ /branches/bleeding_edge/src/profile-generator.h     Wed Aug 18 01:19:29 2010
@@ -67,6 +67,8 @@
   ~StringsStorage();

   const char* GetName(String* name);
+  inline const char* GetFunctionName(String* name);
+  inline const char* GetFunctionName(const char* name);

  private:
   INLINE(static bool StringsMatch(void* key1, void* key2)) {
@@ -298,9 +300,13 @@
   void AddPathToCurrentProfiles(const Vector<CodeEntry*>& path);

  private:
-  INLINE(const char* GetFunctionName(String* name));
-  INLINE(const char* GetFunctionName(const char* name));
   const char* GetName(int args_count);
+  const char* GetFunctionName(String* name) {
+    return function_and_resource_names_.GetFunctionName(name);
+  }
+  const char* GetFunctionName(const char* name) {
+    return function_and_resource_names_.GetFunctionName(name);
+  }
   List<CpuProfile*>* GetProfilesList(int security_token_id);
   int TokenToIndex(int security_token_id);

@@ -498,7 +504,6 @@
   };

   HeapEntry() { }
- void Init(HeapSnapshot* snapshot, int children_count, int retainers_count);
   void Init(HeapSnapshot* snapshot,
             Type type,
             const char* name,
@@ -640,12 +645,19 @@
 // HeapSnapshotGenerator fills in a HeapSnapshot.
 class HeapSnapshot {
  public:
+  enum Type {
+    kFull = v8::HeapSnapshot::kFull,
+    kAggregated = v8::HeapSnapshot::kAggregated
+  };
+
   HeapSnapshot(HeapSnapshotsCollection* collection,
+               Type type,
                const char* title,
                unsigned uid);
   ~HeapSnapshot();

   HeapSnapshotsCollection* collection() { return collection_; }
+  Type type() { return type_; }
   const char* title() { return title_; }
   unsigned uid() { return uid_; }
   HeapEntry* root() { return entries_[root_entry_index_]; }
@@ -655,6 +667,12 @@
   HeapEntry* AddEntry(
       HeapObject* object, int children_count, int retainers_count);
   bool WillAddEntry(HeapObject* object);
+  HeapEntry* AddEntry(HeapEntry::Type type,
+                      const char* name,
+                      uint64_t id,
+                      int size,
+                      int children_count,
+                      int retainers_count);
   int AddCalculatedData();
   HeapEntryCalculatedData& GetCalculatedData(int index) {
     return calculated_data_[index];
@@ -681,6 +699,7 @@
   static int CalculateNetworkSize(JSObject* obj);

   HeapSnapshotsCollection* collection_;
+  Type type_;
   const char* title_;
   unsigned uid_;
   int root_entry_index_;
@@ -688,6 +707,9 @@
   List<HeapEntry*> entries_;
   bool entries_sorted_;
   List<HeapEntryCalculatedData> calculated_data_;
+#ifdef DEBUG
+  int raw_entries_size_;
+#endif

   friend class HeapSnapshotTester;

@@ -792,12 +814,16 @@

   bool is_tracking_objects() { return is_tracking_objects_; }

-  HeapSnapshot* NewSnapshot(const char* name, unsigned uid);
+  HeapSnapshot* NewSnapshot(
+      HeapSnapshot::Type type, const char* name, unsigned uid);
   void SnapshotGenerationFinished() { ids_.SnapshotGenerationFinished(); }
   List<HeapSnapshot*>* snapshots() { return &snapshots_; }
   HeapSnapshot* GetSnapshot(unsigned uid);

   const char* GetName(String* name) { return names_.GetName(name); }
+  const char* GetFunctionName(String* name) {
+    return names_.GetFunctionName(name);
+  }

   TokenEnumerator* token_enumerator() { return token_enumerator_; }

@@ -847,6 +873,8 @@
   int entries_count() { return entries_count_; }
   int total_children_count() { return total_children_count_; }
   int total_retainers_count() { return total_retainers_count_; }
+
+  static HeapEntry *const kHeapEntryPlaceholder;

  private:
   struct EntryInfo {
@@ -903,8 +931,6 @@
                                    HeapEntry* child_entry) = 0;
     virtual void SetRootReference(Object* child_obj,
                                   HeapEntry* child_entry) = 0;
-
-    static HeapEntry *const kHeapEntryPlaceholder;
   };

   explicit HeapSnapshotGenerator(HeapSnapshot* snapshot);
=======================================
--- /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Wed Aug 11 02:03:23 2010 +++ /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Wed Aug 18 01:19:29 2010
@@ -372,6 +372,7 @@
   i::HeapIterator iterator;
for (i::HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next())
     ret_profile.CollectStats(obj);
+  ret_profile.CoarseAndAggregate();
   RetainerProfilePrinter printer;
   ret_profile.DebugPrintStats(&printer);
   const char* retainers_of_a = printer.GetRetainers("A");
@@ -650,6 +651,8 @@
   CompileAndRunScript(
       "function lazy(x) { return x - 1; }\n"
       "function compiled(x) { return x + 1; }\n"
+      "var inferred = function(x) { return x; }\n"
+ "var anonymous = (function() { return function() { return 0; } })();\n"
       "compiled(1)");
   const v8::HeapSnapshot* snapshot =
       v8::HeapProfiler::TakeSnapshot(v8::String::New("code"));
@@ -663,6 +666,18 @@
       GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy");
   CHECK_NE(NULL, lazy);
   CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType());
+  const v8::HeapGraphNode* inferred =
+      GetProperty(global, v8::HeapGraphEdge::kProperty, "inferred");
+  CHECK_NE(NULL, inferred);
+  CHECK_EQ(v8::HeapGraphNode::kClosure, inferred->GetType());
+  v8::String::AsciiValue inferred_name(inferred->GetName());
+  CHECK_EQ("inferred", *inferred_name);
+  const v8::HeapGraphNode* anonymous =
+      GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous");
+  CHECK_NE(NULL, anonymous);
+  CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType());
+  v8::String::AsciiValue anonymous_name(anonymous->GetName());
+  CHECK_EQ("(anonymous function)", *anonymous_name);

   // Find references to code.
   const v8::HeapGraphNode* compiled_code =
@@ -863,5 +878,115 @@
   // This call must not cause an assertion error in debug builds.
   i::HeapSnapshotTester::CalculateNetworkSize(*jsobj);
 }
+
+
+static const v8::HeapGraphNode* GetChild(
+    const v8::HeapGraphNode* node,
+    v8::HeapGraphNode::Type type,
+    const char* name,
+    const v8::HeapGraphNode* after = NULL) {
+  bool ignore_child = after == NULL ? false : true;
+  for (int i = 0, count = node->GetChildrenCount(); i < count; ++i) {
+    const v8::HeapGraphEdge* prop = node->GetChild(i);
+    const v8::HeapGraphNode* child = prop->GetToNode();
+    v8::String::AsciiValue child_name(child->GetName());
+    if (!ignore_child
+        && child->GetType() == type
+        && strcmp(name, *child_name) == 0)
+      return child;
+    if (after != NULL && child == after) ignore_child = false;
+  }
+  return NULL;
+}
+
+static bool IsNodeRetainedAs(const v8::HeapGraphNode* node,
+                             int element) {
+  for (int i = 0, count = node->GetRetainersCount(); i < count; ++i) {
+    const v8::HeapGraphEdge* prop = node->GetRetainer(i);
+    if (prop->GetType() == v8::HeapGraphEdge::kElement
+        && element == prop->GetName()->Int32Value())
+      return true;
+  }
+  return false;
+}
+
+TEST(AggregatedHeapSnapshot) {
+  v8::HandleScope scope;
+  LocalContext env;
+
+  CompileAndRunScript(
+      "function A() {}\n"
+      "function B(x) { this.x = x; }\n"
+      "var a = new A();\n"
+      "var b = new B(a);");
+  const v8::HeapSnapshot* snapshot =
+      v8::HeapProfiler::TakeSnapshot(
+          v8::String::New("agg"), v8::HeapSnapshot::kAggregated);
+  const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(),
+                                              v8::HeapGraphNode::kInternal,
+                                              "STRING_TYPE");
+  CHECK_NE(NULL, strings);
+  CHECK_NE(0, strings->GetSelfSize());
+  CHECK_NE(0, strings->GetInstancesCount());
+  const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(),
+                                           v8::HeapGraphNode::kInternal,
+                                           "MAP_TYPE");
+  CHECK_NE(NULL, maps);
+  CHECK_NE(0, maps->GetSelfSize());
+  CHECK_NE(0, maps->GetInstancesCount());
+
+  const v8::HeapGraphNode* a = GetChild(snapshot->GetRoot(),
+                                        v8::HeapGraphNode::kObject,
+                                        "A");
+  CHECK_NE(NULL, a);
+  CHECK_NE(0, a->GetSelfSize());
+  CHECK_EQ(1, a->GetInstancesCount());
+
+  const v8::HeapGraphNode* b = GetChild(snapshot->GetRoot(),
+                                        v8::HeapGraphNode::kObject,
+                                        "B");
+  CHECK_NE(NULL, b);
+  CHECK_NE(0, b->GetSelfSize());
+  CHECK_EQ(1, b->GetInstancesCount());
+
+  const v8::HeapGraphNode* glob_prop = GetChild(snapshot->GetRoot(),
+                                                v8::HeapGraphNode::kObject,
+                                                "(global property)",
+                                                b);
+  CHECK_NE(NULL, glob_prop);
+  CHECK_EQ(0, glob_prop->GetSelfSize());
+  CHECK_EQ(0, glob_prop->GetInstancesCount());
+  CHECK_NE(0, glob_prop->GetChildrenCount());
+
+  const v8::HeapGraphNode* a_from_glob_prop = GetChild(
+      glob_prop,
+      v8::HeapGraphNode::kObject,
+      "A");
+  CHECK_NE(NULL, a_from_glob_prop);
+  CHECK_EQ(0, a_from_glob_prop->GetSelfSize());
+  CHECK_EQ(0, a_from_glob_prop->GetInstancesCount());
+  CHECK_EQ(0, a_from_glob_prop->GetChildrenCount());  // Retains nothing.
+ CHECK(IsNodeRetainedAs(a_from_glob_prop, 1)); // (global propery) has 1 ref.
+
+  const v8::HeapGraphNode* b_with_children = GetChild(
+      snapshot->GetRoot(),
+      v8::HeapGraphNode::kObject,
+      "B",
+      b);
+  CHECK_NE(NULL, b_with_children);
+  CHECK_EQ(0, b_with_children->GetSelfSize());
+  CHECK_EQ(0, b_with_children->GetInstancesCount());
+  CHECK_NE(0, b_with_children->GetChildrenCount());
+
+  const v8::HeapGraphNode* a_from_b = GetChild(
+      b_with_children,
+      v8::HeapGraphNode::kObject,
+      "A");
+  CHECK_NE(NULL, a_from_b);
+  CHECK_EQ(0, a_from_b->GetSelfSize());
+  CHECK_EQ(0, a_from_b->GetInstancesCount());
+  CHECK_EQ(0, a_from_b->GetChildrenCount());  // Retains nothing.
+  CHECK(IsNodeRetainedAs(a_from_b, 1));  // B has 1 ref to A.
+}

 #endif  // ENABLE_LOGGING_AND_PROFILING

--
v8-dev mailing list
v8-dev@googlegroups.com
http://groups.google.com/group/v8-dev

Reply via email to