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_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.
97class 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