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