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
10namespace v8 {
11namespace internal {
12
13// A simple ring buffer class with maximum size known at compile time.
14// The class only implements the functionality required in GCTracer.
15template <typename T, size_t MAX_SIZE>
16class RingBuffer {
17 public:
18  class const_iterator {
19   public:
20    const_iterator() : index_(0), elements_(NULL) {}
21
22    const_iterator(size_t index, const T* elements)
23        : index_(index), elements_(elements) {}
24
25    bool operator==(const const_iterator& rhs) const {
26      return elements_ == rhs.elements_ && index_ == rhs.index_;
27    }
28
29    bool operator!=(const const_iterator& rhs) const {
30      return elements_ != rhs.elements_ || index_ != rhs.index_;
31    }
32
33    operator const T*() const { return elements_ + index_; }
34
35    const T* operator->() const { return elements_ + index_; }
36
37    const T& operator*() const { return elements_[index_]; }
38
39    const_iterator& operator++() {
40      index_ = (index_ + 1) % (MAX_SIZE + 1);
41      return *this;
42    }
43
44    const_iterator& operator--() {
45      index_ = (index_ + MAX_SIZE) % (MAX_SIZE + 1);
46      return *this;
47    }
48
49   private:
50    size_t index_;
51    const T* elements_;
52  };
53
54  RingBuffer() : begin_(0), end_(0) {}
55
56  bool empty() const { return begin_ == end_; }
57  size_t size() const {
58    return (end_ - begin_ + MAX_SIZE + 1) % (MAX_SIZE + 1);
59  }
60  const_iterator begin() const { return const_iterator(begin_, elements_); }
61  const_iterator end() const { return const_iterator(end_, elements_); }
62  const_iterator back() const { return --end(); }
63  void push_back(const T& element) {
64    elements_[end_] = element;
65    end_ = (end_ + 1) % (MAX_SIZE + 1);
66    if (end_ == begin_) begin_ = (begin_ + 1) % (MAX_SIZE + 1);
67  }
68  void push_front(const T& element) {
69    begin_ = (begin_ + MAX_SIZE) % (MAX_SIZE + 1);
70    if (begin_ == end_) end_ = (end_ + MAX_SIZE) % (MAX_SIZE + 1);
71    elements_[begin_] = element;
72  }
73
74 private:
75  T elements_[MAX_SIZE + 1];
76  size_t begin_;
77  size_t end_;
78
79  DISALLOW_COPY_AND_ASSIGN(RingBuffer);
80};
81
82
83// GCTracer collects and prints ONE line after each garbage collector
84// invocation IFF --trace_gc is used.
85// TODO(ernstm): Unit tests.
86class GCTracer {
87 public:
88  class Scope {
89   public:
90    enum ScopeId {
91      EXTERNAL,
92      MC_MARK,
93      MC_SWEEP,
94      MC_SWEEP_NEWSPACE,
95      MC_SWEEP_OLDSPACE,
96      MC_SWEEP_CODE,
97      MC_SWEEP_CELL,
98      MC_SWEEP_MAP,
99      MC_EVACUATE_PAGES,
100      MC_UPDATE_NEW_TO_NEW_POINTERS,
101      MC_UPDATE_ROOT_TO_NEW_POINTERS,
102      MC_UPDATE_OLD_TO_NEW_POINTERS,
103      MC_UPDATE_POINTERS_TO_EVACUATED,
104      MC_UPDATE_POINTERS_BETWEEN_EVACUATED,
105      MC_UPDATE_MISC_POINTERS,
106      MC_WEAKCOLLECTION_PROCESS,
107      MC_WEAKCOLLECTION_CLEAR,
108      MC_WEAKCOLLECTION_ABORT,
109      MC_FLUSH_CODE,
110      NUMBER_OF_SCOPES
111    };
112
113    Scope(GCTracer* tracer, ScopeId scope) : tracer_(tracer), scope_(scope) {
114      start_time_ = base::OS::TimeCurrentMillis();
115    }
116
117    ~Scope() {
118      DCHECK(scope_ < NUMBER_OF_SCOPES);  // scope_ is unsigned.
119      tracer_->current_.scopes[scope_] +=
120          base::OS::TimeCurrentMillis() - start_time_;
121    }
122
123   private:
124    GCTracer* tracer_;
125    ScopeId scope_;
126    double start_time_;
127
128    DISALLOW_COPY_AND_ASSIGN(Scope);
129  };
130
131
132  class AllocationEvent {
133   public:
134    // Default constructor leaves the event uninitialized.
135    AllocationEvent() {}
136
137    AllocationEvent(double duration, intptr_t allocation_in_bytes);
138
139    // Time spent in the mutator during the end of the last garbage collection
140    // to the beginning of the next garbage collection.
141    double duration_;
142
143    // Memory allocated in the new space during the end of the last garbage
144    // collection to the beginning of the next garbage collection.
145    intptr_t allocation_in_bytes_;
146  };
147
148  class Event {
149   public:
150    enum Type { SCAVENGER = 0, MARK_COMPACTOR = 1, START = 2 };
151
152    // Default constructor leaves the event uninitialized.
153    Event() {}
154
155    Event(Type type, const char* gc_reason, const char* collector_reason);
156
157    // Returns a string describing the event type.
158    const char* TypeName(bool short_name) const;
159
160    // Type of event
161    Type type;
162
163    const char* gc_reason;
164    const char* collector_reason;
165
166    // Timestamp set in the constructor.
167    double start_time;
168
169    // Timestamp set in the destructor.
170    double end_time;
171
172    // Size of objects in heap set in constructor.
173    intptr_t start_object_size;
174
175    // Size of objects in heap set in destructor.
176    intptr_t end_object_size;
177
178    // Size of memory allocated from OS set in constructor.
179    intptr_t start_memory_size;
180
181    // Size of memory allocated from OS set in destructor.
182    intptr_t end_memory_size;
183
184    // Total amount of space either wasted or contained in one of free lists
185    // before the current GC.
186    intptr_t start_holes_size;
187
188    // Total amount of space either wasted or contained in one of free lists
189    // after the current GC.
190    intptr_t end_holes_size;
191
192    // Size of new space objects in constructor.
193    intptr_t new_space_object_size;
194
195    // Number of incremental marking steps since creation of tracer.
196    // (value at start of event)
197    int cumulative_incremental_marking_steps;
198
199    // Incremental marking steps since
200    // - last event for SCAVENGER events
201    // - last MARK_COMPACTOR event for MARK_COMPACTOR events
202    int incremental_marking_steps;
203
204    // Bytes marked since creation of tracer (value at start of event).
205    intptr_t cumulative_incremental_marking_bytes;
206
207    // Bytes marked since
208    // - last event for SCAVENGER events
209    // - last MARK_COMPACTOR event for MARK_COMPACTOR events
210    intptr_t incremental_marking_bytes;
211
212    // Cumulative duration of incremental marking steps since creation of
213    // tracer. (value at start of event)
214    double cumulative_incremental_marking_duration;
215
216    // Duration of incremental marking steps since
217    // - last event for SCAVENGER events
218    // - last MARK_COMPACTOR event for MARK_COMPACTOR events
219    double incremental_marking_duration;
220
221    // Cumulative pure duration of incremental marking steps since creation of
222    // tracer. (value at start of event)
223    double cumulative_pure_incremental_marking_duration;
224
225    // Duration of pure incremental marking steps since
226    // - last event for SCAVENGER events
227    // - last MARK_COMPACTOR event for MARK_COMPACTOR events
228    double pure_incremental_marking_duration;
229
230    // Longest incremental marking step since start of marking.
231    // (value at start of event)
232    double longest_incremental_marking_step;
233
234    // Amounts of time spent in different scopes during GC.
235    double scopes[Scope::NUMBER_OF_SCOPES];
236  };
237
238  static const int kRingBufferMaxSize = 10;
239
240  typedef RingBuffer<Event, kRingBufferMaxSize> EventBuffer;
241
242  typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer;
243
244  explicit GCTracer(Heap* heap);
245
246  // Start collecting data.
247  void Start(GarbageCollector collector, const char* gc_reason,
248             const char* collector_reason);
249
250  // Stop collecting data and print results.
251  void Stop();
252
253  // Log an allocation throughput event.
254  void AddNewSpaceAllocationTime(double duration, intptr_t allocation_in_bytes);
255
256  // Log an incremental marking step.
257  void AddIncrementalMarkingStep(double duration, intptr_t bytes);
258
259  // Log time spent in marking.
260  void AddMarkingTime(double duration) {
261    cumulative_marking_duration_ += duration;
262  }
263
264  // Time spent in marking.
265  double cumulative_marking_duration() const {
266    return cumulative_marking_duration_;
267  }
268
269  // Log time spent in sweeping on main thread.
270  void AddSweepingTime(double duration) {
271    cumulative_sweeping_duration_ += duration;
272  }
273
274  // Time spent in sweeping on main thread.
275  double cumulative_sweeping_duration() const {
276    return cumulative_sweeping_duration_;
277  }
278
279  // Compute the mean duration of the last scavenger events. Returns 0 if no
280  // events have been recorded.
281  double MeanScavengerDuration() const {
282    return MeanDuration(scavenger_events_);
283  }
284
285  // Compute the max duration of the last scavenger events. Returns 0 if no
286  // events have been recorded.
287  double MaxScavengerDuration() const { return MaxDuration(scavenger_events_); }
288
289  // Compute the mean duration of the last mark compactor events. Returns 0 if
290  // no events have been recorded.
291  double MeanMarkCompactorDuration() const {
292    return MeanDuration(mark_compactor_events_);
293  }
294
295  // Compute the max duration of the last mark compactor events. Return 0 if no
296  // events have been recorded.
297  double MaxMarkCompactorDuration() const {
298    return MaxDuration(mark_compactor_events_);
299  }
300
301  // Compute the mean step duration of the last incremental marking round.
302  // Returns 0 if no incremental marking round has been completed.
303  double MeanIncrementalMarkingDuration() const;
304
305  // Compute the max step duration of the last incremental marking round.
306  // Returns 0 if no incremental marking round has been completed.
307  double MaxIncrementalMarkingDuration() const;
308
309  // Compute the average incremental marking speed in bytes/millisecond.
310  // Returns 0 if no events have been recorded.
311  intptr_t IncrementalMarkingSpeedInBytesPerMillisecond() const;
312
313  // Compute the average scavenge speed in bytes/millisecond.
314  // Returns 0 if no events have been recorded.
315  intptr_t ScavengeSpeedInBytesPerMillisecond() const;
316
317  // Compute the max mark-sweep speed in bytes/millisecond.
318  // Returns 0 if no events have been recorded.
319  intptr_t MarkCompactSpeedInBytesPerMillisecond() const;
320
321  // Allocation throughput in the new space in bytes/millisecond.
322  // Returns 0 if no events have been recorded.
323  intptr_t NewSpaceAllocationThroughputInBytesPerMillisecond() const;
324
325 private:
326  // Print one detailed trace line in name=value format.
327  // TODO(ernstm): Move to Heap.
328  void PrintNVP() const;
329
330  // Print one trace line.
331  // TODO(ernstm): Move to Heap.
332  void Print() const;
333
334  // Compute the mean duration of the events in the given ring buffer.
335  double MeanDuration(const EventBuffer& events) const;
336
337  // Compute the max duration of the events in the given ring buffer.
338  double MaxDuration(const EventBuffer& events) const;
339
340  // Pointer to the heap that owns this tracer.
341  Heap* heap_;
342
343  // Current tracer event. Populated during Start/Stop cycle. Valid after Stop()
344  // has returned.
345  Event current_;
346
347  // Previous tracer event.
348  Event previous_;
349
350  // Previous MARK_COMPACTOR event.
351  Event previous_mark_compactor_event_;
352
353  // RingBuffers for SCAVENGER events.
354  EventBuffer scavenger_events_;
355
356  // RingBuffers for MARK_COMPACTOR events.
357  EventBuffer mark_compactor_events_;
358
359  // RingBuffer for allocation events.
360  AllocationEventBuffer allocation_events_;
361
362  // Cumulative number of incremental marking steps since creation of tracer.
363  int cumulative_incremental_marking_steps_;
364
365  // Cumulative size of incremental marking steps (in bytes) since creation of
366  // tracer.
367  intptr_t cumulative_incremental_marking_bytes_;
368
369  // Cumulative duration of incremental marking steps since creation of tracer.
370  double cumulative_incremental_marking_duration_;
371
372  // Cumulative duration of pure incremental marking steps since creation of
373  // tracer.
374  double cumulative_pure_incremental_marking_duration_;
375
376  // Longest incremental marking step since start of marking.
377  double longest_incremental_marking_step_;
378
379  // Total marking time.
380  // This timer is precise when run with --print-cumulative-gc-stat
381  double cumulative_marking_duration_;
382
383  // Total sweeping time on the main thread.
384  // This timer is precise when run with --print-cumulative-gc-stat
385  // TODO(hpayer): Account for sweeping time on sweeper threads. Add a
386  // different field for that.
387  // TODO(hpayer): This timer right now just holds the sweeping time
388  // of the initial atomic sweeping pause. Make sure that it accumulates
389  // all sweeping operations performed on the main thread.
390  double cumulative_sweeping_duration_;
391
392  // Holds the new space top pointer recorded at the end of the last garbage
393  // collection.
394  intptr_t new_space_top_after_gc_;
395
396  DISALLOW_COPY_AND_ASSIGN(GCTracer);
397};
398}
399}  // namespace v8::internal
400
401#endif  // V8_HEAP_GC_TRACER_H_
402