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_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 { 110 IncrementalMarkingInfos() : duration(0), longest_step(0), steps(0) {} 111 112 void Update(double duration) { 113 steps++; 114 this->duration += duration; 115 if (duration > longest_step) { 116 longest_step = duration; 117 } 118 } 119 120 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 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 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