New heap profiler: add support for progress reporting and control.
authormikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 13 Dec 2010 10:42:06 +0000 (10:42 +0000)
committermikhail.naganov@gmail.com <mikhail.naganov@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 13 Dec 2010 10:42:06 +0000 (10:42 +0000)
As taking a snapshot of a large heap takes noticeable time, it's
good to be able to monitor and control it.

The change itself is small, big code deletes and additions are in
fact moves. The only significant change is simplification of
approximated retained sizes calculation algorithm.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5978 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8-profiler.h
include/v8.h
src/api.cc
src/heap-profiler.cc
src/heap-profiler.h
src/profile-generator-inl.h
src/profile-generator.cc
src/profile-generator.h
src/runtime.cc
test/cctest/test-heap-profiler.cc

index 08f47ca..675a229 100644 (file)
@@ -245,7 +245,6 @@ class V8EXPORT HeapGraphPath {
 class V8EXPORT HeapGraphNode {
  public:
   enum Type {
-    kInternal = 0,   // For compatibility, will be removed.
     kHidden = 0,     // Hidden node, may be filtered when shown to user.
     kArray = 1,      // An array of elements.
     kString = 2,     // A string.
@@ -413,7 +412,8 @@ class V8EXPORT HeapProfiler {
    */
   static const HeapSnapshot* TakeSnapshot(
       Handle<String> title,
-      HeapSnapshot::Type type = HeapSnapshot::kFull);
+      HeapSnapshot::Type type = HeapSnapshot::kFull,
+      ActivityControl* control = NULL);
 };
 
 
index 8ecf63a..7fd0631 100644 (file)
@@ -3281,6 +3281,24 @@ class V8EXPORT OutputStream {  // NOLINT
 };
 
 
+/**
+ * An interface for reporting progress and controlling long-running
+ * activities.
+ */
+class V8EXPORT ActivityControl {  // NOLINT
+ public:
+  enum ControlOption {
+    kContinue = 0,
+    kAbort = 1
+  };
+  virtual ~ActivityControl() {}
+  /**
+   * Notify about current progress. The activity can be stopped by
+   * returning kAbort as the callback result.
+   */
+  virtual ControlOption ReportProgressValue(int done, int total) = 0;
+};
+
 
 // --- I m p l e m e n t a t i o n ---
 
index 4bff5e1..e169bd0 100644 (file)
@@ -4947,7 +4947,8 @@ const HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
 
 
 const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title,
-                                               HeapSnapshot::Type type) {
+                                               HeapSnapshot::Type type,
+                                               ActivityControl* control) {
   IsDeadCheck("v8::HeapProfiler::TakeSnapshot");
   i::HeapSnapshot::Type internal_type = i::HeapSnapshot::kFull;
   switch (type) {
@@ -4961,7 +4962,8 @@ const HeapSnapshot* HeapProfiler::TakeSnapshot(Handle<String> title,
       UNREACHABLE();
   }
   return reinterpret_cast<const HeapSnapshot*>(
-      i::HeapProfiler::TakeSnapshot(*Utils::OpenHandle(*title), internal_type));
+      i::HeapProfiler::TakeSnapshot(
+          *Utils::OpenHandle(*title), internal_type, control));
 }
 
 #endif  // ENABLE_LOGGING_AND_PROFILING
index 91ac986..6700d38 100644 (file)
@@ -348,27 +348,34 @@ void HeapProfiler::TearDown() {
 
 #ifdef ENABLE_LOGGING_AND_PROFILING
 
-HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name, int type) {
+HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name,
+                                         int type,
+                                         v8::ActivityControl* control) {
   ASSERT(singleton_ != NULL);
-  return singleton_->TakeSnapshotImpl(name, type);
+  return singleton_->TakeSnapshotImpl(name, type, control);
 }
 
 
-HeapSnapshot* HeapProfiler::TakeSnapshot(String* name, int type) {
+HeapSnapshot* HeapProfiler::TakeSnapshot(String* name,
+                                         int type,
+                                         v8::ActivityControl* control) {
   ASSERT(singleton_ != NULL);
-  return singleton_->TakeSnapshotImpl(name, type);
+  return singleton_->TakeSnapshotImpl(name, type, control);
 }
 
 
-HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, int type) {
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
+                                             int type,
+                                             v8::ActivityControl* control) {
   Heap::CollectAllGarbage(true);
   HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type);
   HeapSnapshot* result =
       snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++);
