• 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/platform/platform.h"
9 #include "src/globals.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 // A simple ring buffer class with maximum size known at compile time.
15 // The class only implements the functionality required in GCTracer.
16 template <typename T, size_t MAX_SIZE>
17 class RingBuffer {
18  public:
19   class const_iterator {
20    public:
const_iterator()21     const_iterator() : index_(0), elements_(NULL) {}
22 
const_iterator(size_t index,const T * elements)23     const_iterator(size_t index, const T* elements)
24         : index_(index), elements_(elements) {}
25 
26     bool operator==(const const_iterator& rhs) const {
27       return elements_ == rhs.elements_ && index_ == rhs.index_;
28     }
29 
30     bool operator!=(const const_iterator& rhs) const {
31       return elements_ != rhs.elements_ || index_ != rhs.index_;
32     }
33 
34     operator const T*() const { return elements_ + index_; }
35 
36     const T* operator->() const { return elements_ + index_; }
37 
38     const T& operator*() const { return elements_[index_]; }
39 
40     const_iterator& operator++() {
41       index_ = (index_ + 1) % (MAX_SIZE + 1);
42       return *this;
43     }
44 
45     const_iterator& operator--() {
46       index_ = (index_ + MAX_SIZE) % (MAX_SIZE + 1);
47       return *this;
48     }
49 
50    private:
51     size_t index_;
52     const T* elements_;
53   };
54 
RingBuffer()55   RingBuffer() : begin_(0), end_(0) {}
56 
empty()57   bool empty() const { return begin_ == end_; }
size()58   size_t size() const {
59     return (end_ - begin_ + MAX_SIZE + 1) % (MAX_SIZE + 1);
60   }
begin()61   const_iterator begin() const { return const_iterator(begin_, elements_); }
end()62   const_iterator end() const { return const_iterator(end_, elements_); }
back()63   const_iterator back() const { return --end(); }
push_back(const T & element)64   void push_back(const T& element) {
65     elements_[end_] = element;
66     end_ = (end_ + 1) % (MAX_SIZE + 1);
67     if (end_ == begin_) begin_ = (begin_ + 1) % (MAX_SIZE + 1);
68   }
push_front(const T & element)69   void push_front(const T& element) {
70     begin_ = (begin_ + MAX_SIZE) % (MAX_SIZE + 1);
71     if (begin_ == end_) end_ = (end_ + MAX_SIZE) % (MAX_SIZE + 1);
72     elements_[begin_] = element;
73   }
74 
reset()75   void reset() {
76     begin_ = 0;
77     end_ = 0;
78   }
79 
80  private:
81   T elements_[MAX_SIZE + 1];
82   size_t begin_;
83   size_t end_;
84 
85   DISALLOW_COPY_AND_ASSIGN(RingBuffer);
86 };
87 
88 
89 enum ScavengeSpeedMode { kForAllObjects, kForSurvivedObjects };
90 
91 
92 // GCTracer collects and prints ONE line after each garbage collector
93 // invocation IFF --trace_gc is used.
94 // TODO(ernstm): Unit tests.
95 class GCTracer {
96  public:
97   class Scope {
98    public:
99     enum ScopeId {
100       EXTERNAL,
101       MC_CLEAR,
102       MC_CLEAR_CODE_FLUSH,
103       MC_CLEAR_DEPENDENT_CODE,
104       MC_CLEAR_GLOBAL_HANDLES,
105       MC_CLEAR_MAPS,
106       MC_CLEAR_SLOTS_BUFFER,
107       MC_CLEAR_STORE_BUFFER,
108       MC_CLEAR_STRING_TABLE,
109       MC_CLEAR_WEAK_CELLS,
110       MC_CLEAR_WEAK_COLLECTIONS,
111       MC_CLEAR_WEAK_LISTS,
112       MC_EVACUATE,
113       MC_EVACUATE_CANDIDATES,
114       MC_EVACUATE_CLEAN_UP,
115       MC_EVACUATE_NEW_SPACE,
116       MC_EVACUATE_UPDATE_POINTERS,
117       MC_EVACUATE_UPDATE_POINTERS_BETWEEN_EVACUATED,
118       MC_EVACUATE_UPDATE_POINTERS_TO_EVACUATED,
119       MC_EVACUATE_UPDATE_POINTERS_TO_NEW,
120       MC_EVACUATE_UPDATE_POINTERS_WEAK,
121       MC_FINISH,
122       MC_INCREMENTAL_FINALIZE,
123       MC_MARK,
124       MC_MARK_FINISH_INCREMENTAL,
125       MC_MARK_PREPARE_CODE_FLUSH,
126       MC_MARK_ROOTS,
127       MC_MARK_WEAK_CLOSURE,
128       MC_SWEEP,
129       MC_SWEEP_CODE,
130       MC_SWEEP_MAP,
131       MC_SWEEP_OLD,
132       SCAVENGER_CODE_FLUSH_CANDIDATES,
133       SCAVENGER_OBJECT_GROUPS,
134       SCAVENGER_OLD_TO_NEW_POINTERS,
135       SCAVENGER_ROOTS,
136       SCAVENGER_SCAVENGE,
137       SCAVENGER_SEMISPACE,
138       SCAVENGER_WEAK,
139       NUMBER_OF_SCOPES
140     };
141 
142     Scope(GCTracer* tracer, ScopeId scope);
143     ~Scope();
144 
145    private:
146     GCTracer* tracer_;
147     ScopeId scope_;
148     double start_time_;
149 
150     DISALLOW_COPY_AND_ASSIGN(Scope);
151   };
152 
153 
154   class AllocationEvent {
155    public:
156     // Default constructor leaves the event uninitialized.
AllocationEvent()157     AllocationEvent() {}
158 
159     AllocationEvent(double duration, size_t allocation_in_bytes);
160 
161     // Time spent in the mutator during the end of the last sample to the
162     // beginning of the next sample.
163     double duration_;
164 
165     // Memory allocated in the new space during the end of the last sample
166     // to the beginning of the next sample
167     size_t allocation_in_bytes_;
168   };
169 
170 
171   class CompactionEvent {
172    public:
CompactionEvent()173     CompactionEvent() : duration(0), live_bytes_compacted(0) {}
174 
CompactionEvent(double duration,intptr_t live_bytes_compacted)175     CompactionEvent(double duration, intptr_t live_bytes_compacted)
176         : duration(duration), live_bytes_compacted(live_bytes_compacted) {}
177 
178     double duration;
179     intptr_t live_bytes_compacted;
180   };
181 
182 
183   class ContextDisposalEvent {
184    public:
185     // Default constructor leaves the event uninitialized.
ContextDisposalEvent()186     ContextDisposalEvent() {}
187 
188     explicit ContextDisposalEvent(double time);
189 
190     // Time when context disposal event happened.
191     double time_;
192   };
193 
194 
195   class SurvivalEvent {
196    public:
197     // Default constructor leaves the event uninitialized.
SurvivalEvent()198     SurvivalEvent() {}
199 
200     explicit SurvivalEvent(double survival_ratio);
201 
202     double promotion_ratio_;
203   };
204 
205 
206   class Event {
207    public:
208     enum Type {
209       SCAVENGER = 0,
210       MARK_COMPACTOR = 1,
211       INCREMENTAL_MARK_COMPACTOR = 2,
212       START = 3
213     };
214 
215     // Default constructor leaves the event uninitialized.
Event()216     Event() {}
217 
218     Event(Type type, const char* gc_reason, const char* collector_reason);
219 
220     // Returns a string describing the event type.
221     const char* TypeName(bool short_name) const;
222 
223     // Type of event
224     Type type;
225 
226     const char* gc_reason;
227     const char* collector_reason;
228 
229     // Timestamp set in the constructor.
230     double start_time;
231 
232     // Timestamp set in the destructor.
233     double end_time;
234 
235     // Memory reduction flag set.
236     bool reduce_memory;
237 
238     // Size of objects in heap set in constructor.
239     intptr_t start_object_size;
240 
241     // Size of objects in heap set in destructor.
242     intptr_t end_object_size;
243 
244     // Size of memory allocated from OS set in constructor.
245     intptr_t start_memory_size;
246 
247     // Size of memory allocated from OS set in destructor.
248     intptr_t end_memory_size;
249 
250     // Total amount of space either wasted or contained in one of free lists
251     // before the current GC.
252     intptr_t start_holes_size;
253 
254     // Total amount of space either wasted or contained in one of free lists
255     // after the current GC.
256     intptr_t end_holes_size;
257 
258     // Size of new space objects in constructor.
259     intptr_t new_space_object_size;
260     // Size of survived new space objects in desctructor.
261     intptr_t survived_new_space_object_size;
262 
263     // Number of incremental marking steps since creation of tracer.
264     // (value at start of event)
265     int cumulative_incremental_marking_steps;
266 
267     // Incremental marking steps since
268     // - last event for SCAVENGER events
269     // - last INCREMENTAL_MARK_COMPACTOR event for INCREMENTAL_MARK_COMPACTOR
270     // events
271     int incremental_marking_steps;
272 
273     // Bytes marked since creation of tracer (value at start of event).
274     intptr_t cumulative_incremental_marking_bytes;
275 
276     // Bytes marked since
277     // - last event for SCAVENGER events
278     // - last INCREMENTAL_MARK_COMPACTOR event for INCREMENTAL_MARK_COMPACTOR
279     // events
280     intptr_t incremental_marking_bytes;
281 
282     // Cumulative duration of incremental marking steps since creation of
283     // tracer. (value at start of event)
284     double cumulative_incremental_marking_duration;
285 
286     // Duration of incremental marking steps since
287     // - last event for SCAVENGER events
288     // - last INCREMENTAL_MARK_COMPACTOR event for INCREMENTAL_MARK_COMPACTOR
289     // events
290     double incremental_marking_duration;
291 
292     // Cumulative pure duration of incremental marking steps since creation of
293     // tracer. (value at start of event)
294     double cumulative_pure_incremental_marking_duration;
295 
296     // Duration of pure incremental marking steps since
297     // - last event for SCAVENGER events
298     // - last INCREMENTAL_MARK_COMPACTOR event for INCREMENTAL_MARK_COMPACTOR
299     // events
300     double pure_incremental_marking_duration;
301 
302     // Longest incremental marking step since start of marking.
303     // (value at start of event)
304     double longest_incremental_marking_step;
305 
306     // Amounts of time spent in different scopes during GC.
307     double scopes[Scope::NUMBER_OF_SCOPES];
308   };
309 
310   static const size_t kRingBufferMaxSize = 10;
311 
312   typedef RingBuffer<Event, kRingBufferMaxSize> EventBuffer;
313 
314   typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer;
315 
316   typedef RingBuffer<ContextDisposalEvent, kRingBufferMaxSize>
317       ContextDisposalEventBuffer;
318 
319   typedef RingBuffer<CompactionEvent, kRingBufferMaxSize> CompactionEventBuffer;
320 
321   typedef RingBuffer<SurvivalEvent, kRingBufferMaxSize> SurvivalEventBuffer;
322 
323   static const int kThroughputTimeFrameMs = 5000;
324 
325   explicit GCTracer(Heap* heap);
326 
327   // Start collecting data.
328   void Start(GarbageCollector collector, const char* gc_reason,
329              const char* collector_reason);
330 
331   // Stop collecting data and print results.
332   void Stop(GarbageCollector collector);
333 
334   // Sample and accumulate bytes allocated since the last GC.
335   void SampleAllocation(double current_ms, size_t new_space_counter_bytes,
336                         size_t old_generation_counter_bytes);
337 
338   // Log the accumulated new space allocation bytes.
339   void AddAllocation(double current_ms);
340 
341   void AddContextDisposalTime(double time);
342 
343   void AddCompactionEvent(double duration, intptr_t live_bytes_compacted);
344 
345   void AddSurvivalRatio(double survival_ratio);
346 
347   // Log an incremental marking step.
348   void AddIncrementalMarkingStep(double duration, intptr_t bytes);
349 
350   void AddIncrementalMarkingFinalizationStep(double duration);
351 
352   // Log time spent in marking.
AddMarkingTime(double duration)353   void AddMarkingTime(double duration) {
354     cumulative_marking_duration_ += duration;
355   }
356 
357   // Time spent in marking.
cumulative_marking_duration()358   double cumulative_marking_duration() const {
359     return cumulative_marking_duration_;
360   }
361 
362   // Log time spent in sweeping on main thread.
AddSweepingTime(double duration)363   void AddSweepingTime(double duration) {
364     cumulative_sweeping_duration_ += duration;
365   }
366 
367   // Time spent in sweeping on main thread.
cumulative_sweeping_duration()368   double cumulative_sweeping_duration() const {
369     return cumulative_sweeping_duration_;
370   }
371 
372   // Compute the mean duration of the last scavenger events. Returns 0 if no
373   // events have been recorded.
MeanScavengerDuration()374   double MeanScavengerDuration() const {
375     return MeanDuration(scavenger_events_);
376   }
377 
378   // Compute the max duration of the last scavenger events. Returns 0 if no
379   // events have been recorded.
MaxScavengerDuration()380   double MaxScavengerDuration() const { return MaxDuration(scavenger_events_); }
381 
382   // Compute the mean duration of the last mark compactor events. Returns 0 if
383   // no events have been recorded.
MeanMarkCompactorDuration()384   double MeanMarkCompactorDuration() const {
385     return MeanDuration(mark_compactor_events_);
386   }
387 
388   // Compute the max duration of the last mark compactor events. Return 0 if no
389   // events have been recorded.
MaxMarkCompactorDuration()390   double MaxMarkCompactorDuration() const {
391     return MaxDuration(mark_compactor_events_);
392   }
393 
394   // Compute the mean duration of the last incremental mark compactor
395   // events. Returns 0 if no events have been recorded.
MeanIncrementalMarkCompactorDuration()396   double MeanIncrementalMarkCompactorDuration() const {
397     return MeanDuration(incremental_mark_compactor_events_);
398   }
399 
400   // Compute the mean step duration of the last incremental marking round.
401   // Returns 0 if no incremental marking round has been completed.
402   double MeanIncrementalMarkingDuration() const;
403 
404   // Compute the max step duration of the last incremental marking round.
405   // Returns 0 if no incremental marking round has been completed.
406   double MaxIncrementalMarkingDuration() const;
407 
408   // Compute the average incremental marking speed in bytes/millisecond.
409   // Returns 0 if no events have been recorded.
410   intptr_t IncrementalMarkingSpeedInBytesPerMillisecond() const;
411 
412   // Compute the average scavenge speed in bytes/millisecond.
413   // Returns 0 if no events have been recorded.
414   intptr_t ScavengeSpeedInBytesPerMillisecond(
415       ScavengeSpeedMode mode = kForAllObjects) const;
416 
417   // Compute the average compaction speed in bytes/millisecond.
418   // Returns 0 if not enough events have been recorded.
419   intptr_t CompactionSpeedInBytesPerMillisecond() const;
420 
421   // Compute the average mark-sweep speed in bytes/millisecond.
422   // Returns 0 if no events have been recorded.
423   intptr_t MarkCompactSpeedInBytesPerMillisecond() const;
424 
425   // Compute the average incremental mark-sweep finalize speed in
426   // bytes/millisecond.
427   // Returns 0 if no events have been recorded.
428   intptr_t FinalIncrementalMarkCompactSpeedInBytesPerMillisecond() const;
429 
430   // Compute the overall mark compact speed including incremental steps
431   // and the final mark-compact step.
432   double CombinedMarkCompactSpeedInBytesPerMillisecond();
433 
434   // Allocation throughput in the new space in bytes/millisecond.
435   // Returns 0 if no allocation events have been recorded.
436   size_t NewSpaceAllocationThroughputInBytesPerMillisecond(
437       double time_ms = 0) const;
438 
439   // Allocation throughput in the old generation in bytes/millisecond in the
440   // last time_ms milliseconds.
441   // Returns 0 if no allocation events have been recorded.
442   size_t OldGenerationAllocationThroughputInBytesPerMillisecond(
443       double time_ms = 0) const;
444 
445   // Allocation throughput in heap in bytes/millisecond in the last time_ms
446   // milliseconds.
447   // Returns 0 if no allocation events have been recorded.
448   size_t AllocationThroughputInBytesPerMillisecond(double time_ms) const;
449 
450   // Allocation throughput in heap in bytes/milliseconds in the last
451   // kThroughputTimeFrameMs seconds.
452   // Returns 0 if no allocation events have been recorded.
453   size_t CurrentAllocationThroughputInBytesPerMillisecond() const;
454 
455   // Allocation throughput in old generation in bytes/milliseconds in the last
456   // kThroughputTimeFrameMs seconds.
457   // Returns 0 if no allocation events have been recorded.
458   size_t CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const;
459 
460   // Computes the context disposal rate in milliseconds. It takes the time
461   // frame of the first recorded context disposal to the current time and
462   // divides it by the number of recorded events.
463   // Returns 0 if no events have been recorded.
464   double ContextDisposalRateInMilliseconds() const;
465 
466   // Computes the average survival ratio based on the last recorded survival
467   // events.
468   // Returns 0 if no events have been recorded.
469   double AverageSurvivalRatio() const;
470 
471   // Returns true if at least one survival event was recorded.
472   bool SurvivalEventsRecorded() const;
473 
474   // Discard all recorded survival events.
475   void ResetSurvivalEvents();
476 
477  private:
478   // Print one detailed trace line in name=value format.
479   // TODO(ernstm): Move to Heap.
480   void PrintNVP() const;
481 
482   // Print one trace line.
483   // TODO(ernstm): Move to Heap.
484   void Print() const;
485 
486   // Prints a line and also adds it to the heap's ring buffer so that
487   // it can be included in later crash dumps.
488   void Output(const char* format, ...) const;
489 
490   // Compute the mean duration of the events in the given ring buffer.
491   double MeanDuration(const EventBuffer& events) const;
492 
493   // Compute the max duration of the events in the given ring buffer.
494   double MaxDuration(const EventBuffer& events) const;
495 
ClearMarkCompactStatistics()496   void ClearMarkCompactStatistics() {
497     cumulative_incremental_marking_steps_ = 0;
498     cumulative_incremental_marking_bytes_ = 0;
499     cumulative_incremental_marking_duration_ = 0;
500     cumulative_pure_incremental_marking_duration_ = 0;
501     longest_incremental_marking_step_ = 0;
502     cumulative_incremental_marking_finalization_steps_ = 0;
503     cumulative_incremental_marking_finalization_duration_ = 0;
504     longest_incremental_marking_finalization_step_ = 0;
505     cumulative_marking_duration_ = 0;
506     cumulative_sweeping_duration_ = 0;
507   }
508 
509   // Pointer to the heap that owns this tracer.
510   Heap* heap_;
511 
512   // Current tracer event. Populated during Start/Stop cycle. Valid after Stop()
513   // has returned.
514   Event current_;
515 
516   // Previous tracer event.
517   Event previous_;
518 
519   // Previous INCREMENTAL_MARK_COMPACTOR event.
520   Event previous_incremental_mark_compactor_event_;
521 
522   // RingBuffers for SCAVENGER events.
523   EventBuffer scavenger_events_;
524 
525   // RingBuffers for MARK_COMPACTOR events.
526   EventBuffer mark_compactor_events_;
527 
528   // RingBuffers for INCREMENTAL_MARK_COMPACTOR events.
529   EventBuffer incremental_mark_compactor_events_;
530 
531   // RingBuffer for allocation events.
532   AllocationEventBuffer new_space_allocation_events_;
533   AllocationEventBuffer old_generation_allocation_events_;
534 
535   // RingBuffer for context disposal events.
536   ContextDisposalEventBuffer context_disposal_events_;
537 
538   // RingBuffer for compaction events.
539   CompactionEventBuffer compaction_events_;
540 
541   // RingBuffer for survival events.
542   SurvivalEventBuffer survival_events_;
543 
544   // Cumulative number of incremental marking steps since creation of tracer.
545   int cumulative_incremental_marking_steps_;
546 
547   // Cumulative size of incremental marking steps (in bytes) since creation of
548   // tracer.
549   intptr_t cumulative_incremental_marking_bytes_;
550 
551   // Cumulative duration of incremental marking steps since creation of tracer.
552   double cumulative_incremental_marking_duration_;
553 
554   // Cumulative duration of pure incremental marking steps since creation of
555   // tracer.
556   double cumulative_pure_incremental_marking_duration_;
557 
558   // Longest incremental marking step since start of marking.
559   double longest_incremental_marking_step_;
560 
561   // Cumulative number of incremental marking finalization steps since creation
562   // of tracer.
563   int cumulative_incremental_marking_finalization_steps_;
564 
565   // Cumulative duration of incremental marking finalization steps since
566   // creation of tracer.
567   double cumulative_incremental_marking_finalization_duration_;
568 
569   // Longest incremental marking finalization step since start of marking.
570   double longest_incremental_marking_finalization_step_;
571 
572   // Total marking time.
573   // This timer is precise when run with --print-cumulative-gc-stat
574   double cumulative_marking_duration_;
575 
576   // Total sweeping time on the main thread.
577   // This timer is precise when run with --print-cumulative-gc-stat
578   // TODO(hpayer): Account for sweeping time on sweeper threads. Add a
579   // different field for that.
580   // TODO(hpayer): This timer right now just holds the sweeping time
581   // of the initial atomic sweeping pause. Make sure that it accumulates
582   // all sweeping operations performed on the main thread.
583   double cumulative_sweeping_duration_;
584 
585   // Timestamp and allocation counter at the last sampled allocation event.
586   double allocation_time_ms_;
587   size_t new_space_allocation_counter_bytes_;
588   size_t old_generation_allocation_counter_bytes_;
589 
590   // Accumulated duration and allocated bytes since the last GC.
591   double allocation_duration_since_gc_;
592   size_t new_space_allocation_in_bytes_since_gc_;
593   size_t old_generation_allocation_in_bytes_since_gc_;
594 
595   double combined_mark_compact_speed_cache_;
596 
597   // Counts how many tracers were started without stopping.
598   int start_counter_;
599 
600   DISALLOW_COPY_AND_ASSIGN(GCTracer);
601 };
602 }  // namespace internal
603 }  // namespace v8
604 
605 #endif  // V8_HEAP_GC_TRACER_H_
606