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