+  bool generation_completed = true;
   switch (s_type) {
     case HeapSnapshot::kFull: {
-      HeapSnapshotGenerator generator(result);
-      generator.GenerateSnapshot();
+      HeapSnapshotGenerator generator(result, control);
+      generation_completed = generator.GenerateSnapshot();
       break;
     }
     case HeapSnapshot::kAggregated: {
@@ -381,13 +388,19 @@ HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name, int type) {
     default:
       UNREACHABLE();
   }
-  snapshots_->SnapshotGenerationFinished();
+  if (!generation_completed) {
+    delete result;
+    result = NULL;
+  }
+  snapshots_->SnapshotGenerationFinished(result);
   return result;
 }
 
 
-HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name, int type) {
-  return TakeSnapshotImpl(snapshots_->GetName(name), type);
+HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
+                                             int type,
+                                             v8::ActivityControl* control) {
+  return TakeSnapshotImpl(snapshots_->GetName(name), type, control);
 }
 
 
index 2ef081e..31d0aff 100644 (file)
@@ -56,8 +56,12 @@ class HeapProfiler {
   static void TearDown();
 
 #ifdef ENABLE_LOGGING_AND_PROFILING
-  static HeapSnapshot* TakeSnapshot(const char* name, int type);
-  static HeapSnapshot* TakeSnapshot(String* name, int type);
+  static HeapSnapshot* TakeSnapshot(const char* name,
+                                    int type,
+                                    v8::ActivityControl* control);
+  static HeapSnapshot* TakeSnapshot(String* name,
+                                    int type,
+                                    v8::ActivityControl* control);
   static int GetSnapshotsCount();
   static HeapSnapshot* GetSnapshot(int index);
   static HeapSnapshot* FindSnapshot(unsigned uid);
@@ -75,8 +79,12 @@ class HeapProfiler {
  private:
   HeapProfiler();
   ~HeapProfiler();
-  HeapSnapshot* TakeSnapshotImpl(const char* name, int type);
-  HeapSnapshot* TakeSnapshotImpl(String* name, int type);
+  HeapSnapshot* TakeSnapshotImpl(const char* name,
+                                 int type,
+                                 v8::ActivityControl* control);
+  HeapSnapshot* TakeSnapshotImpl(String* name,
+                                 int type,
+                                 v8::ActivityControl* control);
 
   HeapSnapshotsCollection* snapshots_;
   unsigned next_snapshot_uid_;
index 8b5c1e2..3df6af0 100644 (file)
@@ -122,7 +122,7 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
 }
 
 
-inline uint64_t HeapEntry::id() {
+uint64_t HeapEntry::id() {
   union {
     Id stored_id;
     uint64_t returned_id;
@@ -146,6 +146,18 @@ void HeapEntriesMap::UpdateEntries(Visitor* visitor) {
   }
 }
 
+
+bool HeapSnapshotGenerator::ReportProgress(bool force) {
+  const int kProgressReportGranularity = 10000;
+  if (control_ != NULL
+      && (force || progress_counter_ % kProgressReportGranularity == 0)) {
+      return
+          control_->ReportProgressValue(progress_counter_, progress_total_) ==
+          v8::ActivityControl::kContinue;
+  }
+  return true;
+}
+
 } }  // namespace v8::internal
 
 #endif  // ENABLE_LOGGING_AND_PROFILING
index ff4661f..364f51d 100644 (file)
@@ -1382,86 +1382,6 @@ HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
 }
 
 
