Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame^] | 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_ |