• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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 {
IncrementalMarkingInfosIncrementalMarkingInfos110     IncrementalMarkingInfos() : duration(0), longest_step(0), steps(0) {}
111 
UpdateIncrementalMarkingInfos112     void Update(double duration) {
113       steps++;
114       this->duration += duration;
115       if (duration > longest_step) {
116         longest_step = duration;
117       }
118     }
119 
ResetCurrentCycleIncrementalMarkingInfos120     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 
AddScopeSample(Scope::ScopeId scope,double duration)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 
TotalExternalTime()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