-void HeapSnapshot::FillReversePostorderIndexes(Vector<HeapEntry*>* entries) {
-  ClearPaint();
-  int current_entry = 0;
-  List<HeapEntry*> nodes_to_visit;
-  nodes_to_visit.Add(root());
-  root()->paint_reachable();
-  while (!nodes_to_visit.is_empty()) {
-    HeapEntry* entry = nodes_to_visit.last();
-    Vector<HeapGraphEdge> children = entry->children();
-    bool has_new_edges = false;
-    for (int i = 0; i < children.length(); ++i) {
-      if (children[i].type() == HeapGraphEdge::kShortcut) continue;
-      HeapEntry* child = children[i].to();
-      if (!child->painted_reachable()) {
-        nodes_to_visit.Add(child);
-        child->paint_reachable();
-        has_new_edges = true;
-      }
-    }
-    if (!has_new_edges) {
-      entry->set_ordered_index(current_entry);
-      (*entries)[current_entry++] = entry;
-      nodes_to_visit.RemoveLast();
-    }
-  }
-  entries->Truncate(current_entry);
-}
-
-
-static int Intersect(int i1, int i2, const Vector<HeapEntry*>& dominators) {
-  int finger1 = i1, finger2 = i2;
-  while (finger1 != finger2) {
-    while (finger1 < finger2) finger1 = dominators[finger1]->ordered_index();
-    while (finger2 < finger1) finger2 = dominators[finger2]->ordered_index();
-  }
-  return finger1;
-}
-
-// The algorithm is based on the article:
-// K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
-// Softw. Pract. Exper. 4 (2001), pp. 1–10.
-void HeapSnapshot::BuildDominatorTree(const Vector<HeapEntry*>& entries,
-                                      Vector<HeapEntry*>* dominators) {
-  if (entries.length() == 0) return;
-  const int root_index = entries.length() - 1;
-  for (int i = 0; i < root_index; ++i) (*dominators)[i] = NULL;
-  (*dominators)[root_index] = entries[root_index];
-  bool changed = true;
-  while (changed) {
-    changed = false;
-    for (int i = root_index - 1; i >= 0; --i) {
-      HeapEntry* new_idom = NULL;
-      Vector<HeapGraphEdge*> rets = entries[i]->retainers();
-      int j = 0;
-      for (; j < rets.length(); ++j) {
-        if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
-        HeapEntry* ret = rets[j]->From();
-        if (dominators->at(ret->ordered_index()) != NULL) {
-          new_idom = ret;
-          break;
-        }
-      }
-      for (++j; j < rets.length(); ++j) {
-        if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
-        HeapEntry* ret = rets[j]->From();
-        if (dominators->at(ret->ordered_index()) != NULL) {
-          new_idom = entries[Intersect(ret->ordered_index(),
-                                       new_idom->ordered_index(),
-                                       *dominators)];
-        }
-      }
-      if (new_idom != NULL && dominators->at(i) != new_idom) {
-        (*dominators)[i] = new_idom;
-        changed = true;
-      }
-    }
-  }
-}
-
-
 void HeapSnapshot::SetDominatorsToSelf() {
   for (int i = 0; i < entries_.length(); ++i) {
     HeapEntry* entry = entries_[i];
@@ -1470,61 +1390,6 @@ void HeapSnapshot::SetDominatorsToSelf() {
 }
 
 
-void HeapSnapshot::SetEntriesDominators() {
-  // This array is used for maintaining reverse postorder of nodes.
-  ScopedVector<HeapEntry*> ordered_entries(entries_.length());
-  FillReversePostorderIndexes(&ordered_entries);
-  ScopedVector<HeapEntry*> dominators(ordered_entries.length());
-  BuildDominatorTree(ordered_entries, &dominators);
-  for (int i = 0; i < ordered_entries.length(); ++i) {
-    ASSERT(dominators[i] != NULL);
-    ordered_entries[i]->set_dominator(dominators[i]);
-  }
-  // For nodes unreachable from root, set dominator to itself.
-  SetDominatorsToSelf();
-}
-
-
-void HeapSnapshot::ApproximateRetainedSizes() {
-  SetEntriesDominators();
-  // As for the dominators tree we only know parent nodes, not
-  // children, to sum up total sizes we traverse the tree level by
-  // level upwards, starting from leaves.
-  for (int i = 0; i < entries_.length(); ++i) {
-    HeapEntry* entry = entries_[i];
-    entry->set_retained_size(entry->self_size());
-    entry->set_leaf();
-  }
-  while (true) {
-    bool onlyLeaves = true;
-    for (int i = 0; i < entries_.length(); ++i) {
-      HeapEntry *entry = entries_[i], *dominator = entry->dominator();
-      if (!entry->is_processed() && dominator != entry) {
-        dominator->set_non_leaf();
-        onlyLeaves = false;
-      }
-    }
-    if (onlyLeaves) break;
-
-    for (int i = 0; i < entries_.length(); ++i) {
-      HeapEntry *entry = entries_[i], *dominator = entry->dominator();
-      if (entry->is_leaf() && dominator != entry) {
-        dominator->add_retained_size(entry->retained_size());
-      }
-    }
-
-    // Mark all current leaves as processed, reset non-leaves back to leaves.
-    for (int i = 0; i < entries_.length(); ++i) {
-      HeapEntry* entry = entries_[i];
-      if (entry->is_leaf())
-        entry->set_processed();
-      else if (entry->is_non_leaf())
-        entry->set_leaf();
-    }
-  }
-}
-
-
 HeapEntry* HeapSnapshot::GetNextEntryToInit() {
   if (entries_.length() > 0) {
     HeapEntry* last_entry = entries_.last();
@@ -1716,15 +1581,22 @@ 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, type, name, uid);
-  snapshots_.Add(snapshot);
-  HashMap::Entry* entry =
-      snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
-                             static_cast<uint32_t>(snapshot->uid()),
-                             true);
-  ASSERT(entry->value == NULL);
-  entry->value = snapshot;
-  return snapshot;
+  return new HeapSnapshot(this, type, name, uid);
+}
+
+
+void HeapSnapshotsCollection::SnapshotGenerationFinished(
+    HeapSnapshot* snapshot) {
+  ids_.SnapshotGenerationFinished();
+  if (snapshot != NULL) {
+    snapshots_.Add(snapshot);
+    HashMap::Entry* entry =
+        snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
+                               static_cast<uint32_t>(snapshot->uid()),
+                               true);
+    ASSERT(entry->value == NULL);
+    entry->value = snapshot;
+  }
 }
 
 
