Home | History | Annotate | Download | only in heap
      1 // Copyright 2014 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_HEAP_GC_TRACER_H_
      6 #define V8_HEAP_GC_TRACER_H_
      7 
      8 #include "src/base/compiler-specific.h"
      9 #include "src/base/platform/platform.h"
     10 #include "src/base/ring-buffer.h"
     11 #include "src/counters.h"
     12 #include "src/globals.h"
     13 #include "testing/gtest/include/gtest/gtest_prod.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 
     18 typedef std::pair<uint64_t, double> BytesAndDuration;
     19 
     20 inline BytesAndDuration MakeBytesAndDuration(uint64_t bytes, double duration) {
     21   return std::make_pair(bytes, duration);
     22 }
     23 
     24 enum ScavengeSpeedMode { kForAllObjects, kForSurvivedObjects };
     25 
     26 #define INCREMENTAL_SCOPES(F)                                      \
     27   /* MC_INCREMENTAL is the top-level incremental marking scope. */ \
     28   F(MC_INCREMENTAL)                                                \
     29   F(MC_INCREMENTAL_SWEEPING)                                       \
     30   F(MC_INCREMENTAL_WRAPPER_PROLOGUE)                               \
     31   F(MC_INCREMENTAL_WRAPPER_TRACING)                                \
     32   F(MC_INCREMENTAL_FINALIZE)                                       \
     33   F(MC_INCREMENTAL_FINALIZE_BODY)                                  \
     34   F(MC_INCREMENTAL_FINALIZE_OBJECT_GROUPING)                       \
     35   F(MC_INCREMENTAL_EXTERNAL_EPILOGUE)                              \
     36   F(MC_INCREMENTAL_EXTERNAL_PROLOGUE)
     37 
     38 #define TRACER_SCOPES(F)                      \
     39   INCREMENTAL_SCOPES(F)                       \
     40   F(EXTERNAL_EPILOGUE)                        \
     41   F(EXTERNAL_PROLOGUE)                        \
     42   F(EXTERNAL_WEAK_GLOBAL_HANDLES)             \
     43   F(MC_CLEAR)                                 \
     44   F(MC_CLEAR_CODE_FLUSH)                      \
     45   F(MC_CLEAR_DEPENDENT_CODE)                  \
     46   F(MC_CLEAR_GLOBAL_HANDLES)                  \
     47   F(MC_CLEAR_MAPS)                            \
     48   F(MC_CLEAR_SLOTS_BUFFER)                    \
     49   F(MC_CLEAR_STORE_BUFFER)                    \
     50   F(MC_CLEAR_STRING_TABLE)                    \
     51   F(MC_CLEAR_WEAK_CELLS)                      \
     52   F(MC_CLEAR_WEAK_COLLECTIONS)                \
     53   F(MC_CLEAR_WEAK_LISTS)                      \
     54   F(MC_EPILOGUE)                              \
     55   F(MC_EVACUATE)                              \
     56   F(MC_EVACUATE_CANDIDATES)                   \
     57   F(MC_EVACUATE_CLEAN_UP)                     \
     58   F(MC_EVACUATE_COPY)                         \
     59   F(MC_EVACUATE_UPDATE_POINTERS)              \
     60   F(MC_EVACUATE_UPDATE_POINTERS_TO_EVACUATED) \
     61   F(MC_EVACUATE_UPDATE_POINTERS_TO_NEW)       \
     62   F(MC_EVACUATE_UPDATE_POINTERS_WEAK)         \
     63   F(MC_FINISH)                                \
     64   F(MC_MARK)                                  \
     65   F(MC_MARK_FINISH_INCREMENTAL)               \
     66   F(MC_MARK_PREPARE_CODE_FLUSH)               \
     67   F(MC_MARK_ROOTS)                            \
     68   F(MC_MARK_WEAK_CLOSURE)                     \
     69   F(MC_MARK_WEAK_CLOSURE_EPHEMERAL)           \
     70   F(MC_MARK_WEAK_CLOSURE_WEAK_HANDLES)        \
     71   F(MC_MARK_WEAK_CLOSURE_WEAK_ROOTS)          \
     72   F(MC_MARK_WEAK_CLOSURE_HARMONY)             \
     73   F(MC_MARK_WRAPPER_EPILOGUE)                 \
     74   F(MC_MARK_WRAPPER_PROLOGUE)                 \
     75   F(MC_MARK_WRAPPER_TRACING)                  \
     76   F(MC_MARK_OBJECT_GROUPING)                  \
     77   F(MC_PROLOGUE)                              \
     78   F(MC_SWEEP)                                 \
     79   F(MC_SWEEP_CODE)                            \
     80   F(MC_SWEEP_MAP)                             \
     81   F(MC_SWEEP_OLD)                             \
     82   F(SCAVENGER_CODE_FLUSH_CANDIDATES)          \
     83   F(SCAVENGER_OLD_TO_NEW_POINTERS)            \
     84   F(SCAVENGER_ROOTS)                          \
     85   F(SCAVENGER_SCAVENGE)                       \
     86   F(SCAVENGER_SEMISPACE)                      \
     87   F(SCAVENGER_WEAK)
     88 
     89 #define TRACE_GC(tracer, scope_id)                             \
     90   GCTracer::Scope::ScopeId gc_tracer_scope_id(scope_id);       \
     91   GCTracer::Scope gc_tracer_scope(tracer, gc_tracer_scope_id); \
     92   TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc"),             \
     93                GCTracer::Scope::Name(gc_tracer_scope_id))
     94 
     95 // GCTracer collects and prints ONE line after each garbage collector
     96 // invocation IFF --trace_gc is used.
     97 class V8_EXPORT_PRIVATE GCTracer {
     98  public:
     99   struct IncrementalMarkingInfos {
    100     IncrementalMarkingInfos() : duration(0), longest_step(0), steps(0) {}
    101 
    102     void Update(double duration) {
    103       steps++;
    104       this->duration += duration;
    105       if (duration > longest_step) {
    106         longest_step = duration;
    107       }
    108     }
    109 
    110     void ResetCurrentCycle() {
    111       duration = 0;
    112       longest_step = 0;
    113       steps = 0;
    114     }
    115 
    116     double duration;
    117     double longest_step;
    118     int steps;
    119   };
    120 
    121   class Scope {
    122    public:
    123     enum ScopeId {
    124 #define DEFINE_SCOPE(scope) scope,
    125       TRACER_SCOPES(DEFINE_SCOPE)
    126 #undef DEFINE_SCOPE
    127           NUMBER_OF_SCOPES,
    128 
    129       FIRST_INCREMENTAL_SCOPE = MC_INCREMENTAL,
    130       LAST_INCREMENTAL_SCOPE = MC_INCREMENTAL_EXTERNAL_PROLOGUE,
    131       NUMBER_OF_INCREMENTAL_SCOPES =
    132           LAST_INCREMENTAL_SCOPE - FIRST_INCREMENTAL_SCOPE + 1
    133     };
    134 
    135     Scope(GCTracer* tracer, ScopeId scope);
    136     ~Scope();
    137     static const char* Name(ScopeId id);
    138 
    139    private:
    140     GCTracer* tracer_;
    141     ScopeId scope_;
    142     double start_time_;
    143     RuntimeCallTimer timer_;
    144 
    145     DISALLOW_COPY_AND_ASSIGN(Scope);
    146   };
    147 
    148 
    149   class Event {
    150    public:
    151     enum Type {
    152       SCAVENGER = 0,
    153       MARK_COMPACTOR = 1,
    154       INCREMENTAL_MARK_COMPACTOR = 2,
    155       MINOR_MARK_COMPACTOR = 3,
    156       START = 4
    157     };
    158 
    159     Event(Type type, GarbageCollectionReason gc_reason,
    160           const char* collector_reason);
    161 
    162     // Returns a string describing the event type.
    163     const char* TypeName(bool short_name) const;
    164 
    165     // Type of event
    166     Type type;
    167 
    168     GarbageCollectionReason gc_reason;
    169     const char* collector_reason;
    170 
    171     // Timestamp set in the constructor.
    172     double start_time;
    173 
    174     // Timestamp set in the destructor.
    175     double end_time;
    176 
    177     // Memory reduction flag set.
    178     bool reduce_memory;
    179 
    180     // Size of objects in heap set in constructor.
    181     size_t start_object_size;
    182 
    183     // Size of objects in heap set in destructor.
    184     size_t end_object_size;
    185 
    186     // Size of memory allocated from OS set in constructor.
    187     size_t start_memory_size;
    188 
    189     // Size of memory allocated from OS set in destructor.
    190     size_t end_memory_size;
    191 
    192     // Total amount of space either wasted or contained in one of free lists
    193     // before the current GC.
    194     size_t start_holes_size;
    195 
    196     // Total amount of space either wasted or contained in one of free lists
    197     // after the current GC.
    198     size_t end_holes_size;
    199 
    200     // Size of new space objects in constructor.
    201     size_t new_space_object_size;
    202 
    203     // Size of survived new space objects in destructor.
    204     size_t survived_new_space_object_size;
    205 
    206     // Bytes marked incrementally for INCREMENTAL_MARK_COMPACTOR
    207     size_t incremental_marking_bytes;
    208 
    209     // Duration of incremental marking steps for INCREMENTAL_MARK_COMPACTOR.
    210     double incremental_marking_duration;
    211 
    212     // Amounts of time spent in different scopes during GC.
    213     double scopes[Scope::NUMBER_OF_SCOPES];
    214 
    215     // Holds details for incremental marking scopes.
    216     IncrementalMarkingInfos
    217         incremental_marking_scopes[Scope::NUMBER_OF_INCREMENTAL_SCOPES];
    218   };
    219 
    220   static const int kThroughputTimeFrameMs = 5000;
    221 
    222   explicit GCTracer(Heap* heap);
    223 
    224   // Start collecting data.
    225   void Start(GarbageCollector collector, GarbageCollectionReason gc_reason,
    226              const char* collector_reason);
    227 
    228   // Stop collecting data and print results.
    229   void Stop(GarbageCollector collector);
    230 
    231   // Sample and accumulate bytes allocated since the last GC.
    232   void SampleAllocation(double current_ms, size_t new_space_counter_bytes,
    233                         size_t old_generation_counter_bytes);
    234 
    235   // Log the accumulated new space allocation bytes.
    236   void AddAllocation(double current_ms);
    237 
    238   void AddContextDisposalTime(double time);
    239 
    240   void AddCompactionEvent(double duration, size_t live_bytes_compacted);
    241 
    242   void AddSurvivalRatio(double survival_ratio);
    243 
    244   // Log an incremental marking step.
    245   void AddIncrementalMarkingStep(double duration, size_t bytes);
    246 
    247   // Compute the average incremental marking speed in bytes/millisecond.
    248   // Returns 0 if no events have been recorded.
    249   double IncrementalMarkingSpeedInBytesPerMillisecond() const;
    250 
    251   // Compute the average scavenge speed in bytes/millisecond.
    252   // Returns 0 if no events have been recorded.
    253   double ScavengeSpeedInBytesPerMillisecond(
    254       ScavengeSpeedMode mode = kForAllObjects) const;
    255 
    256   // Compute the average compaction speed in bytes/millisecond.
    257   // Returns 0 if not enough events have been recorded.
    258   double CompactionSpeedInBytesPerMillisecond() const;
    259 
    260   // Compute the average mark-sweep speed in bytes/millisecond.
    261   // Returns 0 if no events have been recorded.
    262   double MarkCompactSpeedInBytesPerMillisecond() const;
    263 
    264   // Compute the average incremental mark-sweep finalize speed in
    265   // bytes/millisecond.
    266   // Returns 0 if no events have been recorded.
    267   double FinalIncrementalMarkCompactSpeedInBytesPerMillisecond() const;
    268 
    269   // Compute the overall mark compact speed including incremental steps
    270   // and the final mark-compact step.
    271   double CombinedMarkCompactSpeedInBytesPerMillisecond();
    272 
    273   // Allocation throughput in the new space in bytes/millisecond.
    274   // Returns 0 if no allocation events have been recorded.
    275   double NewSpaceAllocationThroughputInBytesPerMillisecond(
    276       double time_ms = 0) const;
    277 
    278   // Allocation throughput in the old generation in bytes/millisecond in the
    279   // last time_ms milliseconds.
    280   // Returns 0 if no allocation events have been recorded.
    281   double OldGenerationAllocationThroughputInBytesPerMillisecond(
    282       double time_ms = 0) const;
    283 
    284   // Allocation throughput in heap in bytes/millisecond in the last time_ms
    285   // milliseconds.
    286   // Returns 0 if no allocation events have been recorded.
    287   double AllocationThroughputInBytesPerMillisecond(double time_ms) const;
    288 
    289   // Allocation throughput in heap in bytes/milliseconds in the last
    290   // kThroughputTimeFrameMs seconds.
    291   // Returns 0 if no allocation events have been recorded.
    292   double CurrentAllocationThroughputInBytesPerMillisecond() const;
    293 
    294   // Allocation throughput in old generation in bytes/milliseconds in the last
    295   // kThroughputTimeFrameMs seconds.
    296   // Returns 0 if no allocation events have been recorded.
    297   double CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const;
    298 
    299   // Computes the context disposal rate in milliseconds. It takes the time
    300   // frame of the first recorded context disposal to the current time and
    301   // divides it by the number of recorded events.
    302   // Returns 0 if no events have been recorded.
    303   double ContextDisposalRateInMilliseconds() const;
    304 
    305   // Computes the average survival ratio based on the last recorded survival
    306   // events.
    307   // Returns 0 if no events have been recorded.
    308   double AverageSurvivalRatio() const;
    309 
    310   // Returns true if at least one survival event was recorded.
    311   bool SurvivalEventsRecorded() const;
    312 
    313   // Discard all recorded survival events.
    314   void ResetSurvivalEvents();
    315 
    316   void NotifyIncrementalMarkingStart();
    317 
    318   V8_INLINE void AddScopeSample(Scope::ScopeId scope, double duration) {
    319     DCHECK(scope < Scope::NUMBER_OF_SCOPES);
    320     if (scope >= Scope::FIRST_INCREMENTAL_SCOPE &&
    321         scope <= Scope::LAST_INCREMENTAL_SCOPE) {
    322       incremental_marking_scopes_[scope - Scope::FIRST_INCREMENTAL_SCOPE]
    323           .Update(duration);
    324     } else {
    325       current_.scopes[scope] += duration;
    326     }
    327   }
    328 
    329  private:
    330   FRIEND_TEST(GCTracer, AverageSpeed);
    331   FRIEND_TEST(GCTracerTest, AllocationThroughput);
    332   FRIEND_TEST(GCTracerTest, NewSpaceAllocationThroughput);
    333   FRIEND_TEST(GCTracerTest, NewSpaceAllocationThroughputWithProvidedTime);
    334   FRIEND_TEST(GCTracerTest, OldGenerationAllocationThroughputWithProvidedTime);
    335   FRIEND_TEST(GCTracerTest, RegularScope);
    336   FRIEND_TEST(GCTracerTest, IncrementalMarkingDetails);
    337   FRIEND_TEST(GCTracerTest, IncrementalScope);
    338   FRIEND_TEST(GCTracerTest, IncrementalMarkingSpeed);
    339 
    340   // Returns the average speed of the events in the buffer.
    341   // If the buffer is empty, the result is 0.
    342   // Otherwise, the result is between 1 byte/ms and 1 GB/ms.
    343   static double AverageSpeed(const base::RingBuffer<BytesAndDuration>& buffer);
    344   static double AverageSpeed(const base::RingBuffer<BytesAndDuration>& buffer,
    345                              const BytesAndDuration& initial, double time_ms);
    346 
    347   void ResetForTesting();
    348   void ResetIncrementalMarkingCounters();
    349   void RecordIncrementalMarkingSpeed(size_t bytes, double duration);
    350 
    351   // Print one detailed trace line in name=value format.
    352   // TODO(ernstm): Move to Heap.
    353   void PrintNVP() const;
    354 
    355   // Print one trace line.
    356   // TODO(ernstm): Move to Heap.
    357   void Print() const;
    358 
    359   // Prints a line and also adds it to the heap's ring buffer so that
    360   // it can be included in later crash dumps.
    361   void PRINTF_FORMAT(2, 3) Output(const char* format, ...) const;
    362 
    363   double TotalExternalTime() const {
    364     return current_.scopes[Scope::EXTERNAL_WEAK_GLOBAL_HANDLES] +
    365            current_.scopes[Scope::EXTERNAL_EPILOGUE] +
    366            current_.scopes[Scope::EXTERNAL_PROLOGUE] +
    367            current_.scopes[Scope::MC_INCREMENTAL_EXTERNAL_EPILOGUE] +
    368            current_.scopes[Scope::MC_INCREMENTAL_EXTERNAL_PROLOGUE];
    369   }
    370 
    371   // Pointer to the heap that owns this tracer.
    372   Heap* heap_;
    373 
    374   // Current tracer event. Populated during Start/Stop cycle. Valid after Stop()
    375   // has returned.
    376   Event current_;
    377 
    378   // Previous tracer event.
    379   Event previous_;
    380 
    381   // Size of incremental marking steps (in bytes) accumulated since the end of
    382   // the last mark compact GC.
    383   size_t incremental_marking_bytes_;
    384 
    385   // Duration of incremental marking steps since the end of the last mark-
    386   // compact event.
    387   double incremental_marking_duration_;
    388 
    389   double incremental_marking_start_time_;
    390 
    391   double recorded_incremental_marking_speed_;
    392 
    393   // Incremental scopes carry more information than just the duration. The infos
    394   // here are merged back upon starting/stopping the GC tracer.
    395   IncrementalMarkingInfos
    396       incremental_marking_scopes_[Scope::NUMBER_OF_INCREMENTAL_SCOPES];
    397 
    398 
    399   // Timestamp and allocation counter at the last sampled allocation event.
    400   double allocation_time_ms_;
    401   size_t new_space_allocation_counter_bytes_;
    402   size_t old_generation_allocation_counter_bytes_;
    403 
    404   // Accumulated duration and allocated bytes since the last GC.
    405   double allocation_duration_since_gc_;
    406   size_t new_space_allocation_in_bytes_since_gc_;
    407   size_t old_generation_allocation_in_bytes_since_gc_;
    408 
    409   double combined_mark_compact_speed_cache_;
    410 
    411   // Counts how many tracers were started without stopping.
    412   int start_counter_;
    413 
    414   // Separate timer used for --runtime_call_stats
    415   RuntimeCallTimer timer_;
    416 
    417   base::RingBuffer<BytesAndDuration> recorded_minor_gcs_total_;
    418   base::RingBuffer<BytesAndDuration> recorded_minor_gcs_survived_;
    419   base::RingBuffer<BytesAndDuration> recorded_compactions_;
    420   base::RingBuffer<BytesAndDuration> recorded_incremental_mark_compacts_;
    421   base::RingBuffer<BytesAndDuration> recorded_mark_compacts_;
    422   base::RingBuffer<BytesAndDuration> recorded_new_generation_allocations_;
    423   base::RingBuffer<BytesAndDuration> recorded_old_generation_allocations_;
    424   base::RingBuffer<double> recorded_context_disposal_times_;
    425   base::RingBuffer<double> recorded_survival_ratios_;
    426 
    427   DISALLOW_COPY_AND_ASSIGN(GCTracer);
    428 };
    429 }  // namespace internal
    430 }  // namespace v8
    431 
    432 #endif  // V8_HEAP_GC_TRACER_H_
    433