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 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 { 100 IncrementalMarkingInfos() : duration(0), longest_step(0), steps(0) {} 101 102 void Update(double duration) { 103 steps++; 104 this->duration += duration; 105 if (duration > longest_step) { 106 longest_step = duration; 107 } 108 } 109 110 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 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 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