@@ -1832,8 +1704,10 @@ void HeapObjectsSet::Insert(Object* obj) {
 }
 
 
-HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
+HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
+                                             v8::ActivityControl* control)
     : snapshot_(snapshot),
+      control_(control),
       collection_(snapshot->collection()),
       filler_(NULL) {
 }
@@ -1990,21 +1864,13 @@ class RootsReferencesExtractor : public ObjectVisitor {
 };
 
 
-void HeapSnapshotGenerator::GenerateSnapshot() {
+bool HeapSnapshotGenerator::GenerateSnapshot() {
   AssertNoAllocation no_alloc;
 
+  SetProgressTotal(4);  // 2 passes + dominators + sizes.
+
   // Pass 1. Iterate heap contents to count entries and references.
-  SnapshotCounter counter(&entries_);
-  filler_ = &counter;
-  filler_->AddEntry(HeapSnapshot::kInternalRootObject);
-  filler_->AddEntry(HeapSnapshot::kGcRootsObject);
-  HeapIterator iterator(HeapIterator::kPreciseFiltering);
-  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
-    ExtractReferences(obj);
-  }
-  SetRootGcRootsReference();
-  RootsReferencesExtractor extractor(this);
-  Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG);
+  if (!CountEntriesAndReferences()) return false;
 
   // Allocate and fill entries in the snapshot, allocate references.
   snapshot_->AllocateEntries(entries_.entries_count(),
@@ -2014,16 +1880,14 @@ void HeapSnapshotGenerator::GenerateSnapshot() {
   entries_.UpdateEntries(&allocator);
 
   // Pass 2. Fill references.
-  SnapshotFiller filler(snapshot_, &entries_);
-  filler_ = &filler;
-  iterator.reset();
-  for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
-    ExtractReferences(obj);
-  }
-  SetRootGcRootsReference();
-  Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG);
+  if (!FillReferences()) return false;
+
+  if (!SetEntriesDominators()) return false;
+  if (!ApproximateRetainedSizes()) return false;
 
