Revision: 11302
Author: [email protected]
Date: Fri Apr 13 01:52:25 2012
Log: This patch is introducing a way to grab heap stats.
The idea is to monitor the heap regulary and track each object in the heap.
With this data we will be able do draw heap usage diagram.
Where X is time and Y is the number of objects.
BUG=none
TEST=HeapSnapshotObjectsStats
Review URL: https://chromiumcodereview.appspot.com/10049002
http://code.google.com/p/v8/source/detail?r=11302
Modified:
/branches/bleeding_edge/include/v8-profiler.h
/branches/bleeding_edge/include/v8.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.cc
/branches/bleeding_edge/src/profile-generator.h
/branches/bleeding_edge/test/cctest/test-heap-profiler.cc
=======================================
--- /branches/bleeding_edge/include/v8-profiler.h Tue Mar 27 04:54:47 2012
+++ /branches/bleeding_edge/include/v8-profiler.h Fri Apr 13 01:52:25 2012
@@ -417,6 +417,33 @@
HeapSnapshot::Type type = HeapSnapshot::kFull,
ActivityControl* control = NULL);
+ /**
+ * Starts tracking of heap objects population statistics. After calling
+ * this method, all heap objects relocations done by the garbage
collector
+ * are being registered.
+ */
+ static void StartHeapObjectsTracking();
+
+ /**
+ * Adds a new time interval entry to the aggregated statistics array. The
+ * time interval entry contains information on the current heap objects
+ * population size. The method also updates aggregated statistics and
+ * reports updates for all previous time intervals via the OutputStream
+ * object. Updates on each time interval are provided as pairs of time
+ * interval index and updated heap objects count.
+ *
+ * StartHeapObjectsTracking must be called before the first call to this
+ * method.
+ */
+ static void PushHeapObjectsStats(OutputStream* stream);
+
+ /**
+ * Stops tracking of heap objects population statistics, cleans up all
+ * collected data. StartHeapObjectsTracking must be called again prior to
+ * calling PushHeapObjectsStats next time.
+ */
+ static void StopHeapObjectsTracking();
+
/**
* Deletes all snapshots taken. All previously returned pointers to
* snapshots and their contents become invalid after this call.
=======================================
--- /branches/bleeding_edge/include/v8.h Wed Apr 11 02:23:57 2012
+++ /branches/bleeding_edge/include/v8.h Fri Apr 13 01:52:25 2012
@@ -3741,7 +3741,8 @@
class V8EXPORT OutputStream { // NOLINT
public:
enum OutputEncoding {
- kAscii = 0 // 7-bit ASCII.
+ kAscii = 0, // 7-bit ASCII.
+ kUint32 = 1
};
enum WriteResult {
kContinue = 0,
@@ -3760,6 +3761,12 @@
* will not be called in case writing was aborted.
*/
virtual WriteResult WriteAsciiChunk(char* data, int size) = 0;
+ /**
+ * Writes the next chunk of heap stats data into the stream. Writing
+ * can be stopped by returning kAbort as function result. EndOfStream
+ * will not be called in case writing was aborted.
+ */
+ virtual WriteResult WriteUint32Chunk(uint32_t* data, int count) = 0;
};
=======================================
--- /branches/bleeding_edge/src/api.cc Wed Apr 11 02:23:57 2012
+++ /branches/bleeding_edge/src/api.cc Fri Apr 13 01:52:25 2012
@@ -6235,6 +6235,27 @@
i::HeapProfiler::TakeSnapshot(
*Utils::OpenHandle(*title), internal_type, control));
}
+
+
+void HeapProfiler::StartHeapObjectsTracking() {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapProfiler::StartHeapObjectsTracking");
+ i::HeapProfiler::StartHeapObjectsTracking();
+}
+
+
+void HeapProfiler::StopHeapObjectsTracking() {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapProfiler::StopHeapObjectsTracking");
+ i::HeapProfiler::StopHeapObjectsTracking();
+}
+
+
+void HeapProfiler::PushHeapObjectsStats(OutputStream* stream) {
+ i::Isolate* isolate = i::Isolate::Current();
+ IsDeadCheck(isolate, "v8::HeapProfiler::PushHeapObjectsStats");
+ return i::HeapProfiler::PushHeapObjectsStats(stream);
+}
void HeapProfiler::DeleteAllSnapshots() {
=======================================
--- /branches/bleeding_edge/src/heap-profiler.cc Fri Jan 13 05:09:52 2012
+++ /branches/bleeding_edge/src/heap-profiler.cc Fri Apr 13 01:52:25 2012
@@ -33,7 +33,6 @@
namespace v8 {
namespace internal {
-
HeapProfiler::HeapProfiler()
: snapshots_(new HeapSnapshotsCollection()),
next_snapshot_uid_(1) {
@@ -84,6 +83,24 @@
type,
control);
}
+
+
+void HeapProfiler::StartHeapObjectsTracking() {
+ ASSERT(Isolate::Current()->heap_profiler() != NULL);
+ Isolate::Current()->heap_profiler()->StartHeapObjectsTrackingImpl();
+}
+
+
+void HeapProfiler::StopHeapObjectsTracking() {
+ ASSERT(Isolate::Current()->heap_profiler() != NULL);
+ Isolate::Current()->heap_profiler()->StopHeapObjectsTrackingImpl();
+}
+
+
+void HeapProfiler::PushHeapObjectsStats(v8::OutputStream* stream) {
+ ASSERT(Isolate::Current()->heap_profiler() != NULL);
+ return
Isolate::Current()->heap_profiler()->PushHeapObjectsStatsImpl(stream);
+}
void HeapProfiler::DefineWrapperClass(
@@ -135,6 +152,20 @@
v8::ActivityControl* control)
{
return TakeSnapshotImpl(snapshots_->names()->GetName(name), type,
control);
}
+
+void HeapProfiler::StartHeapObjectsTrackingImpl() {
+ snapshots_->StartHeapObjectsTracking();
+}
+
+
+void HeapProfiler::PushHeapObjectsStatsImpl(OutputStream* stream) {
+ snapshots_->PushHeapObjectsStats(stream);
+}
+
+
+void HeapProfiler::StopHeapObjectsTrackingImpl() {
+ snapshots_->StopHeapObjectsTracking();
+}
int HeapProfiler::GetSnapshotsCount() {
=======================================
--- /branches/bleeding_edge/src/heap-profiler.h Fri Jan 13 05:09:52 2012
+++ /branches/bleeding_edge/src/heap-profiler.h Fri Apr 13 01:52:25 2012
@@ -44,8 +44,6 @@
} \
} while (false)
-// The HeapProfiler writes data to the log files, which can be
postprocessed
-// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
class HeapProfiler {
public:
static void SetUp();
@@ -57,6 +55,10 @@
static HeapSnapshot* TakeSnapshot(String* name,
int type,
v8::ActivityControl* control);
+
+ static void StartHeapObjectsTracking();
+ static void StopHeapObjectsTracking();
+ static void PushHeapObjectsStats(OutputStream* stream);
static int GetSnapshotsCount();
static HeapSnapshot* GetSnapshot(int index);
static HeapSnapshot* FindSnapshot(unsigned uid);
@@ -84,6 +86,10 @@
v8::ActivityControl* control);
void ResetSnapshots();
+ void StartHeapObjectsTrackingImpl();
+ void StopHeapObjectsTrackingImpl();
+ void PushHeapObjectsStatsImpl(OutputStream* stream);
+
HeapSnapshotsCollection* snapshots_;
unsigned next_snapshot_uid_;
List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;
=======================================
--- /branches/bleeding_edge/src/profile-generator.cc Tue Apr 10 23:58:42
2012
+++ /branches/bleeding_edge/src/profile-generator.cc Fri Apr 13 01:52:25
2012
@@ -1404,6 +1404,82 @@
return 0;
}
}
+
+
+SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr) {
+ ASSERT(static_cast<uint32_t>(entries_->length()) >
entries_map_.occupancy());
+ HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr),
true);
+ if (entry->value != NULL) {
+ int entry_index =
+ static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
+ EntryInfo& entry_info = entries_->at(entry_index);
+ entry_info.accessed = true;
+ return entry_info.id;
+ }
+ entry->value = reinterpret_cast<void*>(entries_->length());
+ SnapshotObjectId id = next_id_;
+ next_id_ += kObjectIdStep;
+ entries_->Add(EntryInfo(id, addr));
+ ASSERT(static_cast<uint32_t>(entries_->length()) >
entries_map_.occupancy());
+ return id;
+}
+
+
+void HeapObjectsMap::StopHeapObjectsTracking() {
+ time_intervals_.Clear();
+}
+
+void HeapObjectsMap::UpdateHeapObjectsMap() {
+ HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
+ "HeapSnapshotsCollection::UpdateHeapObjectsMap");
+ HeapIterator iterator(HeapIterator::kFilterUnreachable);
+ for (HeapObject* obj = iterator.next();
+ obj != NULL;
+ obj = iterator.next()) {
+ FindOrAddEntry(obj->address());
+ }
+ initial_fill_mode_ = false;
+ RemoveDeadEntries();
+}
+
+
+void HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) {
+ UpdateHeapObjectsMap();
+ time_intervals_.Add(TimeInterval(next_id_));
+ int prefered_chunk_size = stream->GetChunkSize();
+ List<uint32_t> stats_buffer;
+ ASSERT(!entries_->is_empty());
+ EntryInfo* entry_info = &entries_->first();
+ EntryInfo* end_entry_info = &entries_->last() + 1;
+ for (int time_interval_index = 0;
+ time_interval_index < time_intervals_.length();
+ ++time_interval_index) {
+ TimeInterval& time_interval = time_intervals_[time_interval_index];
+ SnapshotObjectId time_interval_id = time_interval.id;
+ uint32_t entries_count = 0;
+ while (entry_info < end_entry_info && entry_info->id <
time_interval_id) {
+ ++entries_count;
+ ++entry_info;
+ }
+ if (time_interval.count != entries_count) {
+ stats_buffer.Add(time_interval_index);
+ stats_buffer.Add(time_interval.count = entries_count);
+ if (stats_buffer.length() >= prefered_chunk_size) {
+ OutputStream::WriteResult result = stream->WriteUint32Chunk(
+ &stats_buffer.first(), stats_buffer.length());
+ if (result == OutputStream::kAbort) return;
+ stats_buffer.Clear();
+ }
+ }
+ }
+ ASSERT(entry_info == end_entry_info);
+ if (!stats_buffer.is_empty()) {
+ OutputStream::WriteResult result =
+ stream->WriteUint32Chunk(&stats_buffer.first(),
stats_buffer.length());
+ if (result == OutputStream::kAbort) return;
+ }
+ stream->EndOfStream();
+}
void HeapObjectsMap::RemoveDeadEntries() {
=======================================
--- /branches/bleeding_edge/src/profile-generator.h Tue Apr 10 23:58:42 2012
+++ /branches/bleeding_edge/src/profile-generator.h Fri Apr 13 01:52:25 2012
@@ -709,6 +709,9 @@
SnapshotObjectId last_assigned_id() const {
return next_id_ - kObjectIdStep;
}
+
+ void StopHeapObjectsTracking();
+ void PushHeapObjectsStats(OutputStream* stream);
static SnapshotObjectId GenerateId(v8::RetainedObjectInfo* info);
static inline SnapshotObjectId GetNthGcSubrootId(int delta);
@@ -730,9 +733,16 @@
Address addr;
bool accessed;
};
+ struct TimeInterval {
+ explicit TimeInterval(SnapshotObjectId id) : id(id), count(0) { }
+ SnapshotObjectId id;
+ uint32_t count;
+ };
void AddEntry(Address addr, SnapshotObjectId id);
SnapshotObjectId FindEntry(Address addr);
+ SnapshotObjectId FindOrAddEntry(Address addr);
+ void UpdateHeapObjectsMap();
void RemoveDeadEntries();
static bool AddressesMatch(void* key1, void* key2) {
@@ -749,6 +759,7 @@
SnapshotObjectId next_id_;
HashMap entries_map_;
List<EntryInfo>* entries_;
+ List<TimeInterval> time_intervals_;
DISALLOW_COPY_AND_ASSIGN(HeapObjectsMap);
};
@@ -760,6 +771,11 @@
~HeapSnapshotsCollection();
bool is_tracking_objects() { return is_tracking_objects_; }
+ void PushHeapObjectsStats(OutputStream* stream) {
+ return ids_.PushHeapObjectsStats(stream);
+ }
+ void StartHeapObjectsTracking() { is_tracking_objects_ = true; }
+ void StopHeapObjectsTracking() { ids_.StopHeapObjectsTracking(); }
HeapSnapshot* NewSnapshot(
HeapSnapshot::Type type, const char* name, unsigned uid);
=======================================
--- /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Sun Apr 8
12:18:06 2012
+++ /branches/bleeding_edge/test/cctest/test-heap-profiler.cc Fri Apr 13
01:52:25 2012
@@ -557,9 +557,14 @@
memcpy(chunk.start(), buffer, chars_written);
return kContinue;
}
+ virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int
chars_written) {
+ ASSERT(false);
+ return kAbort;
+ }
void WriteTo(i::Vector<char> dest) { buffer_.WriteTo(dest); }
int eos_signaled() { return eos_signaled_; }
int size() { return buffer_.size(); }
+
private:
i::Collector<char> buffer_;
int eos_signaled_;
@@ -690,6 +695,153 @@
CHECK_GT(stream.size(), 0);
CHECK_EQ(0, stream.eos_signaled());
}
+
+namespace {
+
+class TestStatsStream : public v8::OutputStream {
+ public:
+ TestStatsStream()
+ : eos_signaled_(0),
+ numbers_written_(0),
+ entries_count_(0),
+ intervals_count_(0),
+ first_interval_index_(-1) { }
+ TestStatsStream(const TestStatsStream& stream)
+ : eos_signaled_(stream.eos_signaled_),
+ numbers_written_(stream.numbers_written_),
+ entries_count_(stream.entries_count_),
+ intervals_count_(stream.intervals_count_),
+ first_interval_index_(stream.first_interval_index_) { }
+ virtual ~TestStatsStream() {}
+ virtual void EndOfStream() { ++eos_signaled_; }
+ virtual WriteResult WriteAsciiChunk(char* buffer, int chars_written) {
+ ASSERT(false);
+ return kAbort;
+ }
+ virtual WriteResult WriteUint32Chunk(uint32_t* buffer, int
numbers_written) {
+ ++intervals_count_;
+ ASSERT(numbers_written);
+ numbers_written_ += numbers_written;
+ entries_count_ = 0;
+ if (first_interval_index_ == -1 && numbers_written != 0)
+ first_interval_index_ = buffer[0];
+ for (int i = 1; i < numbers_written; i += 2)
+ entries_count_ += buffer[i];
+
+ return kContinue;
+ }
+ int eos_signaled() { return eos_signaled_; }
+ int numbers_written() { return numbers_written_; }
+ uint32_t entries_count() const { return entries_count_; }
+ int intervals_count() const { return intervals_count_; }
+ int first_interval_index() const { return first_interval_index_; }
+
+ private:
+ int eos_signaled_;
+ int numbers_written_;
+ uint32_t entries_count_;
+ int intervals_count_;
+ int first_interval_index_;
+};
+
+} // namespace
+
+static TestStatsStream GetHeapStatsUpdate() {
+ TestStatsStream stream;
+ v8::HeapProfiler::PushHeapObjectsStats(&stream);
+ CHECK_EQ(1, stream.eos_signaled());
+ return stream;
+}
+
+
+TEST(HeapSnapshotObjectsStats) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ v8::HeapProfiler::StartHeapObjectsTracking();
+ // We have to call GC 5 times. In other case the garbage will be
+ // the reason of flakiness.
+ for (int i = 0; i < 5; ++i) {
+ HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
+ }
+
+ {
+ // Single chunk of data expected in update. Initial data.
+ TestStatsStream stats_update = GetHeapStatsUpdate();
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(2, stats_update.numbers_written());
+ CHECK_EQ(0, stats_update.first_interval_index());
+ }
+
+ // No data expected in update because nothing has happened.
+ CHECK_EQ(0, GetHeapStatsUpdate().numbers_written());
+ {
+ v8::HandleScope inner_scope_1;
+ v8::Local<v8::String> string1 = v8_str("string1");
+ {
+ // Single chunk of data with one new entry expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate();
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(2, stats_update.numbers_written());
+ CHECK_EQ(1, stats_update.entries_count());
+ CHECK_EQ(2, stats_update.first_interval_index());
+ }
+
+ // No data expected in update because nothing happened.
+ CHECK_EQ(0, GetHeapStatsUpdate().numbers_written());
+
+ {
+ v8::HandleScope inner_scope_2;
+ v8::Local<v8::String> string2 = v8_str("string2");
+
+ {
+ v8::HandleScope inner_scope_3;
+ v8::Handle<v8::String> string3 = v8::String::New("string3");
+ v8::Handle<v8::String> string4 = v8::String::New("string4");
+
+ {
+ // Single chunk of data with three new entries expected in
update.
+ TestStatsStream stats_update = GetHeapStatsUpdate();
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(2, stats_update.numbers_written());
+ CHECK_EQ(3, stats_update.entries_count());
+ CHECK_EQ(4, stats_update.first_interval_index());
+ }
+ }
+
+ {
+ // Single chunk of data with two left entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate();
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(2, stats_update.numbers_written());
+ CHECK_EQ(1, stats_update.entries_count());
+ // Two strings from forth interval were released.
+ CHECK_EQ(4, stats_update.first_interval_index());
+ }
+ }
+
+ {
+ // Single chunk of data with 0 left entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate();
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(2, stats_update.numbers_written());
+ CHECK_EQ(0, stats_update.entries_count());
+ // The last string from forth interval was released.
+ CHECK_EQ(4, stats_update.first_interval_index());
+ }
+ }
+ {
+ // Single chunk of data with 0 left entries expected in update.
+ TestStatsStream stats_update = GetHeapStatsUpdate();
+ CHECK_EQ(1, stats_update.intervals_count());
+ CHECK_EQ(2, stats_update.numbers_written());
+ CHECK_EQ(0, stats_update.entries_count());
+ // The only string from the second interval was released.
+ CHECK_EQ(2, stats_update.first_interval_index());
+ }
+
+ v8::HeapProfiler::StopHeapObjectsTracking();
+}
static void CheckChildrenIds(const v8::HeapSnapshot* snapshot,
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev