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