-  snapshot_->ApproximateRetainedSizes();
+  progress_counter_ = progress_total_;
+  if (!ReportProgress(true)) return false;
+  return true;
 }
 
 
@@ -2351,6 +2215,183 @@ void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) {
 }
 
 
+void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
+  if (control_ == NULL) return;
+
+  HeapIterator iterator(HeapIterator::kPreciseFiltering);
+  int objects_count = 0;
+  for (HeapObject* obj = iterator.next();
+       obj != NULL;
+       obj = iterator.next(), ++objects_count) {}
+  progress_total_ = objects_count * iterations_count;
+  progress_counter_ = 0;
+}
+
+
+bool HeapSnapshotGenerator::CountEntriesAndReferences() {
+  SnapshotCounter counter(&entries_);
+  filler_ = &counter;
+  filler_->AddEntry(HeapSnapshot::kInternalRootObject);
+  filler_->AddEntry(HeapSnapshot::kGcRootsObject);
+  return IterateAndExtractReferences();
+}
+
+
+bool HeapSnapshotGenerator::FillReferences() {
+  SnapshotFiller filler(snapshot_, &entries_);
+  filler_ = &filler;
+  return IterateAndExtractReferences();
+}
+
+
+void HeapSnapshotGenerator::FillReversePostorderIndexes(
+    Vector<HeapEntry*>* entries) {
+  snapshot_->ClearPaint();
+  int current_entry = 0;
+  List<HeapEntry*> nodes_to_visit;
+  nodes_to_visit.Add(snapshot_->root());
+  snapshot_->root()->paint_reachable();
+  while (!nodes_to_visit.is_empty()) {
+    HeapEntry* entry = nodes_to_visit.last();
+    Vector<HeapGraphEdge> children = entry->children();
+    bool has_new_edges = false;
+    for (int i = 0; i < children.length(); ++i) {
+      if (children[i].type() == HeapGraphEdge::kShortcut) continue;
+      HeapEntry* child = children[i].to();
+      if (!child->painted_reachable()) {
+        nodes_to_visit.Add(child);
+        child->paint_reachable();
+        has_new_edges = true;
+      }
+    }
+    if (!has_new_edges) {
+      entry->set_ordered_index(current_entry);
+      (*entries)[current_entry++] = entry;
+      nodes_to_visit.RemoveLast();
+    }
+  }
+  entries->Truncate(current_entry);
+}
+
+
+static int Intersect(int i1, int i2, const Vector<HeapEntry*>& dominators) {
+  int finger1 = i1, finger2 = i2;
+  while (finger1 != finger2) {
+    while (finger1 < finger2) finger1 = dominators[finger1]->ordered_index();
+    while (finger2 < finger1) finger2 = dominators[finger2]->ordered_index();
+  }
+  return finger1;
+}
+
+// The algorithm is based on the article:
+// K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm"
+// Softw. Pract. Exper. 4 (2001), pp. 1–10.
+bool HeapSnapshotGenerator::BuildDominatorTree(
+    const Vector<HeapEntry*>& entries,
+    Vector<HeapEntry*>* dominators) {
+  if (entries.length() == 0) return true;
+  const int entries_length = entries.length(), root_index = entries_length - 1;
+  for (int i = 0; i < root_index; ++i) (*dominators)[i] = NULL;
+  (*dominators)[root_index] = entries[root_index];
+  int changed = 1;
+  const int base_progress_counter = progress_counter_;
+  while (changed != 0) {
+    changed = 0;
+    for (int i = root_index - 1; i >= 0; --i) {
+      HeapEntry* new_idom = NULL;
+      Vector<HeapGraphEdge*> rets = entries[i]->retainers();
+      int j = 0;
+      for (; j < rets.length(); ++j) {
+        if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
+        HeapEntry* ret = rets[j]->From();
+        if (dominators->at(ret->ordered_index()) != NULL) {
+          new_idom = ret;
+          break;
+        }
+      }
+      for (++j; j < rets.length(); ++j) {
+        if (rets[j]->type() == HeapGraphEdge::kShortcut) continue;
+        HeapEntry* ret = rets[j]->From();
+        if (dominators->at(ret->ordered_index()) != NULL) {
+          new_idom = entries[Intersect(ret->ordered_index(),
+                                       new_idom->ordered_index(),
+                                       *dominators)];
+        }
+      }
+      if (new_idom != NULL && dominators->at(i) != new_idom) {
+        (*dominators)[i] = new_idom;
+        ++changed;
+      }
+    }
+    int remaining = entries_length - changed;
+    if (remaining < 0) remaining = 0;
+    progress_counter_ = base_progress_counter + remaining;
+    if (!ReportProgress(true)) return false;
+  }
+  return true;
+}
+
+
+bool HeapSnapshotGenerator::SetEntriesDominators() {
+  // This array is used for maintaining reverse postorder of nodes.
+  ScopedVector<HeapEntry*> ordered_entries(snapshot_->entries()->length());
+  FillReversePostorderIndexes(&ordered_entries);
+  ScopedVector<HeapEntry*> dominators(ordered_entries.length());
+  if (!BuildDominatorTree(ordered_entries, &dominators)) return false;
+  for (int i = 0; i < ordered_entries.length(); ++i) {
+    ASSERT(dominators[i] != NULL);
+    ordered_entries[i]->set_dominator(dominators[i]);
+  }
+  // For nodes unreachable from root, set dominator to itself.
+  snapshot_->SetDominatorsToSelf();
+  return true;
+}
+
+
+bool HeapSnapshotGenerator::ApproximateRetainedSizes() {
+  // As for the dominators tree we only know parent nodes, not
+  // children, to sum up total sizes we "bubble" node's self size
+  // adding it to all of its parents.
+  for (int i = 0; i < snapshot_->entries()->length(); ++i) {
+    HeapEntry* entry = snapshot_->entries()->at(i);
+    entry->set_retained_size(entry->self_size());
+  }
+  for (int i = 0;
+       i < snapshot_->entries()->length();
+       ++i, IncProgressCounter()) {
+    HeapEntry* entry = snapshot_->entries()->at(i);
+    int entry_size = entry->self_size();
+    for (HeapEntry* dominator = entry->dominator();
+         dominator != entry;
+         entry = dominator, dominator = entry->dominator()) {
+      dominator->add_retained_size(entry_size);
+    }
+    if (!ReportProgress()) return false;
+  }
+  return true;
+}
+
+
+bool HeapSnapshotGenerator::IterateAndExtractReferences() {
+  HeapIterator iterator(HeapIterator::kPreciseFiltering);
+  bool interrupted = false;
+  // Heap iteration with precise filtering must be finished in any case.
+  for (HeapObject* obj = iterator.next();
+       obj != NULL;
+       obj = iterator.next(), IncProgressCounter()) {
+    if (!interrupted) {
+      ExtractReferences(obj);
+      if (!ReportProgress()) interrupted = true;
+    }
+  }
+  if (interrupted) return false;
+  SetRootGcRootsReference();
+  RootsReferencesExtractor extractor(this);
+  Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG);
+  return ReportProgress();
+}
+
+
 void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) {
   raw_additions_root_ =
       NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0));
