From aae81e125b8f6a3a54a3279d6749f6546b03f6fc Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Fri, 13 Jul 2012 12:22:09 +0000 Subject: [PATCH] Add counters that automatically track object sizes and counts. Review URL: https://chromiumcodereview.appspot.com/10702168 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12082 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/d8.cc | 18 +++--- src/d8.h | 2 +- src/flag-definitions.h | 2 + src/heap.cc | 33 +++++++++++ src/heap.h | 15 +++++ src/mark-compact.cc | 27 +++++++++ src/objects-visiting.h | 125 ++++++++++++++++++++--------------------- src/v8-counters.cc | 8 +++ src/v8-counters.h | 16 ++++++ 9 files changed, 175 insertions(+), 71 deletions(-) diff --git a/src/d8.cc b/src/d8.cc index fb90c94abb..804b76ceeb 100644 --- a/src/d8.cc +++ b/src/d8.cc @@ -1301,20 +1301,24 @@ void Shell::OnExit() { counters[j].key = i.CurrentKey(); } qsort(counters, number_of_counters, sizeof(counters[0]), CompareKeys); - printf("+--------------------------------------------+-------------+\n"); - printf("| Name | Value |\n"); - printf("+--------------------------------------------+-------------+\n"); + printf("+----------------------------------------------------------------+" + "-------------+\n"); + printf("| Name |" + " Value |\n"); + printf("+----------------------------------------------------------------+" + "-------------+\n"); for (j = 0; j < number_of_counters; j++) { Counter* counter = counters[j].counter; const char* key = counters[j].key; if (counter->is_histogram()) { - printf("| c:%-40s | %11i |\n", key, counter->count()); - printf("| t:%-40s | %11i |\n", key, counter->sample_total()); + printf("| c:%-60s | %11i |\n", key, counter->count()); + printf("| t:%-60s | %11i |\n", key, counter->sample_total()); } else { - printf("| %-42s | %11i |\n", key, counter->count()); + printf("| %-62s | %11i |\n", key, counter->count()); } } - printf("+--------------------------------------------+-------------+\n"); + printf("+----------------------------------------------------------------+" + "-------------+\n"); delete [] counters; } delete counters_file_; diff --git a/src/d8.h b/src/d8.h index be53f991d6..5f356a6742 100644 --- a/src/d8.h +++ b/src/d8.h @@ -67,7 +67,7 @@ class CounterCollection { CounterCollection(); Counter* GetNextCounter(); private: - static const unsigned kMaxCounters = 256; + static const unsigned kMaxCounters = 512; uint32_t magic_number_; uint32_t max_counters_; uint32_t max_name_size_; diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 1c4f914f20..046b2db52e 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -367,6 +367,8 @@ DEFINE_bool(incremental_marking, true, "use incremental marking") DEFINE_bool(incremental_marking_steps, true, "do incremental marking steps") DEFINE_bool(trace_incremental_marking, false, "trace progress of the incremental marking") +DEFINE_bool(track_gc_object_stats, false, + "track object counts and memory usage") // v8.cc DEFINE_bool(use_idle_notification, true, diff --git a/src/heap.cc b/src/heap.cc index 8cf1af84f1..309d0adf02 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -179,6 +179,8 @@ Heap::Heap() // Put a dummy entry in the remembered pages so we can find the list the // minidump even if there are no real unmapped pages. RememberUnmappedPage(NULL, false); + + ClearObjectStats(true); } @@ -7197,4 +7199,35 @@ void Heap::RememberUnmappedPage(Address page, bool compacted) { remembered_unmapped_pages_index_ %= kRememberedUnmappedPages; } + +void Heap::ClearObjectStats(bool clear_last_time_stats) { + memset(object_counts_, 0, sizeof(object_counts_)); + memset(object_sizes_, 0, sizeof(object_sizes_)); + if (clear_last_time_stats) { + memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_)); + memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_)); + } +} + + +static LazyMutex checkpoint_object_stats_mutex = LAZY_MUTEX_INITIALIZER; + + +void Heap::CheckpointObjectStats() { + ScopedLock lock(checkpoint_object_stats_mutex.Pointer()); + Counters* counters = isolate()->counters(); +#define ADJUST_LAST_TIME_OBJECT_COUNT(name) \ + counters->count_of_##name()->Increment(object_counts_[name]); \ + counters->count_of_##name()->Decrement(object_counts_last_time_[name]); \ + counters->size_of_##name()->Increment(object_sizes_[name]); \ + counters->size_of_##name()->Decrement(object_sizes_last_time_[name]); + INSTANCE_TYPE_LIST(ADJUST_LAST_TIME_OBJECT_COUNT) +#undef ADJUST_LAST_TIME_OBJECT_COUNT + memcpy(object_counts_last_time_, object_counts_, + sizeof(object_counts_)); + memcpy(object_sizes_last_time_, object_sizes_, + sizeof(object_sizes_)); + ClearObjectStats(); +} + } } // namespace v8::internal diff --git a/src/heap.h b/src/heap.h index 8d9adb02db..08c91a41d2 100644 --- a/src/heap.h +++ b/src/heap.h @@ -1600,6 +1600,14 @@ class Heap { global_ic_age_ = (global_ic_age_ + 1) & SharedFunctionInfo::ICAgeBits::kMax; } + void RecordObjectStats(InstanceType type, size_t size) { + ASSERT(type <= LAST_TYPE); + object_counts_[type]++; + object_sizes_[type] += size; + } + + void CheckpointObjectStats(); + private: Heap(); @@ -1993,11 +2001,18 @@ class Heap { void AdvanceIdleIncrementalMarking(intptr_t step_size); + void ClearObjectStats(bool clear_last_time_stats = false); static const int kInitialSymbolTableSize = 2048; static const int kInitialEvalCacheSize = 64; static const int kInitialNumberStringCacheSize = 256; + // Object counts and used memory by InstanceType + size_t object_counts_[LAST_TYPE + 1]; + size_t object_counts_last_time_[LAST_TYPE + 1]; + size_t object_sizes_[LAST_TYPE + 1]; + size_t object_sizes_last_time_[LAST_TYPE + 1]; + // Maximum GC pause. int max_gc_pause_; diff --git a/src/mark-compact.cc b/src/mark-compact.cc index e61457991e..024b7ef407 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -944,6 +944,17 @@ class StaticMarkingVisitor : public StaticVisitorBase { table_.GetVisitor(map)(map, obj); } + template + class ObjectStatsTracker { + public: + static inline void Visit(Map* map, HeapObject* obj) { + Heap* heap = map->GetHeap(); + int object_size = obj->Size(); + heap->RecordObjectStats(map->instance_type(), object_size); + non_count_table_.GetVisitorById(static_cast(id))(map, obj); + } + }; + static void Initialize() { table_.Register(kVisitShortcutCandidate, &FixedBodyVisitor(); + + if (FLAG_track_gc_object_stats) { + // Copy the visitor table to make call-through possible. + non_count_table_.CopyFrom(&table_); +#define VISITOR_ID_COUNT_FUNCTION(id)\ + table_.Register(kVisit##id, ObjectStatsTracker::Visit); + VISITOR_ID_LIST(VISITOR_ID_COUNT_FUNCTION) +#undef VISITOR_ID_COUNT_FUNCTION + } } INLINE(static void VisitPointer(Heap* heap, Object** p)) { @@ -1557,11 +1577,14 @@ class StaticMarkingVisitor : public StaticVisitorBase { typedef void (*Callback)(Map* map, HeapObject* object); static VisitorDispatchTable table_; + static VisitorDispatchTable non_count_table_; }; VisitorDispatchTable StaticMarkingVisitor::table_; +VisitorDispatchTable + StaticMarkingVisitor::non_count_table_; class MarkingVisitor : public ObjectVisitor { @@ -2437,6 +2460,10 @@ void MarkCompactCollector::AfterMarking() { // Clean up dead objects from the runtime profiler. heap()->isolate()->runtime_profiler()->RemoveDeadSamples(); } + + if (FLAG_track_gc_object_stats) { + heap()->CheckpointObjectStats(); + } } diff --git a/src/objects-visiting.h b/src/objects-visiting.h index b476dfef2e..a84c51225b 100644 --- a/src/objects-visiting.h +++ b/src/objects-visiting.h @@ -46,71 +46,70 @@ namespace internal { // Base class for all static visitors. class StaticVisitorBase : public AllStatic { public: +#define VISITOR_ID_LIST(V) \ + V(SeqAsciiString) \ + V(SeqTwoByteString) \ + V(ShortcutCandidate) \ + V(ByteArray) \ + V(FreeSpace) \ + V(FixedArray) \ + V(FixedDoubleArray) \ + V(GlobalContext) \ + V(DataObject2) \ + V(DataObject3) \ + V(DataObject4) \ + V(DataObject5) \ + V(DataObject6) \ + V(DataObject7) \ + V(DataObject8) \ + V(DataObject9) \ + V(DataObjectGeneric) \ + V(JSObject2) \ + V(JSObject3) \ + V(JSObject4) \ + V(JSObject5) \ + V(JSObject6) \ + V(JSObject7) \ + V(JSObject8) \ + V(JSObject9) \ + V(JSObjectGeneric) \ + V(Struct2) \ + V(Struct3) \ + V(Struct4) \ + V(Struct5) \ + V(Struct6) \ + V(Struct7) \ + V(Struct8) \ + V(Struct9) \ + V(StructGeneric) \ + V(ConsString) \ + V(SlicedString) \ + V(Oddball) \ + V(Code) \ + V(Map) \ + V(PropertyCell) \ + V(SharedFunctionInfo) \ + V(JSFunction) \ + V(JSWeakMap) \ + V(JSRegExp) + + // For data objects, JS objects and structs along with generic visitor which + // can visit object of any size we provide visitors specialized by + // object size in words. + // Ids of specialized visitors are declared in a linear order (without + // holes) starting from the id of visitor specialized for 2 words objects + // (base visitor id) and ending with the id of generic visitor. + // Method GetVisitorIdForSize depends on this ordering to calculate visitor + // id of specialized visitor from given instance size, base visitor id and + // generic visitor's id. enum VisitorId { - kVisitSeqAsciiString = 0, - kVisitSeqTwoByteString, - kVisitShortcutCandidate, - kVisitByteArray, - kVisitFreeSpace, - kVisitFixedArray, - kVisitFixedDoubleArray, - kVisitGlobalContext, - - // For data objects, JS objects and structs along with generic visitor which - // can visit object of any size we provide visitors specialized by - // object size in words. - // Ids of specialized visitors are declared in a linear order (without - // holes) starting from the id of visitor specialized for 2 words objects - // (base visitor id) and ending with the id of generic visitor. - // Method GetVisitorIdForSize depends on this ordering to calculate visitor - // id of specialized visitor from given instance size, base visitor id and - // generic visitor's id. - - kVisitDataObject, - kVisitDataObject2 = kVisitDataObject, - kVisitDataObject3, - kVisitDataObject4, - kVisitDataObject5, - kVisitDataObject6, - kVisitDataObject7, - kVisitDataObject8, - kVisitDataObject9, - kVisitDataObjectGeneric, - - kVisitJSObject, - kVisitJSObject2 = kVisitJSObject, - kVisitJSObject3, - kVisitJSObject4, - kVisitJSObject5, - kVisitJSObject6, - kVisitJSObject7, - kVisitJSObject8, - kVisitJSObject9, - kVisitJSObjectGeneric, - - kVisitStruct, - kVisitStruct2 = kVisitStruct, - kVisitStruct3, - kVisitStruct4, - kVisitStruct5, - kVisitStruct6, - kVisitStruct7, - kVisitStruct8, - kVisitStruct9, - kVisitStructGeneric, - - kVisitConsString, - kVisitSlicedString, - kVisitOddball, - kVisitCode, - kVisitMap, - kVisitPropertyCell, - kVisitSharedFunctionInfo, - kVisitJSFunction, - kVisitJSWeakMap, - kVisitJSRegExp, - +#define VISITOR_ID_ENUM_DECL(id) kVisit##id, + VISITOR_ID_LIST(VISITOR_ID_ENUM_DECL) +#undef VISITOR_ID_ENUM_DECL kVisitorIdCount, + kVisitDataObject = kVisitDataObject2, + kVisitJSObject = kVisitJSObject2, + kVisitStruct = kVisitStruct2, kMinObjectSizeInWords = 2 }; diff --git a/src/v8-counters.cc b/src/v8-counters.cc index 04fd077576..8d490bec4b 100644 --- a/src/v8-counters.cc +++ b/src/v8-counters.cc @@ -53,6 +53,14 @@ Counters::Counters() { STATS_COUNTER_LIST_2(SC) #undef SC +#define SC(name) \ + StatsCounter count_of_##name = { "c:" "V8.CountOf_" #name, NULL, false };\ + count_of_##name##_ = count_of_##name; \ + StatsCounter size_of_##name = { "c:" "V8.SizeOf_" #name, NULL, false };\ + size_of_##name##_ = size_of_##name; + INSTANCE_TYPE_LIST(SC) +#undef SC + StatsCounter state_counters[] = { #define COUNTER_NAME(name) \ { "c:V8.State" #name, NULL, false }, diff --git a/src/v8-counters.h b/src/v8-counters.h index f25ebdcd88..e05a3065dd 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -30,6 +30,7 @@ #include "allocation.h" #include "counters.h" +#include "objects.h" #include "v8globals.h" namespace v8 { @@ -308,6 +309,12 @@ class Counters { STATS_COUNTER_LIST_2(SC) #undef SC +#define SC(name) \ + StatsCounter* count_of_##name() { return &count_of_##name##_; } \ + StatsCounter* size_of_##name() { return &size_of_##name##_; } + INSTANCE_TYPE_LIST(SC) +#undef SC + enum Id { #define RATE_ID(name, caption) k_##name, HISTOGRAM_TIMER_LIST(RATE_ID) @@ -319,6 +326,9 @@ class Counters { STATS_COUNTER_LIST_1(COUNTER_ID) STATS_COUNTER_LIST_2(COUNTER_ID) #undef COUNTER_ID +#define COUNTER_ID(name) kCountOf##name, kSizeOf##name, + INSTANCE_TYPE_LIST(COUNTER_ID) +#undef COUNTER_ID #define COUNTER_ID(name) k_##name, STATE_TAG_LIST(COUNTER_ID) #undef COUNTER_ID @@ -346,6 +356,12 @@ class Counters { STATS_COUNTER_LIST_2(SC) #undef SC +#define SC(name) \ + StatsCounter size_of_##name##_; \ + StatsCounter count_of_##name##_; + INSTANCE_TYPE_LIST(SC) +#undef SC + enum { #define COUNTER_ID(name) __##name, STATE_TAG_LIST(COUNTER_ID)