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/platform/platform.h" 9 10 namespace v8 { 11 namespace internal { 12 13 // A simple ring buffer class with maximum size known at compile time. 14 // The class only implements the functionality required in GCTracer. 15 template <typename T, size_t MAX_SIZE> 16 class RingBuffer { 17 public: 18 class const_iterator { 19 public: 20 const_iterator() : index_(0), elements_(NULL) {} 21 22 const_iterator(size_t index, const T* elements) 23 : index_(index), elements_(elements) {} 24 25 bool operator==(const const_iterator& rhs) const { 26 return elements_ == rhs.elements_ && index_ == rhs.index_; 27 } 28 29 bool operator!=(const const_iterator& rhs) const { 30 return elements_ != rhs.elements_ || index_ != rhs.index_; 31 } 32 33 operator const T*() const { return elements_ + index_; } 34 35 const T* operator->() const { return elements_ + index_; } 36 37 const T& operator*() const { return elements_[index_]; } 38 39 const_iterator& operator++() { 40 index_ = (index_ + 1) % (MAX_SIZE + 1); 41 return *this; 42 } 43 44 const_iterator& operator--() { 45 index_ = (index_ + MAX_SIZE) % (MAX_SIZE + 1); 46 return *this; 47 } 48 49 private: 50 size_t index_; 51 const T* elements_; 52 }; 53 54 RingBuffer() : begin_(0), end_(0) {} 55 56 bool empty() const { return begin_ == end_; } 57 size_t size() const { 58 return (end_ - begin_ + MAX_SIZE + 1) % (MAX_SIZE + 1); 59 } 60 const_iterator begin() const { return const_iterator(begin_, elements_); } 61 const_iterator end() const { return const_iterator(end_, elements_); } 62 const_iterator back() const { return --end(); } 63 void push_back(const T& element) { 64 elements_[end_] = element; 65 end_ = (end_ + 1) % (MAX_SIZE + 1); 66 if (end_ == begin_) begin_ = (begin_ + 1) % (MAX_SIZE + 1); 67 } 68 void push_front(const T& element) { 69 begin_ = (begin_ + MAX_SIZE) % (MAX_SIZE + 1); 70 if (begin_ == end_) end_ = (end_ + MAX_SIZE) % (MAX_SIZE + 1); 71 elements_[begin_] = element; 72 } 73 74 private: 75 T elements_[MAX_SIZE + 1]; 76 size_t begin_; 77 size_t end_; 78 79 DISALLOW_COPY_AND_ASSIGN(RingBuffer); 80 }; 81 82 83 // GCTracer collects and prints ONE line after each garbage collector 84 // invocation IFF --trace_gc is used. 85 // TODO(ernstm): Unit tests. 86 class GCTracer { 87 public: 88 class Scope { 89 public: 90 enum ScopeId { 91 EXTERNAL, 92 MC_MARK, 93 MC_SWEEP, 94 MC_SWEEP_NEWSPACE, 95 MC_SWEEP_OLDSPACE, 96 MC_SWEEP_CODE, 97 MC_SWEEP_CELL, 98 MC_SWEEP_MAP, 99 MC_EVACUATE_PAGES, 100 MC_UPDATE_NEW_TO_NEW_POINTERS, 101 MC_UPDATE_ROOT_TO_NEW_POINTERS, 102 MC_UPDATE_OLD_TO_NEW_POINTERS, 103 MC_UPDATE_POINTERS_TO_EVACUATED, 104 MC_UPDATE_POINTERS_BETWEEN_EVACUATED, 105 MC_UPDATE_MISC_POINTERS, 106 MC_WEAKCOLLECTION_PROCESS, 107 MC_WEAKCOLLECTION_CLEAR, 108 MC_WEAKCOLLECTION_ABORT, 109 MC_FLUSH_CODE, 110 NUMBER_OF_SCOPES 111 }; 112 113 Scope(GCTracer* tracer, ScopeId scope) : tracer_(tracer), scope_(scope) { 114 start_time_ = base::OS::TimeCurrentMillis(); 115 } 116 117 ~Scope() { 118 DCHECK(scope_ < NUMBER_OF_SCOPES); // scope_ is unsigned. 119 tracer_->current_.scopes[scope_] += 120 base::OS::TimeCurrentMillis() - start_time_; 121 } 122 123 private: 124 GCTracer* tracer_; 125 ScopeId scope_; 126 double start_time_; 127 128 DISALLOW_COPY_AND_ASSIGN(Scope); 129 }; 130 131 132 class AllocationEvent { 133 public: 134 // Default constructor leaves the event uninitialized. 135 AllocationEvent() {} 136 137 AllocationEvent(double duration, intptr_t allocation_in_bytes); 138 139 // Time spent in the mutator during the end of the last garbage collection 140 // to the beginning of the next garbage collection. 141 double duration_; 142 143 // Memory allocated in the new space during the end of the last garbage 144 // collection to the beginning of the next garbage collection. 145 intptr_t allocation_in_bytes_; 146 }; 147 148 class Event { 149 public: 150 enum Type { SCAVENGER = 0, MARK_COMPACTOR = 1, START = 2 }; 151 152 // Default constructor leaves the event uninitialized. 153 Event() {} 154 155 Event(Type type, const char* gc_reason, const char* collector_reason); 156 157 // Returns a string describing the event type. 158 const char* TypeName(bool short_name) const; 159 160 // Type of event 161 Type type; 162 163 const char* gc_reason; 164 const char* collector_reason; 165 166 // Timestamp set in the constructor. 167 double start_time; 168 169 // Timestamp set in the destructor. 170 double end_time; 171 172 // Size of objects in heap set in constructor. 173 intptr_t start_object_size; 174 175 // Size of objects in heap set in destructor. 176 intptr_t end_object_size; 177 178 // Size of memory allocated from OS set in constructor. 179 intptr_t start_memory_size; 180 181 // Size of memory allocated from OS set in destructor. 182 intptr_t end_memory_size; 183 184 // Total amount of space either wasted or contained in one of free lists 185 // before the current GC. 186 intptr_t start_holes_size; 187 188 // Total amount of space either wasted or contained in one of free lists 189 // after the current GC. 190 intptr_t end_holes_size; 191 192 // Size of new space objects in constructor. 193 intptr_t new_space_object_size; 194 195 // Number of incremental marking steps since creation of tracer. 196 // (value at start of event) 197 int cumulative_incremental_marking_steps; 198 199 // Incremental marking steps since 200 // - last event for SCAVENGER events 201 // - last MARK_COMPACTOR event for MARK_COMPACTOR events 202 int incremental_marking_steps; 203 204 // Bytes marked since creation of tracer (value at start of event). 205 intptr_t cumulative_incremental_marking_bytes; 206 207 // Bytes marked since 208 // - last event for SCAVENGER events 209 // - last MARK_COMPACTOR event for MARK_COMPACTOR events 210 intptr_t incremental_marking_bytes; 211 212 // Cumulative duration of incremental marking steps since creation of 213 // tracer. (value at start of event) 214 double cumulative_incremental_marking_duration; 215 216 // Duration of incremental marking steps since 217 // - last event for SCAVENGER events 218 // - last MARK_COMPACTOR event for MARK_COMPACTOR events 219 double incremental_marking_duration; 220 221 // Cumulative pure duration of incremental marking steps since creation of 222 // tracer. (value at start of event) 223 double cumulative_pure_incremental_marking_duration; 224 225 // Duration of pure incremental marking steps since 226 // - last event for SCAVENGER events 227 // - last MARK_COMPACTOR event for MARK_COMPACTOR events 228 double pure_incremental_marking_duration; 229 230 // Longest incremental marking step since start of marking. 231 // (value at start of event) 232 double longest_incremental_marking_step; 233 234 // Amounts of time spent in different scopes during GC. 235 double scopes[Scope::NUMBER_OF_SCOPES]; 236 }; 237 238 static const int kRingBufferMaxSize = 10; 239 240 typedef RingBuffer<Event, kRingBufferMaxSize> EventBuffer; 241 242 typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer; 243 244 explicit GCTracer(Heap* heap); 245 246 // Start collecting data. 247 void Start(GarbageCollector collector, const char* gc_reason, 248 const char* collector_reason); 249 250 // Stop collecting data and print results. 251 void Stop(); 252 253 // Log an allocation throughput event. 254 void AddNewSpaceAllocationTime(double duration, intptr_t allocation_in_bytes); 255 256 // Log an incremental marking step. 257 void AddIncrementalMarkingStep(double duration, intptr_t bytes); 258 259 // Log time spent in marking. 260 void AddMarkingTime(double duration) { 261 cumulative_marking_duration_ += duration; 262 } 263 264 // Time spent in marking. 265 double cumulative_marking_duration() const { 266 return cumulative_marking_duration_; 267 } 268 269 // Log time spent in sweeping on main thread. 270 void AddSweepingTime(double duration) { 271 cumulative_sweeping_duration_ += duration; 272 } 273 274 // Time spent in sweeping on main thread. 275 double cumulative_sweeping_duration() const { 276 return cumulative_sweeping_duration_; 277 } 278 279 // Compute the mean duration of the last scavenger events. Returns 0 if no 280 // events have been recorded. 281 double MeanScavengerDuration() const { 282 return MeanDuration(scavenger_events_); 283 } 284 285 // Compute the max duration of the last scavenger events. Returns 0 if no 286 // events have been recorded. 287 double MaxScavengerDuration() const { return MaxDuration(scavenger_events_); } 288 289 // Compute the mean duration of the last mark compactor events. Returns 0 if 290 // no events have been recorded. 291 double MeanMarkCompactorDuration() const { 292 return MeanDuration(mark_compactor_events_); 293 } 294 295 // Compute the max duration of the last mark compactor events. Return 0 if no 296 // events have been recorded. 297 double MaxMarkCompactorDuration() const { 298 return MaxDuration(mark_compactor_events_); 299 } 300 301 // Compute the mean step duration of the last incremental marking round. 302 // Returns 0 if no incremental marking round has been completed. 303 double MeanIncrementalMarkingDuration() const; 304 305 // Compute the max step duration of the last incremental marking round. 306 // Returns 0 if no incremental marking round has been completed. 307 double MaxIncrementalMarkingDuration() const; 308 309 // Compute the average incremental marking speed in bytes/millisecond. 310 // Returns 0 if no events have been recorded. 311 intptr_t IncrementalMarkingSpeedInBytesPerMillisecond() const; 312 313 // Compute the average scavenge speed in bytes/millisecond. 314 // Returns 0 if no events have been recorded. 315 intptr_t ScavengeSpeedInBytesPerMillisecond() const; 316 317 // Compute the max mark-sweep speed in bytes/millisecond. 318 // Returns 0 if no events have been recorded. 319 intptr_t MarkCompactSpeedInBytesPerMillisecond() const; 320 321 // Allocation throughput in the new space in bytes/millisecond. 322 // Returns 0 if no events have been recorded. 323 intptr_t NewSpaceAllocationThroughputInBytesPerMillisecond() const; 324 325 private: 326 // Print one detailed trace line in name=value format. 327 // TODO(ernstm): Move to Heap. 328 void PrintNVP() const; 329 330 // Print one trace line. 331 // TODO(ernstm): Move to Heap. 332 void Print() const; 333 334 // Compute the mean duration of the events in the given ring buffer. 335 double MeanDuration(const EventBuffer& events) const; 336 337 // Compute the max duration of the events in the given ring buffer. 338 double MaxDuration(const EventBuffer& events) const; 339 340 // Pointer to the heap that owns this tracer. 341 Heap* heap_; 342 343 // Current tracer event. Populated during Start/Stop cycle. Valid after Stop() 344 // has returned. 345 Event current_; 346 347 // Previous tracer event. 348 Event previous_; 349 350 // Previous MARK_COMPACTOR event. 351 Event previous_mark_compactor_event_; 352 353 // RingBuffers for SCAVENGER events. 354 EventBuffer scavenger_events_; 355 356 // RingBuffers for MARK_COMPACTOR events. 357 EventBuffer mark_compactor_events_; 358 359 // RingBuffer for allocation events. 360 AllocationEventBuffer allocation_events_; 361 362 // Cumulative number of incremental marking steps since creation of tracer. 363 int cumulative_incremental_marking_steps_; 364 365 // Cumulative size of incremental marking steps (in bytes) since creation of 366 // tracer. 367 intptr_t cumulative_incremental_marking_bytes_; 368 369 // Cumulative duration of incremental marking steps since creation of tracer. 370 double cumulative_incremental_marking_duration_; 371 372 // Cumulative duration of pure incremental marking steps since creation of 373 // tracer. 374 double cumulative_pure_incremental_marking_duration_; 375 376 // Longest incremental marking step since start of marking. 377 double longest_incremental_marking_step_; 378 379 // Total marking time. 380 // This timer is precise when run with --print-cumulative-gc-stat 381 double cumulative_marking_duration_; 382 383 // Total sweeping time on the main thread. 384 // This timer is precise when run with --print-cumulative-gc-stat 385 // TODO(hpayer): Account for sweeping time on sweeper threads. Add a 386 // different field for that. 387 // TODO(hpayer): This timer right now just holds the sweeping time 388 // of the initial atomic sweeping pause. Make sure that it accumulates 389 // all sweeping operations performed on the main thread. 390 double cumulative_sweeping_duration_; 391 392 // Holds the new space top pointer recorded at the end of the last garbage 393 // collection. 394 intptr_t new_space_top_after_gc_; 395 396 DISALLOW_COPY_AND_ASSIGN(GCTracer); 397 }; 398 } 399 } // namespace v8::internal 400 401 #endif // V8_HEAP_GC_TRACER_H_ 402