index 16764a2..cacd27e 100644 (file)
@@ -526,7 +526,7 @@ class HeapEntry BASE_EMBEDDED {
   HeapSnapshot* snapshot() { return snapshot_; }
   Type type() { return static_cast<Type>(type_); }
   const char* name() { return name_; }
-  uint64_t id();
+  inline uint64_t id();
   int self_size() { return self_size_; }
   int retained_size() { return retained_size_; }
   void add_retained_size(int size) { retained_size_ += size; }
@@ -558,13 +558,6 @@ class HeapEntry BASE_EMBEDDED {
   void ApplyAndPaintAllReachable(Visitor* visitor);
   void PaintAllReachable();
 
-  bool is_leaf() { return painted_ == kLeaf; }
-  void set_leaf() { painted_ = kLeaf; }
-  bool is_non_leaf() { return painted_ == kNonLeaf; }
-  void set_non_leaf() { painted_ = kNonLeaf; }
-  bool is_processed() { return painted_ == kProcessed; }
-  void set_processed() { painted_ = kProcessed; }
-
   void SetIndexedReference(HeapGraphEdge::Type type,
                            int child_index,
                            int index,
@@ -625,10 +618,6 @@ class HeapEntry BASE_EMBEDDED {
   static const unsigned kUnpainted = 0;
   static const unsigned kPainted = 1;
   static const unsigned kPaintedReachableFromOthers = 2;
-  // Paints used for approximate retained sizes calculation.
-  static const unsigned kLeaf = 0;
-  static const unsigned kNonLeaf = 1;
-  static const unsigned kProcessed = 2;
 
   static const int kExactRetainedSizeTag = 1;
 
@@ -682,6 +671,7 @@ class HeapSnapshot {
   unsigned uid() { return uid_; }
   HeapEntry* root() { return root_entry_; }
   HeapEntry* gc_roots() { return gc_roots_entry_; }
+  List<HeapEntry*>* entries() { return &entries_; }
 
   void AllocateEntries(
       int entries_count, int children_count, int retainers_count);
@@ -693,7 +683,6 @@ class HeapSnapshot {
                       int size,
                       int children_count,
                       int retainers_count);
-  void ApproximateRetainedSizes();
   void ClearPaint();
   HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
   HeapEntry* GetEntryById(uint64_t id);
@@ -716,10 +705,6 @@ class HeapSnapshot {
                       int children_count,
                       int retainers_count);
   HeapEntry* GetNextEntryToInit();
-  void BuildDominatorTree(const Vector<HeapEntry*>& entries,
-                          Vector<HeapEntry*>* dominators);
-  void FillReversePostorderIndexes(Vector<HeapEntry*>* entries);
-  void SetEntriesDominators();
 
   HeapSnapshotsCollection* collection_;
   Type type_;
@@ -845,7 +830,7 @@ class HeapSnapshotsCollection {
 
   HeapSnapshot* NewSnapshot(
       HeapSnapshot::Type type, const char* name, unsigned uid);
-  void SnapshotGenerationFinished() { ids_.SnapshotGenerationFinished(); }
+  void SnapshotGenerationFinished(HeapSnapshot* snapshot);
   List<HeapSnapshot*>* snapshots() { return &snapshots_; }
   HeapSnapshot* GetSnapshot(unsigned uid);
 
@@ -968,16 +953,27 @@ class HeapSnapshotGenerator {
                                         HeapEntry* child_entry) = 0;
   };
 
-  explicit HeapSnapshotGenerator(HeapSnapshot* snapshot);
-  void GenerateSnapshot();
+  HeapSnapshotGenerator(HeapSnapshot* snapshot,
+                        v8::ActivityControl* control);
+  bool GenerateSnapshot();
 
  private:
+  bool ApproximateRetainedSizes();
+  bool BuildDominatorTree(const Vector<HeapEntry*>& entries,
+                          Vector<HeapEntry*>* dominators);
+  bool CountEntriesAndReferences();
   HeapEntry* GetEntry(Object* obj);
+  void IncProgressCounter() { ++progress_counter_; }
   void ExtractReferences(HeapObject* obj);
   void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry);
   void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry);
   void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry);
   void ExtractInternalReferences(JSObject* js_obj, HeapEntry* entry);
+  bool FillReferences();
+  void FillReversePostorderIndexes(Vector<HeapEntry*>* entries);
+  bool IterateAndExtractReferences();
+  inline bool ReportProgress(bool force = false);
+  bool SetEntriesDominators();
   void SetClosureReference(HeapObject* parent_obj,
                            HeapEntry* parent,
                            String* reference_name,
@@ -1009,8 +1005,10 @@ class HeapSnapshotGenerator {
   void SetRootShortcutReference(Object* child);
   void SetRootGcRootsReference();
   void SetGcRootsReference(Object* child);
+  void SetProgressTotal(int iterations_count);
 
   HeapSnapshot* snapshot_;
+  v8::ActivityControl* control_;
   HeapSnapshotsCollection* collection_;
   // Mapping from HeapObject* pointers to HeapEntry* pointers.
   HeapEntriesMap entries_;
@@ -1018,6 +1016,9 @@ class HeapSnapshotGenerator {
   // Used during references extraction to mark heap objects that
   // are references via non-hidden properties.
   HeapObjectsSet known_references_;
+  // Used during snapshot generation.
+  int progress_counter_;
+  int progress_total_;
 
   friend class IndexedReferencesExtractor;
   friend class RootsReferencesExtractor;
index c5ac082..bd4b890 100644 (file)
@@ -4693,7 +4693,7 @@ static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
     // Even if our string is small enough to fit in new space we still have to
     // handle it being allocated in old space as may happen in the third
     // attempt.  See CALL_AND_RETRY in heap-inl.h and similar code in
-    // CEntryStub::GenerateCore.  
+    // CEntryStub::GenerateCore.
     return SlowQuoteJsonString<Char, StringType>(characters);
   }
   StringType* new_string = StringType::cast(new_object);
index ac4afb2..ad242fe 100644 (file)
@@ -1211,4 +1211,51 @@ TEST(HeapSnapshotGetNodeById) {
   CHECK_EQ(NULL, snapshot->GetNodeById(0x1000000UL));
 }
 
+
+namespace {
+
+class TestActivityControl : public v8::ActivityControl {
+ public:
+  explicit TestActivityControl(int abort_count)
+      : done_(0), total_(0), abort_count_(abort_count) {}
+  ControlOption ReportProgressValue(int done, int total) {
+    done_ = done;
+    total_ = total;
+    return --abort_count_ != 0 ? kContinue : kAbort;
+  }
+  int done() { return done_; }
+  int total() { return total_; }
+
+ private:
+  int done_;
+  int total_;
+  int abort_count_;
+};
+}
+
+TEST(TakeHeapSnapshotAborting) {
+  v8::HandleScope scope;
+  LocalContext env;
+
+  const int snapshots_count = v8::HeapProfiler::GetSnapshotsCount();
+  TestActivityControl aborting_control(3);
+  const v8::HeapSnapshot* no_snapshot =
+      v8::HeapProfiler::TakeSnapshot(v8::String::New("abort"),
+                                     v8::HeapSnapshot::kFull,
+                                     &aborting_control);
+  CHECK_EQ(NULL, no_snapshot);
+  CHECK_EQ(snapshots_count, v8::HeapProfiler::GetSnapshotsCount());
+  CHECK_GT(aborting_control.total(), aborting_control.done());
+
+  TestActivityControl control(-1);  // Don't abort.
+  const v8::HeapSnapshot* snapshot =
+      v8::HeapProfiler::TakeSnapshot(v8::String::New("full"),
+                                     v8::HeapSnapshot::kFull,
+                                     &control);
+  CHECK_NE(NULL, snapshot);
+  CHECK_EQ(snapshots_count + 1, v8::HeapProfiler::GetSnapshotsCount());
+  CHECK_EQ(control.total(), control.done());
+  CHECK_GT(control.total(), 0);
+}
+
 #endif  // ENABLE_LOGGING_AND_PROFILING