Line data Source code
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" // nogncheck
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 : typedef std::pair<uint64_t, double> BytesAndDuration;
19 :
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_EXTERNAL_EPILOGUE) \
35 : F(MC_INCREMENTAL_EXTERNAL_PROLOGUE)
36 :
37 : #define TRACER_SCOPES(F) \
38 : INCREMENTAL_SCOPES(F) \
39 : F(HEAP_EPILOGUE) \
40 : F(HEAP_EPILOGUE_REDUCE_NEW_SPACE) \
41 : F(HEAP_EXTERNAL_EPILOGUE) \
42 : F(HEAP_EXTERNAL_PROLOGUE) \
43 : F(HEAP_EXTERNAL_WEAK_GLOBAL_HANDLES) \
44 : F(HEAP_PROLOGUE) \
45 : F(MC_CLEAR) \
46 : F(MC_CLEAR_CODE_FLUSH) \
47 : F(MC_CLEAR_DEPENDENT_CODE) \
48 : F(MC_CLEAR_MAPS) \
49 : F(MC_CLEAR_SLOTS_BUFFER) \
50 : F(MC_CLEAR_STORE_BUFFER) \
51 : F(MC_CLEAR_STRING_TABLE) \
52 : F(MC_CLEAR_WEAK_CELLS) \
53 : F(MC_CLEAR_WEAK_COLLECTIONS) \
54 : F(MC_CLEAR_WEAK_LISTS) \
55 : F(MC_EPILOGUE) \
56 : F(MC_EVACUATE) \
57 : F(MC_EVACUATE_CANDIDATES) \
58 : F(MC_EVACUATE_CLEAN_UP) \
59 : F(MC_EVACUATE_COPY) \
60 : F(MC_EVACUATE_EPILOGUE) \
61 : F(MC_EVACUATE_PROLOGUE) \
62 : F(MC_EVACUATE_REBALANCE) \
63 : F(MC_EVACUATE_UPDATE_POINTERS) \
64 : F(MC_EVACUATE_UPDATE_POINTERS_TO_EVACUATED) \
65 : F(MC_EVACUATE_UPDATE_POINTERS_TO_NEW) \
66 : F(MC_EVACUATE_UPDATE_POINTERS_WEAK) \
67 : F(MC_FINISH) \
68 : F(MC_MARK) \
69 : F(MC_MARK_FINISH_INCREMENTAL) \
70 : F(MC_MARK_PREPARE_CODE_FLUSH) \
71 : F(MC_MARK_ROOTS) \
72 : F(MC_MARK_WEAK_CLOSURE) \
73 : F(MC_MARK_WEAK_CLOSURE_EPHEMERAL) \
74 : F(MC_MARK_WEAK_CLOSURE_WEAK_HANDLES) \
75 : F(MC_MARK_WEAK_CLOSURE_WEAK_ROOTS) \
76 : F(MC_MARK_WEAK_CLOSURE_HARMONY) \
77 : F(MC_MARK_WRAPPER_EPILOGUE) \
78 : F(MC_MARK_WRAPPER_PROLOGUE) \
79 : F(MC_MARK_WRAPPER_TRACING) \
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(MC_MINOR_MC) \
86 : F(MINOR_MC_MARK) \
87 : F(MINOR_MC_MARK_CODE_FLUSH_CANDIDATES) \
88 : F(MINOR_MC_MARK_GLOBAL_HANDLES) \
89 : F(MINOR_MC_MARK_OLD_TO_NEW_POINTERS) \
90 : F(MINOR_MC_MARK_ROOTS) \
91 : F(MINOR_MC_MARK_WEAK) \
92 : F(SCAVENGER_CODE_FLUSH_CANDIDATES) \
93 : F(SCAVENGER_EVACUATE) \
94 : F(SCAVENGER_OLD_TO_NEW_POINTERS) \
95 : F(SCAVENGER_ROOTS) \
96 : F(SCAVENGER_SCAVENGE) \
97 : F(SCAVENGER_SEMISPACE) \
98 : F(SCAVENGER_WEAK)
99 :
100 : #define TRACE_GC(tracer, scope_id) \
101 : GCTracer::Scope::ScopeId gc_tracer_scope_id(scope_id); \
102 : GCTracer::Scope gc_tracer_scope(tracer, gc_tracer_scope_id); \
103 : TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.gc"), \
104 : GCTracer::Scope::Name(gc_tracer_scope_id))
105 :
106 : // GCTracer collects and prints ONE line after each garbage collector
107 : // invocation IFF --trace_gc is used.
108 : class V8_EXPORT_PRIVATE GCTracer {
109 : public:
110 : struct IncrementalMarkingInfos {
111 1952632 : IncrementalMarkingInfos() : duration(0), longest_step(0), steps(0) {}
112 :
113 25363 : void Update(double duration) {
114 25363 : steps++;
115 25363 : this->duration += duration;
116 25363 : if (duration > longest_step) {
117 6187 : longest_step = duration;
118 : }
119 25363 : }
120 :
121 : void ResetCurrentCycle() {
122 426784 : duration = 0;
123 426784 : longest_step = 0;
124 426784 : steps = 0;
125 : }
126 :
127 : double duration;
128 : double longest_step;
129 : int steps;
130 : };
131 :
132 : class Scope {
133 : public:
134 : enum ScopeId {
135 : #define DEFINE_SCOPE(scope) scope,
136 : TRACER_SCOPES(DEFINE_SCOPE)
137 : #undef DEFINE_SCOPE
138 : NUMBER_OF_SCOPES,
139 :
140 : FIRST_INCREMENTAL_SCOPE = MC_INCREMENTAL,
141 : LAST_INCREMENTAL_SCOPE = MC_INCREMENTAL_EXTERNAL_PROLOGUE,
142 : NUMBER_OF_INCREMENTAL_SCOPES =
143 : LAST_INCREMENTAL_SCOPE - FIRST_INCREMENTAL_SCOPE + 1
144 : };
145 :
146 : Scope(GCTracer* tracer, ScopeId scope);
147 : ~Scope();
148 : static const char* Name(ScopeId id);
149 :
150 : private:
151 : GCTracer* tracer_;
152 : ScopeId scope_;
153 : double start_time_;
154 : RuntimeCallTimer timer_;
155 :
156 : DISALLOW_COPY_AND_ASSIGN(Scope);
157 : };
158 :
159 :
160 : class Event {
161 : public:
162 : enum Type {
163 : SCAVENGER = 0,
164 : MARK_COMPACTOR = 1,
165 : INCREMENTAL_MARK_COMPACTOR = 2,
166 : MINOR_MARK_COMPACTOR = 3,
167 : START = 4
168 : };
169 :
170 : Event(Type type, GarbageCollectionReason gc_reason,
171 : const char* collector_reason);
172 :
173 : // Returns a string describing the event type.
174 : const char* TypeName(bool short_name) const;
175 :
176 : // Type of event
177 : Type type;
178 :
179 : GarbageCollectionReason gc_reason;
180 : const char* collector_reason;
181 :
182 : // Timestamp set in the constructor.
183 : double start_time;
184 :
185 : // Timestamp set in the destructor.
186 : double end_time;
187 :
188 : // Memory reduction flag set.
189 : bool reduce_memory;
190 :
191 : // Size of objects in heap set in constructor.
192 : size_t start_object_size;
193 :
194 : // Size of objects in heap set in destructor.
195 : size_t end_object_size;
196 :
197 : // Size of memory allocated from OS set in constructor.
198 : size_t start_memory_size;
199 :
200 : // Size of memory allocated from OS set in destructor.
201 : size_t end_memory_size;
202 :
203 : // Total amount of space either wasted or contained in one of free lists
204 : // before the current GC.
205 : size_t start_holes_size;
206 :
207 : // Total amount of space either wasted or contained in one of free lists
208 : // after the current GC.
209 : size_t end_holes_size;
210 :
211 : // Size of new space objects in constructor.
212 : size_t new_space_object_size;
213 :
214 : // Size of survived new space objects in destructor.
215 : size_t survived_new_space_object_size;
216 :
217 : // Bytes marked incrementally for INCREMENTAL_MARK_COMPACTOR
218 : size_t incremental_marking_bytes;
219 :
220 : // Duration of incremental marking steps for INCREMENTAL_MARK_COMPACTOR.
221 : double incremental_marking_duration;
222 :
223 : // Amounts of time spent in different scopes during GC.
224 : double scopes[Scope::NUMBER_OF_SCOPES];
225 :
226 : // Holds details for incremental marking scopes.
227 : IncrementalMarkingInfos
228 : incremental_marking_scopes[Scope::NUMBER_OF_INCREMENTAL_SCOPES];
229 : };
230 :
231 : static const int kThroughputTimeFrameMs = 5000;
232 :
233 : explicit GCTracer(Heap* heap);
234 :
235 : // Start collecting data.
236 : void Start(GarbageCollector collector, GarbageCollectionReason gc_reason,
237 : const char* collector_reason);
238 :
239 : // Stop collecting data and print results.
240 : void Stop(GarbageCollector collector);
241 :
242 : void NotifyYoungGenerationHandling(
243 : YoungGenerationHandling young_generation_handling);
244 :
245 : // Sample and accumulate bytes allocated since the last GC.
246 : void SampleAllocation(double current_ms, size_t new_space_counter_bytes,
247 : size_t old_generation_counter_bytes);
248 :
249 : // Log the accumulated new space allocation bytes.
250 : void AddAllocation(double current_ms);
251 :
252 : void AddContextDisposalTime(double time);
253 :
254 : void AddCompactionEvent(double duration, size_t live_bytes_compacted);
255 :
256 : void AddSurvivalRatio(double survival_ratio);
257 :
258 : // Log an incremental marking step.
259 : void AddIncrementalMarkingStep(double duration, size_t bytes);
260 :
261 : // Compute the average incremental marking speed in bytes/millisecond.
262 : // Returns 0 if no events have been recorded.
263 : double IncrementalMarkingSpeedInBytesPerMillisecond() const;
264 :
265 : // Compute the average scavenge speed in bytes/millisecond.
266 : // Returns 0 if no events have been recorded.
267 : double ScavengeSpeedInBytesPerMillisecond(
268 : ScavengeSpeedMode mode = kForAllObjects) const;
269 :
270 : // Compute the average compaction speed in bytes/millisecond.
271 : // Returns 0 if not enough events have been recorded.
272 : double CompactionSpeedInBytesPerMillisecond() const;
273 :
274 : // Compute the average mark-sweep speed in bytes/millisecond.
275 : // Returns 0 if no events have been recorded.
276 : double MarkCompactSpeedInBytesPerMillisecond() const;
277 :
278 : // Compute the average incremental mark-sweep finalize speed in
279 : // bytes/millisecond.
280 : // Returns 0 if no events have been recorded.
281 : double FinalIncrementalMarkCompactSpeedInBytesPerMillisecond() const;
282 :
283 : // Compute the overall mark compact speed including incremental steps
284 : // and the final mark-compact step.
285 : double CombinedMarkCompactSpeedInBytesPerMillisecond();
286 :
287 : // Allocation throughput in the new space in bytes/millisecond.
288 : // Returns 0 if no allocation events have been recorded.
289 : double NewSpaceAllocationThroughputInBytesPerMillisecond(
290 : double time_ms = 0) const;
291 :
292 : // Allocation throughput in the old generation in bytes/millisecond in the
293 : // last time_ms milliseconds.
294 : // Returns 0 if no allocation events have been recorded.
295 : double OldGenerationAllocationThroughputInBytesPerMillisecond(
296 : double time_ms = 0) const;
297 :
298 : // Allocation throughput in heap in bytes/millisecond in the last time_ms
299 : // milliseconds.
300 : // Returns 0 if no allocation events have been recorded.
301 : double AllocationThroughputInBytesPerMillisecond(double time_ms) const;
302 :
303 : // Allocation throughput in heap in bytes/milliseconds in the last
304 : // kThroughputTimeFrameMs seconds.
305 : // Returns 0 if no allocation events have been recorded.
306 : double CurrentAllocationThroughputInBytesPerMillisecond() const;
307 :
308 : // Allocation throughput in old generation in bytes/milliseconds in the last
309 : // kThroughputTimeFrameMs seconds.
310 : // Returns 0 if no allocation events have been recorded.
311 : double CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const;
312 :
313 : // Computes the context disposal rate in milliseconds. It takes the time
314 : // frame of the first recorded context disposal to the current time and
315 : // divides it by the number of recorded events.
316 : // Returns 0 if no events have been recorded.
317 : double ContextDisposalRateInMilliseconds() const;
318 :
319 : // Computes the average survival ratio based on the last recorded survival
320 : // events.
321 : // Returns 0 if no events have been recorded.
322 : double AverageSurvivalRatio() const;
323 :
324 : // Returns true if at least one survival event was recorded.
325 : bool SurvivalEventsRecorded() const;
326 :
327 : // Discard all recorded survival events.
328 : void ResetSurvivalEvents();
329 :
330 : void NotifyIncrementalMarkingStart();
331 :
332 : V8_INLINE void AddScopeSample(Scope::ScopeId scope, double duration) {
333 : DCHECK(scope < Scope::NUMBER_OF_SCOPES);
334 3094966 : if (scope >= Scope::FIRST_INCREMENTAL_SCOPE &&
335 : scope <= Scope::LAST_INCREMENTAL_SCOPE) {
336 25363 : incremental_marking_scopes_[scope - Scope::FIRST_INCREMENTAL_SCOPE]
337 25363 : .Update(duration);
338 : } else {
339 3069603 : current_.scopes[scope] += duration;
340 : }
341 : }
342 :
343 : private:
344 : FRIEND_TEST(GCTracer, AverageSpeed);
345 : FRIEND_TEST(GCTracerTest, AllocationThroughput);
346 : FRIEND_TEST(GCTracerTest, NewSpaceAllocationThroughput);
347 : FRIEND_TEST(GCTracerTest, NewSpaceAllocationThroughputWithProvidedTime);
348 : FRIEND_TEST(GCTracerTest, OldGenerationAllocationThroughputWithProvidedTime);
349 : FRIEND_TEST(GCTracerTest, RegularScope);
350 : FRIEND_TEST(GCTracerTest, IncrementalMarkingDetails);
351 : FRIEND_TEST(GCTracerTest, IncrementalScope);
352 : FRIEND_TEST(GCTracerTest, IncrementalMarkingSpeed);
353 :
354 : // Returns the average speed of the events in the buffer.
355 : // If the buffer is empty, the result is 0.
356 : // Otherwise, the result is between 1 byte/ms and 1 GB/ms.
357 : static double AverageSpeed(const base::RingBuffer<BytesAndDuration>& buffer);
358 : static double AverageSpeed(const base::RingBuffer<BytesAndDuration>& buffer,
359 : const BytesAndDuration& initial, double time_ms);
360 :
361 : void ResetForTesting();
362 : void ResetIncrementalMarkingCounters();
363 : void RecordIncrementalMarkingSpeed(size_t bytes, double duration);
364 :
365 : // Print one detailed trace line in name=value format.
366 : // TODO(ernstm): Move to Heap.
367 : void PrintNVP() const;
368 :
369 : // Print one trace line.
370 : // TODO(ernstm): Move to Heap.
371 : void Print() const;
372 :
373 : // Prints a line and also adds it to the heap's ring buffer so that
374 : // it can be included in later crash dumps.
375 : void PRINTF_FORMAT(2, 3) Output(const char* format, ...) const;
376 :
377 : double TotalExternalTime() const {
378 245014 : return current_.scopes[Scope::HEAP_EXTERNAL_WEAK_GLOBAL_HANDLES] +
379 245014 : current_.scopes[Scope::HEAP_EXTERNAL_EPILOGUE] +
380 245014 : current_.scopes[Scope::HEAP_EXTERNAL_PROLOGUE] +
381 122507 : current_.scopes[Scope::MC_INCREMENTAL_EXTERNAL_EPILOGUE] +
382 122507 : current_.scopes[Scope::MC_INCREMENTAL_EXTERNAL_PROLOGUE];
383 : }
384 :
385 : // Pointer to the heap that owns this tracer.
386 : Heap* heap_;
387 :
388 : // Current tracer event. Populated during Start/Stop cycle. Valid after Stop()
389 : // has returned.
390 : Event current_;
391 :
392 : // Previous tracer event.
393 : Event previous_;
394 :
395 : // Size of incremental marking steps (in bytes) accumulated since the end of
396 : // the last mark compact GC.
397 : size_t incremental_marking_bytes_;
398 :
399 : // Duration of incremental marking steps since the end of the last mark-
400 : // compact event.
401 : double incremental_marking_duration_;
402 :
403 : double incremental_marking_start_time_;
404 :
405 : double recorded_incremental_marking_speed_;
406 :
407 : // Incremental scopes carry more information than just the duration. The infos
408 : // here are merged back upon starting/stopping the GC tracer.
409 : IncrementalMarkingInfos
410 : incremental_marking_scopes_[Scope::NUMBER_OF_INCREMENTAL_SCOPES];
411 :
412 :
413 : // Timestamp and allocation counter at the last sampled allocation event.
414 : double allocation_time_ms_;
415 : size_t new_space_allocation_counter_bytes_;
416 : size_t old_generation_allocation_counter_bytes_;
417 :
418 : // Accumulated duration and allocated bytes since the last GC.
419 : double allocation_duration_since_gc_;
420 : size_t new_space_allocation_in_bytes_since_gc_;
421 : size_t old_generation_allocation_in_bytes_since_gc_;
422 :
423 : double combined_mark_compact_speed_cache_;
424 :
425 : // Counts how many tracers were started without stopping.
426 : int start_counter_;
427 :
428 : // Separate timer used for --runtime_call_stats
429 : RuntimeCallTimer timer_;
430 :
431 : base::RingBuffer<BytesAndDuration> recorded_minor_gcs_total_;
432 : base::RingBuffer<BytesAndDuration> recorded_minor_gcs_survived_;
433 : base::RingBuffer<BytesAndDuration> recorded_compactions_;
434 : base::RingBuffer<BytesAndDuration> recorded_incremental_mark_compacts_;
435 : base::RingBuffer<BytesAndDuration> recorded_mark_compacts_;
436 : base::RingBuffer<BytesAndDuration> recorded_new_generation_allocations_;
437 : base::RingBuffer<BytesAndDuration> recorded_old_generation_allocations_;
438 : base::RingBuffer<double> recorded_context_disposal_times_;
439 : base::RingBuffer<double> recorded_survival_ratios_;
440 :
441 : DISALLOW_COPY_AND_ASSIGN(GCTracer);
442 : };
443 : } // namespace internal
444 : } // namespace v8
445 :
446 : #endif // V8_HEAP_GC_TRACER_H_
|