| Index: src/profile-generator.cc | 
| diff --git a/src/profile-generator.cc b/src/profile-generator.cc | 
| index 97de08e5bf96b3703699731c70ee817d4aa3e944..194a40395a251faa92e0aaf138e2ff1c2983052f 100644 | 
| --- a/src/profile-generator.cc | 
| +++ b/src/profile-generator.cc | 
| @@ -1308,19 +1308,6 @@ HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag, | 
| } | 
|  | 
|  | 
| -HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count, | 
| -                                                 int retainers_count) { | 
| -  ASSERT(natives_root_entry_ == NULL); | 
| -  return (natives_root_entry_ = AddEntry( | 
| -      HeapEntry::kObject, | 
| -      "(Native objects)", | 
| -      HeapObjectsMap::kNativesRootObjectId, | 
| -      0, | 
| -      children_count, | 
| -      retainers_count)); | 
| -} | 
| - | 
| - | 
| HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, | 
| const char* name, | 
| uint64_t id, | 
| @@ -1402,10 +1389,8 @@ void HeapSnapshot::Print(int max_depth) { | 
| const uint64_t HeapObjectsMap::kInternalRootObjectId = 1; | 
| const uint64_t HeapObjectsMap::kGcRootsObjectId = | 
| HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep; | 
| -const uint64_t HeapObjectsMap::kNativesRootObjectId = | 
| -    HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; | 
| const uint64_t HeapObjectsMap::kGcRootsFirstSubrootId = | 
| -    HeapObjectsMap::kNativesRootObjectId + HeapObjectsMap::kObjectIdStep; | 
| +    HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep; | 
| const uint64_t HeapObjectsMap::kFirstAvailableObjectId = | 
| HeapObjectsMap::kGcRootsFirstSubrootId + | 
| VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep; | 
| @@ -2712,11 +2697,6 @@ class GlobalHandlesExtractor : public ObjectVisitor { | 
| NativeObjectsExplorer* explorer_; | 
| }; | 
|  | 
| -HeapThing const NativeObjectsExplorer::kNativesRootObject = | 
| -    reinterpret_cast<HeapThing>( | 
| -        static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId)); | 
| - | 
| - | 
| NativeObjectsExplorer::NativeObjectsExplorer( | 
| HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress) | 
| : snapshot_(snapshot), | 
| @@ -2724,6 +2704,7 @@ NativeObjectsExplorer::NativeObjectsExplorer( | 
| progress_(progress), | 
| embedder_queried_(false), | 
| objects_by_info_(RetainedInfosMatch), | 
| +      native_groups_(StringsMatch), | 
| filler_(NULL) { | 
| } | 
|  | 
| @@ -2744,32 +2725,22 @@ NativeObjectsExplorer::~NativeObjectsExplorer() { | 
|  | 
| HeapEntry* NativeObjectsExplorer::AllocateEntry( | 
| HeapThing ptr, int children_count, int retainers_count) { | 
| -  if (ptr == kNativesRootObject) { | 
| -    return snapshot_->AddNativesRootEntry(children_count, retainers_count); | 
| -  } else { | 
| -    v8::RetainedObjectInfo* info = | 
| -        reinterpret_cast<v8::RetainedObjectInfo*>(ptr); | 
| -    intptr_t elements = info->GetElementCount(); | 
| -    intptr_t size = info->GetSizeInBytes(); | 
| -    return snapshot_->AddEntry( | 
| -        HeapEntry::kNative, | 
| -        elements != -1 ? | 
| -            collection_->names()->GetFormatted( | 
| -                "%s / %" V8_PTR_PREFIX "d entries", | 
| -                info->GetLabel(), | 
| -                info->GetElementCount()) : | 
| -                collection_->names()->GetCopy(info->GetLabel()), | 
| -        HeapObjectsMap::GenerateId(info), | 
| -        size != -1 ? static_cast<int>(size) : 0, | 
| -        children_count, | 
| -        retainers_count); | 
| -  } | 
| -} | 
| - | 
| - | 
| -void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) { | 
| -  if (EstimateObjectsCount() <= 0) return; | 
| -  filler->AddEntry(kNativesRootObject, this); | 
| +  v8::RetainedObjectInfo* info = | 
| +      reinterpret_cast<v8::RetainedObjectInfo*>(ptr); | 
| +  intptr_t elements = info->GetElementCount(); | 
| +  intptr_t size = info->GetSizeInBytes(); | 
| +  return snapshot_->AddEntry( | 
| +      HeapEntry::kNative, | 
| +      elements != -1 ? | 
| +          collection_->names()->GetFormatted( | 
| +              "%s / %" V8_PTR_PREFIX "d entries", | 
| +              info->GetLabel(), | 
| +              info->GetElementCount()) : | 
| +          collection_->names()->GetCopy(info->GetLabel()), | 
| +      HeapObjectsMap::GenerateId(info), | 
| +      size != -1 ? static_cast<int>(size) : 0, | 
| +      children_count, | 
| +      retainers_count); | 
| } | 
|  | 
|  | 
| @@ -2835,19 +2806,61 @@ bool NativeObjectsExplorer::IterateAndExtractReferences( | 
| SetWrapperNativeReferences(objects->at(i), info); | 
| } | 
| } | 
| -  SetRootNativesRootReference(); | 
| +  SetRootNativeRootsReference(); | 
| filler_ = NULL; | 
| return true; | 
| } | 
|  | 
|  | 
| +class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo { | 
| + public: | 
| +  explicit NativeGroupRetainedObjectInfo(const char* label) | 
| +      : disposed_(false), | 
| +        hash_(reinterpret_cast<intptr_t>(label)), | 
| +        label_(label) { | 
| +  } | 
| + | 
| +  virtual ~NativeGroupRetainedObjectInfo() {} | 
| +  virtual void Dispose() { | 
| +    CHECK(!disposed_); | 
| +    disposed_ = true; | 
| +  } | 
| +  virtual bool IsEquivalent(RetainedObjectInfo* other) { | 
| +    return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel()); | 
| +  } | 
| +  virtual intptr_t GetHash() { return hash_; } | 
| +  virtual const char* GetLabel() { return label_; } | 
| + | 
| + private: | 
| +  bool disposed_; | 
| +  int hash_; | 
| +  const char* label_; | 
| +}; | 
| + | 
| + | 
| +NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo( | 
| +    const char* label) { | 
| +  const char* label_copy = collection_->names()->GetCopy(label); | 
| +  intptr_t hash = HashSequentialString(label_copy, strlen(label_copy), | 
| +                                       HEAP->HashSeed()); | 
| +  HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy), | 
| +                                                hash, true); | 
| +  if (entry->value == NULL) | 
| +    entry->value = new NativeGroupRetainedObjectInfo(label); | 
| +  return static_cast<NativeGroupRetainedObjectInfo*>(entry->value); | 
| +} | 
| + | 
| + | 
| void NativeObjectsExplorer::SetNativeRootReference( | 
| v8::RetainedObjectInfo* info) { | 
| HeapEntry* child_entry = filler_->FindOrAddEntry(info, this); | 
| ASSERT(child_entry != NULL); | 
| -  filler_->SetIndexedAutoIndexReference( | 
| -      HeapGraphEdge::kElement, | 
| -      kNativesRootObject, snapshot_->natives_root(), | 
| +  NativeGroupRetainedObjectInfo* group_info = | 
| +      FindOrAddGroupInfo(info->GetGroupLabel()); | 
| +  HeapEntry* group_entry = filler_->FindOrAddEntry(group_info, this); | 
| +  filler_->SetNamedAutoIndexReference( | 
| +      HeapGraphEdge::kInternal, | 
| +      group_info, group_entry, | 
| info, child_entry); | 
| } | 
|  | 
| @@ -2868,11 +2881,19 @@ void NativeObjectsExplorer::SetWrapperNativeReferences( | 
| } | 
|  | 
|  | 
| -void NativeObjectsExplorer::SetRootNativesRootReference() { | 
| -  filler_->SetIndexedAutoIndexReference( | 
| -      HeapGraphEdge::kElement, | 
| -      V8HeapExplorer::kInternalRootObject, snapshot_->root(), | 
| -      kNativesRootObject, snapshot_->natives_root()); | 
| +void NativeObjectsExplorer::SetRootNativeRootsReference() { | 
| +  for (HashMap::Entry* entry = native_groups_.Start(); | 
| +       entry; | 
| +       entry = native_groups_.Next(entry)) { | 
| +    NativeGroupRetainedObjectInfo* group_info = | 
| +        static_cast<NativeGroupRetainedObjectInfo*>(entry->value); | 
| +    HeapEntry* group_entry = filler_->FindOrAddEntry(group_info, this); | 
| +    ASSERT(group_entry != NULL); | 
| +    filler_->SetIndexedAutoIndexReference( | 
| +        HeapGraphEdge::kElement, | 
| +        V8HeapExplorer::kInternalRootObject, snapshot_->root(), | 
| +        group_info, group_entry); | 
| +  } | 
| } | 
|  | 
|  | 
| @@ -3107,7 +3128,6 @@ void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) { | 
| bool HeapSnapshotGenerator::CountEntriesAndReferences() { | 
| SnapshotCounter counter(&entries_); | 
| v8_heap_explorer_.AddRootEntries(&counter); | 
| -  dom_explorer_.AddRootEntries(&counter); | 
| return | 
| v8_heap_explorer_.IterateAndExtractReferences(&counter) && | 
| dom_explorer_.IterateAndExtractReferences(&counter); | 
|  |