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
15namespace v8 {
16namespace internal {
17
18typedef std::pair<uint64_t, double> BytesAndDuration;
19
20inline BytesAndDuration MakeBytesAndDuration(uint64_t bytes, double duration) {
21  return std::make_pair(bytes, duration);
22}
23
24enum 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.
107class 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