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 
MakeBytesAndDuration(uint64_t bytes,double duration)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 {
IncrementalMarkingInfosIncrementalMarkingInfos100     IncrementalMarkingInfos() : duration(0), longest_step(0), steps(0) {}
101 
UpdateIncrementalMarkingInfos102     void Update(double duration) {
103       steps++;
104       this->duration += duration;
105       if (duration > longest_step) {
106         longest_step = duration;
107       }
108     }
109 
ResetCurrentCycleIncrementalMarkingInfos110     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 
AddScopeSample(Scope::ScopeId scope,double duration)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 
TotalExternalTime()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