New heap profiler: add support for progress reporting and control.

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
parent b1a2cc1e
......@@ -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);
};
......
......@@ -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 ---
......
......@@ -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
......
......@@ -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);
}
......
......@@ -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_;
......
......@@ -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
......
This diff is collapsed.
......@@ -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;
......
......@@ -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);
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment