// Copyright 2014 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/heap/gc-tracer.h" #include "src/counters.h" #include "src/heap/heap-inl.h" #include "src/isolate.h" namespace v8 { namespace internal { static intptr_t CountTotalHolesSize(Heap* heap) { intptr_t holes_size = 0; OldSpaces spaces(heap); for (OldSpace* space = spaces.next(); space != NULL; space = spaces.next()) { holes_size += space->Waste() + space->Available(); } return holes_size; } GCTracer::Scope::Scope(GCTracer* tracer, ScopeId scope) : tracer_(tracer), scope_(scope) { start_time_ = tracer_->heap_->MonotonicallyIncreasingTimeInMs(); } GCTracer::Scope::~Scope() { DCHECK(scope_ < NUMBER_OF_SCOPES); // scope_ is unsigned. tracer_->current_.scopes[scope_] += tracer_->heap_->MonotonicallyIncreasingTimeInMs() - start_time_; } GCTracer::AllocationEvent::AllocationEvent(double duration, size_t allocation_in_bytes) { duration_ = duration; allocation_in_bytes_ = allocation_in_bytes; } GCTracer::ContextDisposalEvent::ContextDisposalEvent(double time) { time_ = time; } GCTracer::SurvivalEvent::SurvivalEvent(double promotion_ratio) { promotion_ratio_ = promotion_ratio; } GCTracer::Event::Event(Type type, const char* gc_reason, const char* collector_reason) : type(type), gc_reason(gc_reason), collector_reason(collector_reason), start_time(0.0), end_time(0.0), reduce_memory(false), start_object_size(0), end_object_size(0), start_memory_size(0), end_memory_size(0), start_holes_size(0), end_holes_size(0), cumulative_incremental_marking_steps(0), incremental_marking_steps(0), cumulative_incremental_marking_bytes(0), incremental_marking_bytes(0), cumulative_incremental_marking_duration(0.0), incremental_marking_duration(0.0), cumulative_pure_incremental_marking_duration(0.0), pure_incremental_marking_duration(0.0), longest_incremental_marking_step(0.0) { for (int i = 0; i < Scope::NUMBER_OF_SCOPES; i++) { scopes[i] = 0; } } const char* GCTracer::Event::TypeName(bool short_name) const { switch (type) { case SCAVENGER: if (short_name) { return "s"; } else { return "Scavenge"; } case MARK_COMPACTOR: case INCREMENTAL_MARK_COMPACTOR: if (short_name) { return "ms"; } else { return "Mark-sweep"; } case START: if (short_name) { return "st"; } else { return "Start"; } } return "Unknown Event Type"; } GCTracer::GCTracer(Heap* heap) : heap_(heap), cumulative_incremental_marking_steps_(0), cumulative_incremental_marking_bytes_(0), cumulative_incremental_marking_duration_(0.0), cumulative_pure_incremental_marking_duration_(0.0), longest_incremental_marking_step_(0.0), cumulative_incremental_marking_finalization_steps_(0), cumulative_incremental_marking_finalization_duration_(0.0), longest_incremental_marking_finalization_step_(0.0), cumulative_marking_duration_(0.0), cumulative_sweeping_duration_(0.0), allocation_time_ms_(0.0), new_space_allocation_counter_bytes_(0), old_generation_allocation_counter_bytes_(0), allocation_duration_since_gc_(0.0), new_space_allocation_in_bytes_since_gc_(0), old_generation_allocation_in_bytes_since_gc_(0), combined_mark_compact_speed_cache_(0.0), start_counter_(0) { current_ = Event(Event::START, NULL, NULL); current_.end_time = heap_->MonotonicallyIncreasingTimeInMs(); previous_ = previous_incremental_mark_compactor_event_ = current_; } void GCTracer::Start(GarbageCollector collector, const char* gc_reason, const char* collector_reason) { start_counter_++; if (start_counter_ != 1) return; previous_ = current_; double start_time = heap_->MonotonicallyIncreasingTimeInMs(); SampleAllocation(start_time, heap_->NewSpaceAllocationCounter(), heap_->OldGenerationAllocationCounter()); if (current_.type == Event::INCREMENTAL_MARK_COMPACTOR) previous_incremental_mark_compactor_event_ = current_; if (collector == SCAVENGER) { current_ = Event(Event::SCAVENGER, gc_reason, collector_reason); } else if (collector == MARK_COMPACTOR) { if (heap_->incremental_marking()->WasActivated()) { current_ = Event(Event::INCREMENTAL_MARK_COMPACTOR, gc_reason, collector_reason); } else { current_ = Event(Event::MARK_COMPACTOR, gc_reason, collector_reason); } } current_.reduce_memory = heap_->ShouldReduceMemory(); current_.start_time = start_time; current_.start_object_size = heap_->SizeOfObjects(); current_.start_memory_size = heap_->isolate()->memory_allocator()->Size(); current_.start_holes_size = CountTotalHolesSize(heap_); current_.new_space_object_size = heap_->new_space()->top() - heap_->new_space()->bottom(); current_.cumulative_incremental_marking_steps = cumulative_incremental_marking_steps_; current_.cumulative_incremental_marking_bytes = cumulative_incremental_marking_bytes_; current_.cumulative_incremental_marking_duration = cumulative_incremental_marking_duration_; current_.cumulative_pure_incremental_marking_duration = cumulative_pure_incremental_marking_duration_; current_.longest_incremental_marking_step = longest_incremental_marking_step_; for (int i = 0; i < Scope::NUMBER_OF_SCOPES; i++) { current_.scopes[i] = 0; } int committed_memory = static_cast(heap_->CommittedMemory() / KB); int used_memory = static_cast(current_.start_object_size / KB); heap_->isolate()->counters()->aggregated_memory_heap_committed()->AddSample( start_time, committed_memory); heap_->isolate()->counters()->aggregated_memory_heap_used()->AddSample( start_time, used_memory); } void GCTracer::Stop(GarbageCollector collector) { start_counter_--; if (start_counter_ != 0) { Output("[Finished reentrant %s during %s.]\n", collector == SCAVENGER ? "Scavenge" : "Mark-sweep", current_.TypeName(false)); return; } DCHECK(start_counter_ >= 0); DCHECK((collector == SCAVENGER && current_.type == Event::SCAVENGER) || (collector == MARK_COMPACTOR && (current_.type == Event::MARK_COMPACTOR || current_.type == Event::INCREMENTAL_MARK_COMPACTOR))); current_.end_time = heap_->MonotonicallyIncreasingTimeInMs(); current_.end_object_size = heap_->SizeOfObjects(); current_.end_memory_size = heap_->isolate()->memory_allocator()->Size(); current_.end_holes_size = CountTotalHolesSize(heap_); current_.survived_new_space_object_size = heap_->SurvivedNewSpaceObjectSize(); AddAllocation(current_.end_time); int committed_memory = static_cast(heap_->CommittedMemory() / KB); int used_memory = static_cast(current_.end_object_size / KB); heap_->isolate()->counters()->aggregated_memory_heap_committed()->AddSample( current_.end_time, committed_memory); heap_->isolate()->counters()->aggregated_memory_heap_used()->AddSample( current_.end_time, used_memory); if (current_.type == Event::SCAVENGER) { current_.incremental_marking_steps = current_.cumulative_incremental_marking_steps - previous_.cumulative_incremental_marking_steps; current_.incremental_marking_bytes = current_.cumulative_incremental_marking_bytes - previous_.cumulative_incremental_marking_bytes; current_.incremental_marking_duration = current_.cumulative_incremental_marking_duration - previous_.cumulative_incremental_marking_duration; current_.pure_incremental_marking_duration = current_.cumulative_pure_incremental_marking_duration - previous_.cumulative_pure_incremental_marking_duration; scavenger_events_.push_front(current_); } else if (current_.type == Event::INCREMENTAL_MARK_COMPACTOR) { current_.incremental_marking_steps = current_.cumulative_incremental_marking_steps - previous_incremental_mark_compactor_event_ .cumulative_incremental_marking_steps; current_.incremental_marking_bytes = current_.cumulative_incremental_marking_bytes - previous_incremental_mark_compactor_event_ .cumulative_incremental_marking_bytes; current_.incremental_marking_duration = current_.cumulative_incremental_marking_duration - previous_incremental_mark_compactor_event_ .cumulative_incremental_marking_duration; current_.pure_incremental_marking_duration = current_.cumulative_pure_incremental_marking_duration - previous_incremental_mark_compactor_event_ .cumulative_pure_incremental_marking_duration; longest_incremental_marking_step_ = 0.0; incremental_mark_compactor_events_.push_front(current_); combined_mark_compact_speed_cache_ = 0.0; } else { DCHECK(current_.incremental_marking_bytes == 0); DCHECK(current_.incremental_marking_duration == 0); DCHECK(current_.pure_incremental_marking_duration == 0); longest_incremental_marking_step_ = 0.0; mark_compactor_events_.push_front(current_); combined_mark_compact_speed_cache_ = 0.0; } // TODO(ernstm): move the code below out of GCTracer. double duration = current_.end_time - current_.start_time; double spent_in_mutator = Max(current_.start_time - previous_.end_time, 0.0); heap_->UpdateCumulativeGCStatistics(duration, spent_in_mutator, current_.scopes[Scope::MC_MARK]); if (current_.type == Event::SCAVENGER && FLAG_trace_gc_ignore_scavenger) return; if (FLAG_trace_gc_nvp) PrintNVP(); else Print(); if (FLAG_trace_gc) { heap_->PrintShortHeapStatistics(); } longest_incremental_marking_finalization_step_ = 0.0; cumulative_incremental_marking_finalization_steps_ = 0; cumulative_incremental_marking_finalization_duration_ = 0.0; } void GCTracer::SampleAllocation(double current_ms, size_t new_space_counter_bytes, size_t old_generation_counter_bytes) { if (allocation_time_ms_ == 0) { // It is the first sample. allocation_time_ms_ = current_ms; new_space_allocation_counter_bytes_ = new_space_counter_bytes; old_generation_allocation_counter_bytes_ = old_generation_counter_bytes; return; } // This assumes that counters are unsigned integers so that the subtraction // below works even if the new counter is less then the old counter. size_t new_space_allocated_bytes = new_space_counter_bytes - new_space_allocation_counter_bytes_; size_t old_generation_allocated_bytes = old_generation_counter_bytes - old_generation_allocation_counter_bytes_; double duration = current_ms - allocation_time_ms_; allocation_time_ms_ = current_ms; new_space_allocation_counter_bytes_ = new_space_counter_bytes; old_generation_allocation_counter_bytes_ = old_generation_counter_bytes; allocation_duration_since_gc_ += duration; new_space_allocation_in_bytes_since_gc_ += new_space_allocated_bytes; old_generation_allocation_in_bytes_since_gc_ += old_generation_allocated_bytes; } void GCTracer::AddAllocation(double current_ms) { allocation_time_ms_ = current_ms; new_space_allocation_events_.push_front(AllocationEvent( allocation_duration_since_gc_, new_space_allocation_in_bytes_since_gc_)); old_generation_allocation_events_.push_front( AllocationEvent(allocation_duration_since_gc_, old_generation_allocation_in_bytes_since_gc_)); allocation_duration_since_gc_ = 0; new_space_allocation_in_bytes_since_gc_ = 0; old_generation_allocation_in_bytes_since_gc_ = 0; } void GCTracer::AddContextDisposalTime(double time) { context_disposal_events_.push_front(ContextDisposalEvent(time)); } void GCTracer::AddCompactionEvent(double duration, intptr_t live_bytes_compacted) { compaction_events_.push_front( CompactionEvent(duration, live_bytes_compacted)); } void GCTracer::AddSurvivalRatio(double promotion_ratio) { survival_events_.push_front(SurvivalEvent(promotion_ratio)); } void GCTracer::AddIncrementalMarkingStep(double duration, intptr_t bytes) { cumulative_incremental_marking_steps_++; cumulative_incremental_marking_bytes_ += bytes; cumulative_incremental_marking_duration_ += duration; longest_incremental_marking_step_ = Max(longest_incremental_marking_step_, duration); cumulative_marking_duration_ += duration; if (bytes > 0) { cumulative_pure_incremental_marking_duration_ += duration; } } void GCTracer::AddIncrementalMarkingFinalizationStep(double duration) { cumulative_incremental_marking_finalization_steps_++; cumulative_incremental_marking_finalization_duration_ += duration; longest_incremental_marking_finalization_step_ = Max(longest_incremental_marking_finalization_step_, duration); } void GCTracer::Output(const char* format, ...) const { if (FLAG_trace_gc) { va_list arguments; va_start(arguments, format); base::OS::VPrint(format, arguments); va_end(arguments); } const int kBufferSize = 256; char raw_buffer[kBufferSize]; Vector buffer(raw_buffer, kBufferSize); va_list arguments2; va_start(arguments2, format); VSNPrintF(buffer, format, arguments2); va_end(arguments2); heap_->AddToRingBuffer(buffer.start()); } void GCTracer::Print() const { if (FLAG_trace_gc) { PrintIsolate(heap_->isolate(), ""); } Output("%8.0f ms: ", heap_->isolate()->time_millis_since_init()); Output("%s %.1f (%.1f) -> %.1f (%.1f) MB, ", current_.TypeName(false), static_cast(current_.start_object_size) / MB, static_cast(current_.start_memory_size) / MB, static_cast(current_.end_object_size) / MB, static_cast(current_.end_memory_size) / MB); int external_time = static_cast(current_.scopes[Scope::EXTERNAL]); double duration = current_.end_time - current_.start_time; Output("%.1f / %d ms", duration, external_time); if (current_.type == Event::SCAVENGER) { if (current_.incremental_marking_steps > 0) { Output(" (+ %.1f ms in %d steps since last GC)", current_.incremental_marking_duration, current_.incremental_marking_steps); } } else { if (current_.incremental_marking_steps > 0) { Output( " (+ %.1f ms in %d steps since start of marking, " "biggest step %.1f ms)", current_.incremental_marking_duration, current_.incremental_marking_steps, current_.longest_incremental_marking_step); } } if (current_.gc_reason != NULL) { Output(" [%s]", current_.gc_reason); } if (current_.collector_reason != NULL) { Output(" [%s]", current_.collector_reason); } Output(".\n"); } void GCTracer::PrintNVP() const { double duration = current_.end_time - current_.start_time; double spent_in_mutator = current_.start_time - previous_.end_time; intptr_t allocated_since_last_gc = current_.start_object_size - previous_.end_object_size; switch (current_.type) { case Event::SCAVENGER: PrintIsolate(heap_->isolate(), "%8.0f ms: " "pause=%.1f " "mutator=%.1f " "gc=%s " "reduce_memory=%d " "scavenge=%.2f " "old_new=%.2f " "weak=%.2f " "roots=%.2f " "code=%.2f " "semispace=%.2f " "object_groups=%.2f " "steps_count=%d " "steps_took=%.1f " "scavenge_throughput=%" V8_PTR_PREFIX "d " "total_size_before=%" V8_PTR_PREFIX "d " "total_size_after=%" V8_PTR_PREFIX "d " "holes_size_before=%" V8_PTR_PREFIX "d " "holes_size_after=%" V8_PTR_PREFIX "d " "allocated=%" V8_PTR_PREFIX "d " "promoted=%" V8_PTR_PREFIX "d " "semi_space_copied=%" V8_PTR_PREFIX "d " "nodes_died_in_new=%d " "nodes_copied_in_new=%d " "nodes_promoted=%d " "promotion_ratio=%.1f%% " "average_survival_ratio=%.1f%% " "promotion_rate=%.1f%% " "semi_space_copy_rate=%.1f%% " "new_space_allocation_throughput=%" V8_PTR_PREFIX "d " "context_disposal_rate=%.1f\n", heap_->isolate()->time_millis_since_init(), duration, spent_in_mutator, current_.TypeName(true), current_.reduce_memory, current_.scopes[Scope::SCAVENGER_SCAVENGE], current_.scopes[Scope::SCAVENGER_OLD_TO_NEW_POINTERS], current_.scopes[Scope::SCAVENGER_WEAK], current_.scopes[Scope::SCAVENGER_ROOTS], current_.scopes[Scope::SCAVENGER_CODE_FLUSH_CANDIDATES], current_.scopes[Scope::SCAVENGER_SEMISPACE], current_.scopes[Scope::SCAVENGER_OBJECT_GROUPS], current_.incremental_marking_steps, current_.incremental_marking_duration, ScavengeSpeedInBytesPerMillisecond(), current_.start_object_size, current_.end_object_size, current_.start_holes_size, current_.end_holes_size, allocated_since_last_gc, heap_->promoted_objects_size(), heap_->semi_space_copied_object_size(), heap_->nodes_died_in_new_space_, heap_->nodes_copied_in_new_space_, heap_->nodes_promoted_, heap_->promotion_ratio_, AverageSurvivalRatio(), heap_->promotion_rate_, heap_->semi_space_copied_rate_, NewSpaceAllocationThroughputInBytesPerMillisecond(), ContextDisposalRateInMilliseconds()); break; case Event::MARK_COMPACTOR: case Event::INCREMENTAL_MARK_COMPACTOR: PrintIsolate( heap_->isolate(), "%8.0f ms: " "pause=%.1f " "mutator=%.1f " "gc=%s " "reduce_memory=%d " "external=%.1f " "clear=%1.f " "clear.code_flush=%.1f " "clear.dependent_code=%.1f " "clear.global_handles=%.1f " "clear.maps=%.1f " "clear.slots_buffer=%.1f " "clear.store_buffer=%.1f " "clear.string_table=%.1f " "clear.weak_cells=%.1f " "clear.weak_collections=%.1f " "clear.weak_lists=%.1f " "evacuate=%.1f " "evacuate.candidates=%.1f " "evacuate.clean_up=%.1f " "evacuate.new_space=%.1f " "evacuate.update_pointers=%.1f " "evacuate.update_pointers.between_evacuated=%.1f " "evacuate.update_pointers.to_evacuated=%.1f " "evacuate.update_pointers.to_new=%.1f " "evacuate.update_pointers.weak=%.1f " "finish=%.1f " "mark=%.1f " "mark.finish_incremental=%.1f " "mark.prepare_code_flush=%.1f " "mark.roots=%.1f " "mark.weak_closure=%.1f " "sweep=%.1f " "sweep.code=%.1f " "sweep.map=%.1f " "sweep.old=%.1f " "incremental_finalize=%.1f " "steps_count=%d " "steps_took=%.1f " "longest_step=%.1f " "finalization_steps_count=%d " "finalization_steps_took=%.1f " "finalization_longest_step=%.1f " "incremental_marking_throughput=%" V8_PTR_PREFIX "d " "total_size_before=%" V8_PTR_PREFIX "d " "total_size_after=%" V8_PTR_PREFIX "d " "holes_size_before=%" V8_PTR_PREFIX "d " "holes_size_after=%" V8_PTR_PREFIX "d " "allocated=%" V8_PTR_PREFIX "d " "promoted=%" V8_PTR_PREFIX "d " "semi_space_copied=%" V8_PTR_PREFIX "d " "nodes_died_in_new=%d " "nodes_copied_in_new=%d " "nodes_promoted=%d " "promotion_ratio=%.1f%% " "average_survival_ratio=%.1f%% " "promotion_rate=%.1f%% " "semi_space_copy_rate=%.1f%% " "new_space_allocation_throughput=%" V8_PTR_PREFIX "d " "context_disposal_rate=%.1f " "compaction_speed=%" V8_PTR_PREFIX "d\n", heap_->isolate()->time_millis_since_init(), duration, spent_in_mutator, current_.TypeName(true), current_.reduce_memory, current_.scopes[Scope::EXTERNAL], current_.scopes[Scope::MC_CLEAR], current_.scopes[Scope::MC_CLEAR_CODE_FLUSH], current_.scopes[Scope::MC_CLEAR_DEPENDENT_CODE], current_.scopes[Scope::MC_CLEAR_GLOBAL_HANDLES], current_.scopes[Scope::MC_CLEAR_MAPS], current_.scopes[Scope::MC_CLEAR_SLOTS_BUFFER], current_.scopes[Scope::MC_CLEAR_STORE_BUFFER], current_.scopes[Scope::MC_CLEAR_STRING_TABLE], current_.scopes[Scope::MC_CLEAR_WEAK_CELLS], current_.scopes[Scope::MC_CLEAR_WEAK_COLLECTIONS], current_.scopes[Scope::MC_CLEAR_WEAK_LISTS], current_.scopes[Scope::MC_EVACUATE], current_.scopes[Scope::MC_EVACUATE_CANDIDATES], current_.scopes[Scope::MC_EVACUATE_CLEAN_UP], current_.scopes[Scope::MC_EVACUATE_NEW_SPACE], current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS], current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_BETWEEN_EVACUATED], current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_TO_EVACUATED], current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_TO_NEW], current_.scopes[Scope::MC_EVACUATE_UPDATE_POINTERS_WEAK], current_.scopes[Scope::MC_FINISH], current_.scopes[Scope::MC_MARK], current_.scopes[Scope::MC_MARK_FINISH_INCREMENTAL], current_.scopes[Scope::MC_MARK_PREPARE_CODE_FLUSH], current_.scopes[Scope::MC_MARK_ROOTS], current_.scopes[Scope::MC_MARK_WEAK_CLOSURE], current_.scopes[Scope::MC_SWEEP], current_.scopes[Scope::MC_SWEEP_CODE], current_.scopes[Scope::MC_SWEEP_MAP], current_.scopes[Scope::MC_SWEEP_OLD], current_.scopes[Scope::MC_INCREMENTAL_FINALIZE], current_.incremental_marking_steps, current_.incremental_marking_duration, current_.longest_incremental_marking_step, cumulative_incremental_marking_finalization_steps_, cumulative_incremental_marking_finalization_duration_, longest_incremental_marking_finalization_step_, IncrementalMarkingSpeedInBytesPerMillisecond(), current_.start_object_size, current_.end_object_size, current_.start_holes_size, current_.end_holes_size, allocated_since_last_gc, heap_->promoted_objects_size(), heap_->semi_space_copied_object_size(), heap_->nodes_died_in_new_space_, heap_->nodes_copied_in_new_space_, heap_->nodes_promoted_, heap_->promotion_ratio_, AverageSurvivalRatio(), heap_->promotion_rate_, heap_->semi_space_copied_rate_, NewSpaceAllocationThroughputInBytesPerMillisecond(), ContextDisposalRateInMilliseconds(), CompactionSpeedInBytesPerMillisecond()); break; case Event::START: break; default: UNREACHABLE(); } } double GCTracer::MeanDuration(const EventBuffer& events) const { if (events.empty()) return 0.0; double mean = 0.0; EventBuffer::const_iterator iter = events.begin(); while (iter != events.end()) { mean += iter->end_time - iter->start_time; ++iter; } return mean / events.size(); } double GCTracer::MaxDuration(const EventBuffer& events) const { if (events.empty()) return 0.0; double maximum = 0.0f; EventBuffer::const_iterator iter = events.begin(); while (iter != events.end()) { maximum = Max(iter->end_time - iter->start_time, maximum); ++iter; } return maximum; } double GCTracer::MeanIncrementalMarkingDuration() const { if (cumulative_incremental_marking_steps_ == 0) return 0.0; // We haven't completed an entire round of incremental marking, yet. // Use data from GCTracer instead of data from event buffers. if (incremental_mark_compactor_events_.empty()) { return cumulative_incremental_marking_duration_ / cumulative_incremental_marking_steps_; } int steps = 0; double durations = 0.0; EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin(); while (iter != incremental_mark_compactor_events_.end()) { steps += iter->incremental_marking_steps; durations += iter->incremental_marking_duration; ++iter; } if (steps == 0) return 0.0; return durations / steps; } double GCTracer::MaxIncrementalMarkingDuration() const { // We haven't completed an entire round of incremental marking, yet. // Use data from GCTracer instead of data from event buffers. if (incremental_mark_compactor_events_.empty()) return longest_incremental_marking_step_; double max_duration = 0.0; EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin(); while (iter != incremental_mark_compactor_events_.end()) max_duration = Max(iter->longest_incremental_marking_step, max_duration); return max_duration; } intptr_t GCTracer::IncrementalMarkingSpeedInBytesPerMillisecond() const { if (cumulative_incremental_marking_duration_ == 0.0) return 0; // We haven't completed an entire round of incremental marking, yet. // Use data from GCTracer instead of data from event buffers. if (incremental_mark_compactor_events_.empty()) { return static_cast(cumulative_incremental_marking_bytes_ / cumulative_pure_incremental_marking_duration_); } intptr_t bytes = 0; double durations = 0.0; EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin(); while (iter != incremental_mark_compactor_events_.end()) { bytes += iter->incremental_marking_bytes; durations += iter->pure_incremental_marking_duration; ++iter; } if (durations == 0.0) return 0; // Make sure the result is at least 1. return Max(static_cast(bytes / durations + 0.5), 1); } intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond( ScavengeSpeedMode mode) const { intptr_t bytes = 0; double durations = 0.0; EventBuffer::const_iterator iter = scavenger_events_.begin(); while (iter != scavenger_events_.end()) { bytes += mode == kForAllObjects ? iter->new_space_object_size : iter->survived_new_space_object_size; durations += iter->end_time - iter->start_time; ++iter; } if (durations == 0.0) return 0; // Make sure the result is at least 1. return Max(static_cast(bytes / durations + 0.5), 1); } intptr_t GCTracer::CompactionSpeedInBytesPerMillisecond() const { if (compaction_events_.size() == 0) return 0; intptr_t bytes = 0; double durations = 0.0; CompactionEventBuffer::const_iterator iter = compaction_events_.begin(); while (iter != compaction_events_.end()) { bytes += iter->live_bytes_compacted; durations += iter->duration; ++iter; } if (durations == 0.0) return 0; // Make sure the result is at least 1. return Max(static_cast(bytes / durations + 0.5), 1); } intptr_t GCTracer::MarkCompactSpeedInBytesPerMillisecond() const { intptr_t bytes = 0; double durations = 0.0; EventBuffer::const_iterator iter = mark_compactor_events_.begin(); while (iter != mark_compactor_events_.end()) { bytes += iter->start_object_size; durations += iter->end_time - iter->start_time; ++iter; } if (durations == 0.0) return 0; // Make sure the result is at least 1. return Max(static_cast(bytes / durations + 0.5), 1); } intptr_t GCTracer::FinalIncrementalMarkCompactSpeedInBytesPerMillisecond() const { intptr_t bytes = 0; double durations = 0.0; EventBuffer::const_iterator iter = incremental_mark_compactor_events_.begin(); while (iter != incremental_mark_compactor_events_.end()) { bytes += iter->start_object_size; durations += iter->end_time - iter->start_time; ++iter; } if (durations == 0.0) return 0; // Make sure the result is at least 1. return Max(static_cast(bytes / durations + 0.5), 1); } double GCTracer::CombinedMarkCompactSpeedInBytesPerMillisecond() { if (combined_mark_compact_speed_cache_ > 0) return combined_mark_compact_speed_cache_; const double kMinimumMarkingSpeed = 0.5; double speed1 = static_cast(IncrementalMarkingSpeedInBytesPerMillisecond()); double speed2 = static_cast( FinalIncrementalMarkCompactSpeedInBytesPerMillisecond()); if (speed1 < kMinimumMarkingSpeed || speed2 < kMinimumMarkingSpeed) { // No data for the incremental marking speed. // Return the non-incremental mark-compact speed. combined_mark_compact_speed_cache_ = static_cast(MarkCompactSpeedInBytesPerMillisecond()); } else { // Combine the speed of incremental step and the speed of the final step. // 1 / (1 / speed1 + 1 / speed2) = speed1 * speed2 / (speed1 + speed2). combined_mark_compact_speed_cache_ = speed1 * speed2 / (speed1 + speed2); } return combined_mark_compact_speed_cache_; } size_t GCTracer::NewSpaceAllocationThroughputInBytesPerMillisecond( double time_ms) const { size_t bytes = new_space_allocation_in_bytes_since_gc_; double durations = allocation_duration_since_gc_; AllocationEventBuffer::const_iterator iter = new_space_allocation_events_.begin(); const size_t max_bytes = static_cast(-1); while (iter != new_space_allocation_events_.end() && bytes < max_bytes - bytes && (time_ms == 0 || durations < time_ms)) { bytes += iter->allocation_in_bytes_; durations += iter->duration_; ++iter; } if (durations == 0.0) return 0; // Make sure the result is at least 1. return Max(static_cast(bytes / durations + 0.5), 1); } size_t GCTracer::OldGenerationAllocationThroughputInBytesPerMillisecond( double time_ms) const { size_t bytes = old_generation_allocation_in_bytes_since_gc_; double durations = allocation_duration_since_gc_; AllocationEventBuffer::const_iterator iter = old_generation_allocation_events_.begin(); const size_t max_bytes = static_cast(-1); while (iter != old_generation_allocation_events_.end() && bytes < max_bytes - bytes && (time_ms == 0 || durations < time_ms)) { bytes += iter->allocation_in_bytes_; durations += iter->duration_; ++iter; } if (durations == 0.0) return 0; // Make sure the result is at least 1. return Max(static_cast(bytes / durations + 0.5), 1); } size_t GCTracer::AllocationThroughputInBytesPerMillisecond( double time_ms) const { return NewSpaceAllocationThroughputInBytesPerMillisecond(time_ms) + OldGenerationAllocationThroughputInBytesPerMillisecond(time_ms); } size_t GCTracer::CurrentAllocationThroughputInBytesPerMillisecond() const { return AllocationThroughputInBytesPerMillisecond(kThroughputTimeFrameMs); } size_t GCTracer::CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const { return OldGenerationAllocationThroughputInBytesPerMillisecond( kThroughputTimeFrameMs); } double GCTracer::ContextDisposalRateInMilliseconds() const { if (context_disposal_events_.size() < kRingBufferMaxSize) return 0.0; double begin = heap_->MonotonicallyIncreasingTimeInMs(); double end = 0.0; ContextDisposalEventBuffer::const_iterator iter = context_disposal_events_.begin(); while (iter != context_disposal_events_.end()) { end = iter->time_; ++iter; } return (begin - end) / context_disposal_events_.size(); } double GCTracer::AverageSurvivalRatio() const { if (survival_events_.size() == 0) return 0.0; double sum_of_rates = 0.0; SurvivalEventBuffer::const_iterator iter = survival_events_.begin(); while (iter != survival_events_.end()) { sum_of_rates += iter->promotion_ratio_; ++iter; } return sum_of_rates / static_cast(survival_events_.size()); } bool GCTracer::SurvivalEventsRecorded() const { return survival_events_.size() > 0; } void GCTracer::ResetSurvivalEvents() { survival_events_.reset(); } } // namespace internal } // namespace v8