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 #include "src/heap/gc-tracer.h"
6
7 #include "src/counters.h"
8 #include "src/heap/heap-inl.h"
9 #include "src/isolate.h"
10
11 namespace v8 {
12 namespace internal {
13
CountTotalHolesSize(Heap * heap)14 static intptr_t CountTotalHolesSize(Heap* heap) {
15 intptr_t holes_size = 0;
16 OldSpaces spaces(heap);
17 for (OldSpace* space = spaces.next(); space != NULL; space = spaces.next()) {
18 holes_size += space->Waste() + space->Available();
19 }
20 return holes_size;
21 }
22
23
Scope(GCTracer * tracer,ScopeId scope)24 GCTracer::Scope::Scope(GCTracer* tracer, ScopeId scope)
25 : tracer_(tracer), scope_(scope) {
26 start_time_ = tracer_->heap_->MonotonicallyIncreasingTimeInMs();
27 }
28
29
~Scope()30 GCTracer::Scope::~Scope() {
31 DCHECK(scope_ < NUMBER_OF_SCOPES); // scope_ is unsigned.
32 tracer_->current_.scopes[scope_] +=
33 tracer_->heap_->MonotonicallyIncreasingTimeInMs() - start_time_;
34 }
35
36
AllocationEvent(double duration,size_t allocation_in_bytes)37 GCTracer::AllocationEvent::AllocationEvent(double duration,
38 size_t allocation_in_bytes) {
39 duration_ = duration;
40 allocation_in_bytes_ = allocation_in_bytes;
41 }
42
43
ContextDisposalEvent(double time)44 GCTracer::ContextDisposalEvent::ContextDisposalEvent(double time) {
45 time_ = time;
46 }
47
48
SurvivalEvent(double promotion_ratio)49 GCTracer::SurvivalEvent::SurvivalEvent(double promotion_ratio) {
50 promotion_ratio_ = promotion_ratio;
51 }
52
53
Event(Type type,const char * gc_reason,const char * collector_reason)54 GCTracer::Event::Event(Type type, const char* gc_reason,
55 const char* collector_reason)
56 : type(type),
57 gc_reason(gc_reason),
58 collector_reason(collector_reason),
59 start_time(0.0),
60 end_time(0.0),
61 reduce_memory(false),
62 start_object_size(0),
63 end_object_size(0),
64 start_memory_size(0),
65 end_memory_size(0),
66 start_holes_size(0),
67 end_holes_size(0),
68 cumulative_incremental_marking_steps(0),
69 incremental_marking_steps(0),
70 cumulative_incremental_marking_bytes(0),
71 incremental_marking_bytes(0),
72 cumulative_incremental_marking_duration(0.0),
73 incremental_marking_duration(0.0),
74 cumulative_pure_incremental_marking_duration(0.0),
75 pure_incremental_marking_duration(0.0),
76 longest_incremental_marking_step(0.0) {
77 for (int i = 0; i < Scope::NUMBER_OF_SCOPES; i++) {
78 scopes[i] = 0;
79 }
80 }
81
82
TypeName(bool short_name) const83 const char* GCTracer::Event::TypeName(bool short_name) const {
84 switch (type) {
85 case SCAVENGER:
86 if (short_name) {
87 return "s";
88 } else {
89 return "Scavenge";
90 }
91 case MARK_COMPACTOR:
92 case INCREMENTAL_MARK_COMPACTOR:
93 if (short_name) {
94 return "ms";
95 } else {
96 return "Mark-sweep";
97 }
98 case START:
99 if (short_name) {
100 return "st";
101 } else {
102 return "Start";
103 }
104 }
105 return "Unknown Event Type";
106 }
107
108
GCTracer(Heap * heap)109 GCTracer::GCTracer(Heap* heap)
110 : heap_(heap),
111 cumulative_incremental_marking_steps_(0),
112 cumulative_incremental_marking_bytes_(0),
113 cumulative_incremental_marking_duration_(0.0),
114 cumulative_pure_incremental_marking_duration_(0.0),
115 longest_incremental_marking_step_(0.0),
116 cumulative_incremental_marking_finalization_steps_(0),
117 cumulative_incremental_marking_finalization_duration_(0.0),
118 longest_incremental_marking_finalization_step_(0.0),
119 cumulative_marking_duration_(0.0),
120 cumulative_sweeping_duration_(0.0),
121 allocation_time_ms_(0.0),
122 new_space_allocation_counter_bytes_(0),
123 old_generation_allocation_counter_bytes_(0),
124 allocation_duration_since_gc_(0.0),
125 new_space_allocation_in_bytes_since_gc_(0),
126 old_generation_allocation_in_bytes_since_gc_(0),
127 combined_mark_compact_speed_cache_(0.0),
128 start_counter_(0) {
129 current_ = Event(Event::START, NULL, NULL);
130 current_.end_time = heap_->MonotonicallyIncreasingTimeInMs();
131 previous_ = previous_incremental_mark_compactor_event_ = current_;
132 }
133
134
Start(GarbageCollector collector,const char * gc_reason,const char * collector_reason)135 void GCTracer::Start(GarbageCollector collector, const char* gc_reason,
136 const char* collector_reason) {
137 start_counter_++;
138 if (start_counter_ != 1) return;
139
140 previous_ = current_;
141 double start_time = heap_->MonotonicallyIncreasingTimeInMs();
142 SampleAllocation(start_time, heap_->NewSpaceAllocationCounter(),
143 heap_->OldGenerationAllocationCounter());
144 if (current_.type == Event::INCREMENTAL_MARK_COMPACTOR)
145 previous_incremental_mark_compactor_event_ = current_;
146
147 if (collector == SCAVENGER) {
148 current_ = Event(Event::SCAVENGER, gc_reason, collector_reason);
149 } else if (collector == MARK_COMPACTOR) {
150 if (heap_->incremental_marking()->WasActivated()) {
151 current_ =
152 Event(Event::INCREMENTAL_MARK_COMPACTOR, gc_reason, collector_reason);
153 } else {
154 current_ = Event(Event::MARK_COMPACTOR, gc_reason, collector_reason);
155 }
156 }
157
158 current_.reduce_memory = heap_->ShouldReduceMemory();
159 current_.start_time = start_time;
160 current_.start_object_size = heap_->SizeOfObjects();
161 current_.start_memory_size = heap_->isolate()->memory_allocator()->Size();
162 current_.start_holes_size = CountTotalHolesSize(heap_);
163 current_.new_space_object_size =
164 heap_->new_space()->top() - heap_->new_space()->bottom();
165
166 current_.cumulative_incremental_marking_steps =
167 cumulative_incremental_marking_steps_;
168 current_.cumulative_incremental_marking_bytes =
169 cumulative_incremental_marking_bytes_;
170 current_.cumulative_incremental_marking_duration =
171 cumulative_incremental_marking_duration_;
172 current_.cumulative_pure_incremental_marking_duration =
173 cumulative_pure_incremental_marking_duration_;
174 current_.longest_incremental_marking_step = longest_incremental_marking_step_;
175
176 for (int i = 0; i < Scope::NUMBER_OF_SCOPES; i++) {
177 current_.scopes[i] = 0;
178 }
179 int committed_memory = static_cast<int>(heap_->CommittedMemory() / KB);
180 int used_memory = static_cast<int>(current_.start_object_size / KB);
181 heap_->isolate()->counters()->aggregated_memory_heap_committed()->AddSample(
182 start_time, committed_memory);
183 heap_->isolate()->counters()->aggregated_memory_heap_used()->AddSample(
184 start_time, used_memory);
185 }
186
187
Stop(GarbageCollector collector)188 void GCTracer::Stop(GarbageCollector collector) {
189 start_counter_--;
190 if (start_counter_ != 0) {
191 Output("[Finished reentrant %s during %s.]\n",
192 collector == SCAVENGER ? "Scavenge" : "Mark-sweep",
193 current_.TypeName(false));
194 return;
195 }
196
197 DCHECK(start_counter_ >= 0);
198 DCHECK((collector == SCAVENGER && current_.type == Event::SCAVENGER) ||
199 (collector == MARK_COMPACTOR &&
200 (current_.type == Event::MARK_COMPACTOR ||
201 current_.type == Event::INCREMENTAL_MARK_COMPACTOR)));
202
203 current_.end_time = heap_->MonotonicallyIncreasingTimeInMs();
204 current_.end_object_size = heap_->SizeOfObjects();
205 current_.end_memory_size = heap_->isolate()->memory_allocator()->Size();
206 current_.end_holes_size = CountTotalHolesSize(heap_);
207 current_.survived_new_space_object_size = heap_->SurvivedNewSpaceObjectSize();
208
209 AddAllocation(current_.end_time);
210
211 int committed_memory = static_cast<int>(heap_->CommittedMemory() / KB);
212 int used_memory = static_cast<int>(current_.end_object_size / KB);
213 heap_->isolate()->counters()->aggregated_memory_heap_committed()->AddSample(
214 current_.end_time, committed_memory);
215 heap_->isolate()->counters()->aggregated_memory_heap_used()->AddSample(
216 current_.end_time, used_memory);
217
218 if (current_.type == Event::SCAVENGER) {
219 current_.incremental_marking_steps =
220 current_.cumulative_incremental_marking_steps -
221 previous_.cumulative_incremental_marking_steps;
222 current_.incremental_marking_bytes =
223 current_.cumulative_incremental_marking_bytes -
224 previous_.cumulative_incremental_marking_bytes;
225 current_.incremental_marking_duration =
226 current_.cumulative_incremental_marking_duration -
227 previous_.cumulative_incremental_marking_duration;
228 current_.pure_incremental_marking_duration =
229 current_.cumulative_pure_incremental_marking_duration -
230 previous_.cumulative_pure_incremental_marking_duration;
231 scavenger_events_.push_front(current_);
232 } else if (current_.type == Event::INCREMENTAL_MARK_COMPACTOR) {
233 current_.incremental_marking_steps =
234 current_.cumulative_incremental_marking_steps -
235 previous_incremental_mark_compactor_event_
236 .cumulative_incremental_marking_steps;
237 current_.incremental_marking_bytes =
238 current_.cumulative_incremental_marking_bytes -
239 previous_incremental_mark_compactor_event_
240 .cumulative_incremental_marking_bytes;
241 current_.incremental_marking_duration =
242 current_.cumulative_incremental_marking_duration -
243 previous_incremental_mark_compactor_event_
244 .cumulative_incremental_marking_duration;
245 current_.pure_incremental_marking_duration =
246 current_.cumulative_pure_incremental_marking_duration -
247 previous_incremental_mark_compactor_event_
248 .cumulative_pure_incremental_marking_duration;
249 longest_incremental_marking_step_ = 0.0;
250 incremental_mark_compactor_events_.push_front(current_);
251 combined_mark_compact_speed_cache_ = 0.0;
252 } else {
253 DCHECK(current_.incremental_marking_bytes == 0);
254 DCHECK(current_.incremental_marking_duration == 0);
255 DCHECK(current_.pure_incremental_marking_duration == 0);
256 longest_incremental_marking_step_ = 0.0;
257 mark_compactor_events_.push_front(current_);
258 combined_mark_compact_speed_cache_ = 0.0;
259 }
260
261 // TODO(ernstm): move the code below out of GCTracer.
262
263 double duration = current_.end_time - current_.start_time;
264 double spent_in_mutator = Max(current_.start_time - previous_.end_time, 0.0);
265
266 heap_->UpdateCumulativeGCStatistics(duration, spent_in_mutator,
267 current_.scopes[Scope::MC_MARK]);
268
269 if (current_.type == Event::SCAVENGER && FLAG_trace_gc_ignore_scavenger)
270 return;
271
272 if (FLAG_trace_gc_nvp)
273 PrintNVP();
274 else
275 Print();
276
277 if (FLAG_trace_gc) {
278 heap_->PrintShortHeapStatistics();
279 }
280
281 longest_incremental_marking_finalization_step_ = 0.0;
282 cumulative_incremental_marking_finalization_steps_ = 0;
283 cumulative_incremental_marking_finalization_duration_ = 0.0;
284 }
285
286
SampleAllocation(double current_ms,size_t new_space_counter_bytes,size_t old_generation_counter_bytes)287 void GCTracer::SampleAllocation(double current_ms,
288 size_t new_space_counter_bytes,
289 size_t old_generation_counter_bytes) {
290 if (allocation_time_ms_ == 0) {
291 // It is the first sample.
292 allocation_time_ms_ = current_ms;
293 new_space_allocation_counter_bytes_ = new_space_counter_bytes;
294 old_generation_allocation_counter_bytes_ = old_generation_counter_bytes;
295 return;
296 }
297 // This assumes that counters are unsigned integers so that the subtraction
298 // below works even if the new counter is less then the old counter.
299 size_t new_space_allocated_bytes =
300 new_space_counter_bytes - new_space_allocation_counter_bytes_;
301 size_t old_generation_allocated_bytes =
302 old_generation_counter_bytes - old_generation_allocation_counter_bytes_;
303 double duration = current_ms - allocation_time_ms_;
304 allocation_time_ms_ = current_ms;
305 new_space_allocation_counter_bytes_ = new_space_counter_bytes;
306 old_generation_allocation_counter_bytes_ = old_generation_counter_bytes;
307 allocation_duration_since_gc_ += duration;
308 new_space_allocation_in_bytes_since_gc_ += new_space_allocated_bytes;
309 old_generation_allocation_in_bytes_since_gc_ +=
310 old_generation_allocated_bytes;
311 }
312
313
AddAllocation(double current_ms)314 void GCTracer::AddAllocation(double current_ms) {
315 allocation_time_ms_ = current_ms;
316 new_space_allocation_events_.push_front(AllocationEvent(
317 allocation_duration_since_gc_, new_space_allocation_in_bytes_since_gc_));
318 old_generation_allocation_events_.push_front(
319 AllocationEvent(allocation_duration_since_gc_,
320 old_generation_allocation_in_bytes_since_gc_));
321 allocation_duration_since_gc_ = 0;
322 new_space_allocation_in_bytes_since_gc_ = 0;
323 old_generation_allocation_in_bytes_since_gc_ = 0;
324 }
325
326
AddContextDisposalTime(double time)327 void GCTracer::AddContextDisposalTime(double time) {
328 context_disposal_events_.push_front(ContextDisposalEvent(time));
329 }
330
331
AddCompactionEvent(double duration,intptr_t live_bytes_compacted)332 void GCTracer::AddCompactionEvent(double duration,
333 intptr_t live_bytes_compacted) {
334 compaction_events_.push_front(
335 CompactionEvent(duration, live_bytes_compacted));
336 }
337
338
AddSurvivalRatio(double promotion_ratio)339 void GCTracer::AddSurvivalRatio(double promotion_ratio) {
340 survival_events_.push_front(SurvivalEvent(promotion_ratio));
341 }
342
343
AddIncrementalMarkingStep(double duration,intptr_t bytes)344 void GCTracer::AddIncrementalMarkingStep(double duration, intptr_t bytes) {
345 cumulative_incremental_marking_steps_++;
346 cumulative_incremental_marking_bytes_ += bytes;
347 cumulative_incremental_marking_duration_ += duration;
348 longest_incremental_marking_step_ =
349 Max(longest_incremental_marking_step_, duration);
350 cumulative_marking_duration_ += duration;
351 if (bytes > 0) {
352 cumulative_pure_incremental_marking_duration_ += duration;
353 }
354 }
355
356
AddIncrementalMarkingFinalizationStep(double duration)357 void GCTracer::AddIncrementalMarkingFinalizationStep(double duration) {
358 cumulative_incremental_marking_finalization_steps_++;
359 cumulative_incremental_marking_finalization_duration_ += duration;
360 longest_incremental_marking_finalization_step_ =
361 Max(longest_incremental_marking_finalization_step_, duration);
362 }
363
364
Output(const char * format,...) const365 void GCTracer::Output(const char* format, ...) const {
366 if (FLAG_trace_gc) {
367 va_list arguments;
368 va_start(arguments, format);
369 base::OS::VPrint(format, arguments);
370 va_end(arguments);
371 }
372
373 const int kBufferSize = 256;
374 char raw_buffer[kBufferSize];
375 Vector<char> buffer(raw_buffer, kBufferSize);
376 va_list arguments2;
377 va_start(arguments2, format);
378 VSNPrintF(buffer, format, arguments2);
379 va_end(arguments2);
380
381 heap_->AddToRingBuffer(buffer.start());
382 }
383
384
Print() const385 void GCTracer::Print() const {
386 if (FLAG_trace_gc) {
387 PrintIsolate(heap_->isolate(), "");
388 }
389 Output("%8.0f ms: ", heap_->isolate()->time_millis_since_init());
390
391 Output("%s %.1f (%.1f) -> %.1f (%.1f) MB, ", current_.TypeName(false),
392 static_cast<double>(current_.start_object_size) / MB,
393 static_cast<double>(current_.start_memory_size) / MB,
394 static_cast<double>(current_.end_object_size) / MB,
395 static_cast<double>(current_.end_memory_size) / MB);
396
397 int external_time = static_cast<int>(current_.scopes[Scope::EXTERNAL]);
398 double duration = current_.end_time - current_.start_time;
399 Output("%.1f / %d ms", duration, external_time);
400
401 if (current_.type == Event::SCAVENGER) {
402 if (current_.incremental_marking_steps > 0) {
403 Output(" (+ %.1f ms in %d steps since last GC)",
404 current_.incremental_marking_duration,
405 current_.incremental_marking_steps);
406 }
407 } else {
408 if (current_.incremental_marking_steps > 0) {
409 Output(
410 " (+ %.1f ms in %d steps since start of marking, "
411 "biggest step %.1f ms)",
412 current_.incremental_marking_duration,
413 current_.incremental_marking_steps,
414 current_.longest_incremental_marking_step);
415 }
416 }
417
418 if (current_.gc_reason != NULL) {
419 Output(" [%s]", current_.gc_reason);
420 }
421
422 if (current_.collector_reason != NULL) {
423 Output(" [%s]", current_.collector_reason);
424 }
425
426 Output(".\n");
427 }
428
429
PrintNVP() const430 void GCTracer::PrintNVP() const {
431 double duration = current_.end_time - current_.start_time;
432 double spent_in_mutator = current_.start_time - previous_.end_time;
433 intptr_t allocated_since_last_gc =
434 current_.start_object_size - previous_.end_object_size;
435
436 switch (current_.type) {
437 case Event::SCAVENGER:
438 PrintIsolate(heap_->isolate(),
439 "%8.0f ms: "
440 "pause=%.1f "
441 "mutator=%.1f "
442 "gc=%s "
443 "reduce_memory=%d "
444 "scavenge=%.2f "
445 "old_new=%.2f "
446 "weak=%.2f "
447 "roots=%.2f "
448 "code=%.2f "
449 "semispace=%.2f "
450 "object_groups=%.2f "
451 "steps_count=%d "
452 "steps_took=%.1f "
453 "scavenge_throughput=%" V8_PTR_PREFIX
454 "d "
455 "total_size_before=%" V8_PTR_PREFIX
456 "d "
457 "total_size_after=%" V8_PTR_PREFIX
458 "d "
459 "holes_size_before=%" V8_PTR_PREFIX
460 "d "
461 "holes_size_after=%" V8_PTR_PREFIX
462 "d "
463 "allocated=%" V8_PTR_PREFIX
464 "d "
465 "promoted=%" V8_PTR_PREFIX
466 "d "
467 "semi_space_copied=%" V8_PTR_PREFIX
468 "d "
469 "nodes_died_in_new=%d "
470 "nodes_copied_in_new=%d "
471 "nodes_promoted=%d "
472 "promotion_ratio=%.1f%% "
473 "average_survival_ratio=%.1f%% "
474 "promotion_rate=%.1f%% "
475 "semi_space_copy_rate=%.1f%% "
476 "new_space_allocation_throughput=%" V8_PTR_PREFIX
477 "d "
478 "context_disposal_rate=%.1f\n",
479 heap_->isolate()->time_millis_since_init(), duration,
480 spent_in_mutator, current_.TypeName(true),
481 current_.reduce_memory,
482 current_.scopes[Scope::SCAVENGER_SCAVENGE],
483 current_.scopes[Scope::SCAVENGER_OLD_TO_NEW_POINTERS],
484 current_.scopes[Scope::SCAVENGER_WEAK],
485 current_.scopes[Scope::SCAVENGER_ROOTS],
486 current_.scopes[Scope::SCAVENGER_CODE_FLUSH_CANDIDATES],
487 current_.scopes[Scope::SCAVENGER_SEMISPACE],
488 current_.scopes[Scope::SCAVENGER_OBJECT_GROUPS],
489 current_.incremental_marking_steps,
490 current_.incremental_marking_duration,
491 ScavengeSpeedInBytesPerMillisecond(),
492 current_.start_object_size, current_.end_object_size,
493 current_.start_holes_size, current_.end_holes_size,
494 allocated_since_last_gc, heap_->promoted_objects_size(),
495 heap_->semi_space_copied_object_size(),
496 heap_->nodes_died_in_new_space_,
497 heap_->nodes_copied_in_new_space_, heap_->nodes_promoted_,
498 heap_->promotion_ratio_, AverageSurvivalRatio(),
499 heap_->promotion_rate_, heap_->semi_space_copied_rate_,
500 NewSpaceAllocationThroughputInBytesPerMillisecond(),
501 ContextDisposalRateInMilliseconds());
502 break;
503 case Event::MARK_COMPACTOR:
504 case Event::INCREMENTAL_MARK_COMPACTOR:
505 PrintIsolate(
506 heap_->isolate(),
507 "%8.0f ms: "
508 "pause=%.1f "
509 "mutator=%.1f "
510 "gc=%s "
511 "reduce_memory=%d "
512 "external=%.1f "
513 "clear=%1.f "
514 "clear.code_flush=%.1f "
515 "clear.dependent_code=%.1f "
516 "clear.global_handles=%.1f "
517 "clear.maps=%.1f "
518 "clear.slots_buffer=%.1f "
519 "clear.store_buffer=%.1f "
520 "clear.string_table=%.1f "
521 "clear.weak_cells=%.1f "
522 "clear.weak_collections=%.1f "
523 "clear.weak_lists=%.1f "
524 "evacuate=%.1f "
525 "evacuate.candidates=%.1f "
526 "evacuate.clean_up=%.1f "
527 "evacuate.new_space=%.1f "
528 "evacuate.update_pointers=%.1f "
529 "evacuate.update_pointers.between_evacuated=%.1f "
530 "evacuate.update_pointers.to_evacuated=%.1f "
531 "evacuate.update_pointers.to_new=%.1f "
532 "evacuate.update_pointers.weak=%.1f "
533 "finish=%.1f "
534 "mark=%.1f "
535 "mark.finish_incremental=%.1f "
536 "mark.prepare_code_flush=%.1f "
537 "mark.roots=%.1f "
538 "mark.weak_closure=%.1f "
539 "sweep=%.1f "
540 "sweep.code=%.1f "
541 "sweep.map=%.1f "
542 "sweep.old=%.1f "
543 "incremental_finalize=%.1f "
544 "steps_count=%d "
545 "steps_took=%.1f "
546 "longest_step=%.1f "
547 "finalization_steps_count=%d "
548 "finalization_steps_took=%.1f "
549 "finalization_longest_step=%.1f "
550 "incremental_marking_throughput=%" V8_PTR_PREFIX
551 "d "
552 "total_size_before=%" V8_PTR_PREFIX
553 "d "
554 "total_size_after=%" V8_PTR_PREFIX
555 "d "
556 "holes_size_before=%" V8_PTR_PREFIX
557 "d "
558 "holes_size_after=%" V8_PTR_PREFIX
559 "d "
560 "allocated=%" V8_PTR_PREFIX
561 "d "
562 "promoted=%" V8_PTR_PREFIX
563 "d "
564 "semi_space_copied=%" V8_PTR_PREFIX
565 "d "
566 "nodes_died_in_new=%d "
567 "nodes_copied_in_new=%d "
568 "nodes_promoted=%d "
569 "promotion_ratio=%.1f%% "
570 "average_survival_ratio=%.1f%% "
571 "promotion_rate=%.1f%% "
572 "semi_space_copy_rate=%.1f%% "
573 "new_space_allocation_throughput=%" V8_PTR_PREFIX
574 "d "
575 "context_disposal_rate=%.1f "
576 "compaction_speed=%" V8_PTR_PREFIX "d\n",
577 heap_->isolate()->time_millis_since_init(), duration,
578 spent_in_mutator, current_.TypeName(true), current_.reduce_memory,
579 current_.scopes[Scope::EXTERNAL], current_.scopes[Scope::MC_CLEAR],
580 current_.scopes[Scope::MC_CLEAR_CODE_FLUSH],
581 current_.scopes[Scope::MC_CLEAR_DEPENDENT_CODE],
582 current_.scopes[Scope::MC_CLEAR_GLOBAL_HANDLES],
583 current_.scopes[Scope::MC_CLEAR_MAPS],
584 current_.scopes[Scope::MC_CLEAR_SLOTS_BUFFER],
585 current_.scopes[Scope::MC_CLEAR_STORE_BUFFER],
586 current_.scopes[Scope::MC_CLEAR_STRING_TABLE],
587 current_.scopes[Scope::MC_CLEAR_WEAK_CELLS],
588 current_.scopes[Scope::MC_CLEAR_WEAK_COLLECTIONS],
589 current_.scopes[Scope::MC_CLEAR_WEAK_LISTS],
590 current_.scopes[Scope::MC_EVACUATE],
591 current_.scopes[Scope::MC_EVACUATE_CANDIDATES],
592 current_.scopes[Scope::MC_EVACUATE_CLEAN_UP],
593 current_.scopes[Scope::MC_EVACUATE_NEW_SPACE],
594 current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS],
595 current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_BETWEEN_EVACUATED],
596 current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_TO_EVACUATED],
597 current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_TO_NEW],
598 current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_WEAK],
599 current_.scopes[Scope::MC_FINISH], current_.scopes[Scope::MC_MARK],
600 current_.scopes[Scope::MC_MARK_FINISH_INCREMENTAL],
601 current_.scopes[Scope::MC_MARK_PREPARE_CODE_FLUSH],
602 current_.scopes[Scope::MC_MARK_ROOTS],
603 current_.scopes[Scope::MC_MARK_WEAK_CLOSURE],
604 current_.scopes[Scope::MC_SWEEP],
605 current_.scopes[Scope::MC_SWEEP_CODE],
606 current_.scopes[Scope::MC_SWEEP_MAP],
607 current_.scopes[Scope::MC_SWEEP_OLD],
608 current_.scopes[Scope::MC_INCREMENTAL_FINALIZE],
609 current_.incremental_marking_steps,
610 current_.incremental_marking_duration,
611 current_.longest_incremental_marking_step,
612 cumulative_incremental_marking_finalization_steps_,
613 cumulative_incremental_marking_finalization_duration_,
614 longest_incremental_marking_finalization_step_,
615 IncrementalMarkingSpeedInBytesPerMillisecond(),
616 current_.start_object_size, current_.end_object_size,
617 current_.start_holes_size, current_.end_holes_size,
618 allocated_since_last_gc, heap_->promoted_objects_size(),
619 heap_->semi_space_copied_object_size(),
620 heap_->nodes_died_in_new_space_, heap_->nodes_copied_in_new_space_,
621 heap_->nodes_promoted_, heap_->promotion_ratio_,
622 AverageSurvivalRatio(), heap_->promotion_rate_,
623 heap_->semi_space_copied_rate_,
624 NewSpaceAllocationThroughputInBytesPerMillisecond(),
625 ContextDisposalRateInMilliseconds(),
626 CompactionSpeedInBytesPerMillisecond());
627 break;
628 case Event::START:
629 break;
630 default:
631 UNREACHABLE();
632 }
633 }
634
635
MeanDuration(const EventBuffer & events) const636 double GCTracer::MeanDuration(const EventBuffer& events) const {
637 if (events.empty()) return 0.0;
638
639 double mean = 0.0;
640 EventBuffer::const_iterator iter = events.begin();
641 while (iter != events.end()) {
642 mean += iter->end_time - iter->start_time;
643 ++iter;
644 }
645
646 return mean / events.size();
647 }
648
649
MaxDuration(const EventBuffer & events) const650 double GCTracer::MaxDuration(const EventBuffer& events) const {
651 if (events.empty()) return 0.0;
652
653 double maximum = 0.0f;
654 EventBuffer::const_iterator iter = events.begin();
655 while (iter != events.end()) {
656 maximum = Max(iter->end_time - iter->start_time, maximum);
657 ++iter;
658 }
659
660 return maximum;
661 }
662
663
MeanIncrementalMarkingDuration() const664 double GCTracer::MeanIncrementalMarkingDuration() const {
665 if (cumulative_incremental_marking_steps_ == 0) return 0.0;
666
667 // We haven't completed an entire round of incremental marking, yet.
668 // Use data from GCTracer instead of data from event buffers.
669 if (incremental_mark_compactor_events_.empty()) {
670 return cumulative_incremental_marking_duration_ /
671 cumulative_incremental_marking_steps_;
672 }
673
674 int steps = 0;
675 double durations = 0.0;
676 EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin();
677 while (iter != incremental_mark_compactor_events_.end()) {
678 steps += iter->incremental_marking_steps;
679 durations += iter->incremental_marking_duration;
680 ++iter;
681 }
682
683 if (steps == 0) return 0.0;
684
685 return durations / steps;
686 }
687
688
MaxIncrementalMarkingDuration() const689 double GCTracer::MaxIncrementalMarkingDuration() const {
690 // We haven't completed an entire round of incremental marking, yet.
691 // Use data from GCTracer instead of data from event buffers.
692 if (incremental_mark_compactor_events_.empty())
693 return longest_incremental_marking_step_;
694
695 double max_duration = 0.0;
696 EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin();
697 while (iter != incremental_mark_compactor_events_.end())
698 max_duration = Max(iter->longest_incremental_marking_step, max_duration);
699
700 return max_duration;
701 }
702
703
IncrementalMarkingSpeedInBytesPerMillisecond() const704 intptr_t GCTracer::IncrementalMarkingSpeedInBytesPerMillisecond() const {
705 if (cumulative_incremental_marking_duration_ == 0.0) return 0;
706
707 // We haven't completed an entire round of incremental marking, yet.
708 // Use data from GCTracer instead of data from event buffers.
709 if (incremental_mark_compactor_events_.empty()) {
710 return static_cast<intptr_t>(cumulative_incremental_marking_bytes_ /
711 cumulative_pure_incremental_marking_duration_);
712 }
713
714 intptr_t bytes = 0;
715 double durations = 0.0;
716 EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin();
717 while (iter != incremental_mark_compactor_events_.end()) {
718 bytes += iter->incremental_marking_bytes;
719 durations += iter->pure_incremental_marking_duration;
720 ++iter;
721 }
722
723 if (durations == 0.0) return 0;
724 // Make sure the result is at least 1.
725 return Max<size_t>(static_cast<size_t>(bytes / durations + 0.5), 1);
726 }
727
728
ScavengeSpeedInBytesPerMillisecond(ScavengeSpeedMode mode) const729 intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond(
730 ScavengeSpeedMode mode) const {
731 intptr_t bytes = 0;
732 double durations = 0.0;
733 EventBuffer::const_iterator iter = scavenger_events_.begin();
734 while (iter != scavenger_events_.end()) {
735 bytes += mode == kForAllObjects ? iter->new_space_object_size
736 : iter->survived_new_space_object_size;
737 durations += iter->end_time - iter->start_time;
738 ++iter;
739 }
740
741 if (durations == 0.0) return 0;
742 // Make sure the result is at least 1.
743 return Max<size_t>(static_cast<size_t>(bytes / durations + 0.5), 1);
744 }
745
746
CompactionSpeedInBytesPerMillisecond() const747 intptr_t GCTracer::CompactionSpeedInBytesPerMillisecond() const {
748 if (compaction_events_.size() == 0) return 0;
749 intptr_t bytes = 0;
750 double durations = 0.0;
751 CompactionEventBuffer::const_iterator iter = compaction_events_.begin();
752 while (iter != compaction_events_.end()) {
753 bytes += iter->live_bytes_compacted;
754 durations += iter->duration;
755 ++iter;
756 }
757
758 if (durations == 0.0) return 0;
759 // Make sure the result is at least 1.
760 return Max<intptr_t>(static_cast<intptr_t>(bytes / durations + 0.5), 1);
761 }
762
763
MarkCompactSpeedInBytesPerMillisecond() const764 intptr_t GCTracer::MarkCompactSpeedInBytesPerMillisecond() const {
765 intptr_t bytes = 0;
766 double durations = 0.0;
767 EventBuffer::const_iterator iter = mark_compactor_events_.begin();
768 while (iter != mark_compactor_events_.end()) {
769 bytes += iter->start_object_size;
770 durations += iter->end_time - iter->start_time;
771 ++iter;
772 }
773
774 if (durations == 0.0) return 0;
775 // Make sure the result is at least 1.
776 return Max<size_t>(static_cast<size_t>(bytes / durations + 0.5), 1);
777 }
778
779
FinalIncrementalMarkCompactSpeedInBytesPerMillisecond() const780 intptr_t GCTracer::FinalIncrementalMarkCompactSpeedInBytesPerMillisecond()
781 const {
782 intptr_t bytes = 0;
783 double durations = 0.0;
784 EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin();
785 while (iter != incremental_mark_compactor_events_.end()) {
786 bytes += iter->start_object_size;
787 durations += iter->end_time - iter->start_time;
788 ++iter;
789 }
790
791 if (durations == 0.0) return 0;
792 // Make sure the result is at least 1.
793 return Max<size_t>(static_cast<size_t>(bytes / durations + 0.5), 1);
794 }
795
796
CombinedMarkCompactSpeedInBytesPerMillisecond()797 double GCTracer::CombinedMarkCompactSpeedInBytesPerMillisecond() {
798 if (combined_mark_compact_speed_cache_ > 0)
799 return combined_mark_compact_speed_cache_;
800 const double kMinimumMarkingSpeed = 0.5;
801 double speed1 =
802 static_cast<double>(IncrementalMarkingSpeedInBytesPerMillisecond());
803 double speed2 = static_cast<double>(
804 FinalIncrementalMarkCompactSpeedInBytesPerMillisecond());
805 if (speed1 < kMinimumMarkingSpeed || speed2 < kMinimumMarkingSpeed) {
806 // No data for the incremental marking speed.
807 // Return the non-incremental mark-compact speed.
808 combined_mark_compact_speed_cache_ =
809 static_cast<double>(MarkCompactSpeedInBytesPerMillisecond());
810 } else {
811 // Combine the speed of incremental step and the speed of the final step.
812 // 1 / (1 / speed1 + 1 / speed2) = speed1 * speed2 / (speed1 + speed2).
813 combined_mark_compact_speed_cache_ = speed1 * speed2 / (speed1 + speed2);
814 }
815 return combined_mark_compact_speed_cache_;
816 }
817
818
NewSpaceAllocationThroughputInBytesPerMillisecond(double time_ms) const819 size_t GCTracer::NewSpaceAllocationThroughputInBytesPerMillisecond(
820 double time_ms) const {
821 size_t bytes = new_space_allocation_in_bytes_since_gc_;
822 double durations = allocation_duration_since_gc_;
823 AllocationEventBuffer::const_iterator iter =
824 new_space_allocation_events_.begin();
825 const size_t max_bytes = static_cast<size_t>(-1);
826 while (iter != new_space_allocation_events_.end() &&
827 bytes < max_bytes - bytes && (time_ms == 0 || durations < time_ms)) {
828 bytes += iter->allocation_in_bytes_;
829 durations += iter->duration_;
830 ++iter;
831 }
832
833 if (durations == 0.0) return 0;
834 // Make sure the result is at least 1.
835 return Max<size_t>(static_cast<size_t>(bytes / durations + 0.5), 1);
836 }
837
838
OldGenerationAllocationThroughputInBytesPerMillisecond(double time_ms) const839 size_t GCTracer::OldGenerationAllocationThroughputInBytesPerMillisecond(
840 double time_ms) const {
841 size_t bytes = old_generation_allocation_in_bytes_since_gc_;
842 double durations = allocation_duration_since_gc_;
843 AllocationEventBuffer::const_iterator iter =
844 old_generation_allocation_events_.begin();
845 const size_t max_bytes = static_cast<size_t>(-1);
846 while (iter != old_generation_allocation_events_.end() &&
847 bytes < max_bytes - bytes && (time_ms == 0 || durations < time_ms)) {
848 bytes += iter->allocation_in_bytes_;
849 durations += iter->duration_;
850 ++iter;
851 }
852
853 if (durations == 0.0) return 0;
854 // Make sure the result is at least 1.
855 return Max<size_t>(static_cast<size_t>(bytes / durations + 0.5), 1);
856 }
857
858
AllocationThroughputInBytesPerMillisecond(double time_ms) const859 size_t GCTracer::AllocationThroughputInBytesPerMillisecond(
860 double time_ms) const {
861 return NewSpaceAllocationThroughputInBytesPerMillisecond(time_ms) +
862 OldGenerationAllocationThroughputInBytesPerMillisecond(time_ms);
863 }
864
865
CurrentAllocationThroughputInBytesPerMillisecond() const866 size_t GCTracer::CurrentAllocationThroughputInBytesPerMillisecond() const {
867 return AllocationThroughputInBytesPerMillisecond(kThroughputTimeFrameMs);
868 }
869
870
CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const871 size_t GCTracer::CurrentOldGenerationAllocationThroughputInBytesPerMillisecond()
872 const {
873 return OldGenerationAllocationThroughputInBytesPerMillisecond(
874 kThroughputTimeFrameMs);
875 }
876
877
ContextDisposalRateInMilliseconds() const878 double GCTracer::ContextDisposalRateInMilliseconds() const {
879 if (context_disposal_events_.size() < kRingBufferMaxSize) return 0.0;
880
881 double begin = heap_->MonotonicallyIncreasingTimeInMs();
882 double end = 0.0;
883 ContextDisposalEventBuffer::const_iterator iter =
884 context_disposal_events_.begin();
885 while (iter != context_disposal_events_.end()) {
886 end = iter->time_;
887 ++iter;
888 }
889
890 return (begin - end) / context_disposal_events_.size();
891 }
892
893
AverageSurvivalRatio() const894 double GCTracer::AverageSurvivalRatio() const {
895 if (survival_events_.size() == 0) return 0.0;
896
897 double sum_of_rates = 0.0;
898 SurvivalEventBuffer::const_iterator iter = survival_events_.begin();
899 while (iter != survival_events_.end()) {
900 sum_of_rates += iter->promotion_ratio_;
901 ++iter;
902 }
903
904 return sum_of_rates / static_cast<double>(survival_events_.size());
905 }
906
907
SurvivalEventsRecorded() const908 bool GCTracer::SurvivalEventsRecorded() const {
909 return survival_events_.size() > 0;
910 }
911
912
ResetSurvivalEvents()913 void GCTracer::ResetSurvivalEvents() { survival_events_.reset(); }
914 } // namespace internal
915 } // namespace v8
916