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