Home | History | Annotate | Download | only in heap
      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/v8.h"
      6 
      7 #include "src/heap/gc-tracer.h"
      8 
      9 namespace v8 {
     10 namespace internal {
     11 
     12 static intptr_t CountTotalHolesSize(Heap* heap) {
     13   intptr_t holes_size = 0;
     14   OldSpaces spaces(heap);
     15   for (OldSpace* space = spaces.next(); space != NULL; space = spaces.next()) {
     16     holes_size += space->Waste() + space->Available();
     17   }
     18   return holes_size;
     19 }
     20 
     21 
     22 GCTracer::AllocationEvent::AllocationEvent(double duration,
     23                                            intptr_t allocation_in_bytes) {
     24   duration_ = duration;
     25   allocation_in_bytes_ = allocation_in_bytes;
     26 }
     27 
     28 
     29 GCTracer::Event::Event(Type type, const char* gc_reason,
     30                        const char* collector_reason)
     31     : type(type),
     32       gc_reason(gc_reason),
     33       collector_reason(collector_reason),
     34       start_time(0.0),
     35       end_time(0.0),
     36       start_object_size(0),
     37       end_object_size(0),
     38       start_memory_size(0),
     39       end_memory_size(0),
     40       start_holes_size(0),
     41       end_holes_size(0),
     42       cumulative_incremental_marking_steps(0),
     43       incremental_marking_steps(0),
     44       cumulative_incremental_marking_bytes(0),
     45       incremental_marking_bytes(0),
     46       cumulative_incremental_marking_duration(0.0),
     47       incremental_marking_duration(0.0),
     48       cumulative_pure_incremental_marking_duration(0.0),
     49       pure_incremental_marking_duration(0.0),
     50       longest_incremental_marking_step(0.0) {
     51   for (int i = 0; i < Scope::NUMBER_OF_SCOPES; i++) {
     52     scopes[i] = 0;
     53   }
     54 }
     55 
     56 
     57 const char* GCTracer::Event::TypeName(bool short_name) const {
     58   switch (type) {
     59     case SCAVENGER:
     60       if (short_name) {
     61         return "s";
     62       } else {
     63         return "Scavenge";
     64       }
     65     case MARK_COMPACTOR:
     66       if (short_name) {
     67         return "ms";
     68       } else {
     69         return "Mark-sweep";
     70       }
     71     case START:
     72       if (short_name) {
     73         return "st";
     74       } else {
     75         return "Start";
     76       }
     77   }
     78   return "Unknown Event Type";
     79 }
     80 
     81 
     82 GCTracer::GCTracer(Heap* heap)
     83     : heap_(heap),
     84       cumulative_incremental_marking_steps_(0),
     85       cumulative_incremental_marking_bytes_(0),
     86       cumulative_incremental_marking_duration_(0.0),
     87       cumulative_pure_incremental_marking_duration_(0.0),
     88       longest_incremental_marking_step_(0.0),
     89       cumulative_marking_duration_(0.0),
     90       cumulative_sweeping_duration_(0.0),
     91       new_space_top_after_gc_(0) {
     92   current_ = Event(Event::START, NULL, NULL);
     93   current_.end_time = base::OS::TimeCurrentMillis();
     94   previous_ = previous_mark_compactor_event_ = current_;
     95 }
     96 
     97 
     98 void GCTracer::Start(GarbageCollector collector, const char* gc_reason,
     99                      const char* collector_reason) {
    100   previous_ = current_;
    101   double start_time = base::OS::TimeCurrentMillis();
    102   if (new_space_top_after_gc_ != 0) {
    103     AddNewSpaceAllocationTime(
    104         start_time - previous_.end_time,
    105         reinterpret_cast<intptr_t>((heap_->new_space()->top()) -
    106                                    new_space_top_after_gc_));
    107   }
    108   if (current_.type == Event::MARK_COMPACTOR)
    109     previous_mark_compactor_event_ = current_;
    110 
    111   if (collector == SCAVENGER) {
    112     current_ = Event(Event::SCAVENGER, gc_reason, collector_reason);
    113   } else {
    114     current_ = Event(Event::MARK_COMPACTOR, gc_reason, collector_reason);
    115   }
    116 
    117   current_.start_time = start_time;
    118   current_.start_object_size = heap_->SizeOfObjects();
    119   current_.start_memory_size = heap_->isolate()->memory_allocator()->Size();
    120   current_.start_holes_size = CountTotalHolesSize(heap_);
    121   current_.new_space_object_size =
    122       heap_->new_space()->top() - heap_->new_space()->bottom();
    123 
    124   current_.cumulative_incremental_marking_steps =
    125       cumulative_incremental_marking_steps_;
    126   current_.cumulative_incremental_marking_bytes =
    127       cumulative_incremental_marking_bytes_;
    128   current_.cumulative_incremental_marking_duration =
    129       cumulative_incremental_marking_duration_;
    130   current_.cumulative_pure_incremental_marking_duration =
    131       cumulative_pure_incremental_marking_duration_;
    132   current_.longest_incremental_marking_step = longest_incremental_marking_step_;
    133 
    134   for (int i = 0; i < Scope::NUMBER_OF_SCOPES; i++) {
    135     current_.scopes[i] = 0;
    136   }
    137 }
    138 
    139 
    140 void GCTracer::Stop() {
    141   current_.end_time = base::OS::TimeCurrentMillis();
    142   current_.end_object_size = heap_->SizeOfObjects();
    143   current_.end_memory_size = heap_->isolate()->memory_allocator()->Size();
    144   current_.end_holes_size = CountTotalHolesSize(heap_);
    145   new_space_top_after_gc_ =
    146       reinterpret_cast<intptr_t>(heap_->new_space()->top());
    147 
    148   if (current_.type == Event::SCAVENGER) {
    149     current_.incremental_marking_steps =
    150         current_.cumulative_incremental_marking_steps -
    151         previous_.cumulative_incremental_marking_steps;
    152     current_.incremental_marking_bytes =
    153         current_.cumulative_incremental_marking_bytes -
    154         previous_.cumulative_incremental_marking_bytes;
    155     current_.incremental_marking_duration =
    156         current_.cumulative_incremental_marking_duration -
    157         previous_.cumulative_incremental_marking_duration;
    158     current_.pure_incremental_marking_duration =
    159         current_.cumulative_pure_incremental_marking_duration -
    160         previous_.cumulative_pure_incremental_marking_duration;
    161     scavenger_events_.push_front(current_);
    162   } else {
    163     current_.incremental_marking_steps =
    164         current_.cumulative_incremental_marking_steps -
    165         previous_mark_compactor_event_.cumulative_incremental_marking_steps;
    166     current_.incremental_marking_bytes =
    167         current_.cumulative_incremental_marking_bytes -
    168         previous_mark_compactor_event_.cumulative_incremental_marking_bytes;
    169     current_.incremental_marking_duration =
    170         current_.cumulative_incremental_marking_duration -
    171         previous_mark_compactor_event_.cumulative_incremental_marking_duration;
    172     current_.pure_incremental_marking_duration =
    173         current_.cumulative_pure_incremental_marking_duration -
    174         previous_mark_compactor_event_
    175             .cumulative_pure_incremental_marking_duration;
    176     longest_incremental_marking_step_ = 0.0;
    177     mark_compactor_events_.push_front(current_);
    178   }
    179 
    180   // TODO(ernstm): move the code below out of GCTracer.
    181 
    182   if (!FLAG_trace_gc && !FLAG_print_cumulative_gc_stat) return;
    183 
    184   double duration = current_.end_time - current_.start_time;
    185   double spent_in_mutator = Max(current_.start_time - previous_.end_time, 0.0);
    186 
    187   heap_->UpdateCumulativeGCStatistics(duration, spent_in_mutator,
    188                                       current_.scopes[Scope::MC_MARK]);
    189 
    190   if (current_.type == Event::SCAVENGER && FLAG_trace_gc_ignore_scavenger)
    191     return;
    192 
    193   if (FLAG_trace_gc) {
    194     if (FLAG_trace_gc_nvp)
    195       PrintNVP();
    196     else
    197       Print();
    198 
    199     heap_->PrintShortHeapStatistics();
    200   }
    201 }
    202 
    203 
    204 void GCTracer::AddNewSpaceAllocationTime(double duration,
    205                                          intptr_t allocation_in_bytes) {
    206   allocation_events_.push_front(AllocationEvent(duration, allocation_in_bytes));
    207 }
    208 
    209 
    210 void GCTracer::AddIncrementalMarkingStep(double duration, intptr_t bytes) {
    211   cumulative_incremental_marking_steps_++;
    212   cumulative_incremental_marking_bytes_ += bytes;
    213   cumulative_incremental_marking_duration_ += duration;
    214   longest_incremental_marking_step_ =
    215       Max(longest_incremental_marking_step_, duration);
    216   cumulative_marking_duration_ += duration;
    217   if (bytes > 0) {
    218     cumulative_pure_incremental_marking_duration_ += duration;
    219   }
    220 }
    221 
    222 
    223 void GCTracer::Print() const {
    224   PrintPID("%8.0f ms: ", heap_->isolate()->time_millis_since_init());
    225 
    226   PrintF("%s %.1f (%.1f) -> %.1f (%.1f) MB, ", current_.TypeName(false),
    227          static_cast<double>(current_.start_object_size) / MB,
    228          static_cast<double>(current_.start_memory_size) / MB,
    229          static_cast<double>(current_.end_object_size) / MB,
    230          static_cast<double>(current_.end_memory_size) / MB);
    231 
    232   int external_time = static_cast<int>(current_.scopes[Scope::EXTERNAL]);
    233   if (external_time > 0) PrintF("%d / ", external_time);
    234 
    235   double duration = current_.end_time - current_.start_time;
    236   PrintF("%.1f ms", duration);
    237   if (current_.type == Event::SCAVENGER) {
    238     if (current_.incremental_marking_steps > 0) {
    239       PrintF(" (+ %.1f ms in %d steps since last GC)",
    240              current_.incremental_marking_duration,
    241              current_.incremental_marking_steps);
    242     }
    243   } else {
    244     if (current_.incremental_marking_steps > 0) {
    245       PrintF(
    246           " (+ %.1f ms in %d steps since start of marking, "
    247           "biggest step %.1f ms)",
    248           current_.incremental_marking_duration,
    249           current_.incremental_marking_steps,
    250           current_.longest_incremental_marking_step);
    251     }
    252   }
    253 
    254   if (current_.gc_reason != NULL) {
    255     PrintF(" [%s]", current_.gc_reason);
    256   }
    257 
    258   if (current_.collector_reason != NULL) {
    259     PrintF(" [%s]", current_.collector_reason);
    260   }
    261 
    262   PrintF(".\n");
    263 }
    264 
    265 
    266 void GCTracer::PrintNVP() const {
    267   PrintPID("%8.0f ms: ", heap_->isolate()->time_millis_since_init());
    268 
    269   double duration = current_.end_time - current_.start_time;
    270   double spent_in_mutator = current_.start_time - previous_.end_time;
    271 
    272   PrintF("pause=%.1f ", duration);
    273   PrintF("mutator=%.1f ", spent_in_mutator);
    274   PrintF("gc=%s ", current_.TypeName(true));
    275 
    276   PrintF("external=%.1f ", current_.scopes[Scope::EXTERNAL]);
    277   PrintF("mark=%.1f ", current_.scopes[Scope::MC_MARK]);
    278   PrintF("sweep=%.2f ", current_.scopes[Scope::MC_SWEEP]);
    279   PrintF("sweepns=%.2f ", current_.scopes[Scope::MC_SWEEP_NEWSPACE]);
    280   PrintF("sweepos=%.2f ", current_.scopes[Scope::MC_SWEEP_OLDSPACE]);
    281   PrintF("sweepcode=%.2f ", current_.scopes[Scope::MC_SWEEP_CODE]);
    282   PrintF("sweepcell=%.2f ", current_.scopes[Scope::MC_SWEEP_CELL]);
    283   PrintF("sweepmap=%.2f ", current_.scopes[Scope::MC_SWEEP_MAP]);
    284   PrintF("evacuate=%.1f ", current_.scopes[Scope::MC_EVACUATE_PAGES]);
    285   PrintF("new_new=%.1f ",
    286          current_.scopes[Scope::MC_UPDATE_NEW_TO_NEW_POINTERS]);
    287   PrintF("root_new=%.1f ",
    288          current_.scopes[Scope::MC_UPDATE_ROOT_TO_NEW_POINTERS]);
    289   PrintF("old_new=%.1f ",
    290          current_.scopes[Scope::MC_UPDATE_OLD_TO_NEW_POINTERS]);
    291   PrintF("compaction_ptrs=%.1f ",
    292          current_.scopes[Scope::MC_UPDATE_POINTERS_TO_EVACUATED]);
    293   PrintF("intracompaction_ptrs=%.1f ",
    294          current_.scopes[Scope::MC_UPDATE_POINTERS_BETWEEN_EVACUATED]);
    295   PrintF("misc_compaction=%.1f ",
    296          current_.scopes[Scope::MC_UPDATE_MISC_POINTERS]);
    297   PrintF("weakcollection_process=%.1f ",
    298          current_.scopes[Scope::MC_WEAKCOLLECTION_PROCESS]);
    299   PrintF("weakcollection_clear=%.1f ",
    300          current_.scopes[Scope::MC_WEAKCOLLECTION_CLEAR]);
    301   PrintF("weakcollection_abort=%.1f ",
    302          current_.scopes[Scope::MC_WEAKCOLLECTION_ABORT]);
    303 
    304   PrintF("total_size_before=%" V8_PTR_PREFIX "d ", current_.start_object_size);
    305   PrintF("total_size_after=%" V8_PTR_PREFIX "d ", current_.end_object_size);
    306   PrintF("holes_size_before=%" V8_PTR_PREFIX "d ", current_.start_holes_size);
    307   PrintF("holes_size_after=%" V8_PTR_PREFIX "d ", current_.end_holes_size);
    308 
    309   intptr_t allocated_since_last_gc =
    310       current_.start_object_size - previous_.end_object_size;
    311   PrintF("allocated=%" V8_PTR_PREFIX "d ", allocated_since_last_gc);
    312   PrintF("promoted=%" V8_PTR_PREFIX "d ", heap_->promoted_objects_size_);
    313   PrintF("semi_space_copied=%" V8_PTR_PREFIX "d ",
    314          heap_->semi_space_copied_object_size_);
    315   PrintF("nodes_died_in_new=%d ", heap_->nodes_died_in_new_space_);
    316   PrintF("nodes_copied_in_new=%d ", heap_->nodes_copied_in_new_space_);
    317   PrintF("nodes_promoted=%d ", heap_->nodes_promoted_);
    318   PrintF("promotion_rate=%.1f%% ", heap_->promotion_rate_);
    319   PrintF("semi_space_copy_rate=%.1f%% ", heap_->semi_space_copied_rate_);
    320   PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ",
    321          NewSpaceAllocationThroughputInBytesPerMillisecond());
    322 
    323   if (current_.type == Event::SCAVENGER) {
    324     PrintF("steps_count=%d ", current_.incremental_marking_steps);
    325     PrintF("steps_took=%.1f ", current_.incremental_marking_duration);
    326     PrintF("scavenge_throughput=%" V8_PTR_PREFIX "d ",
    327            ScavengeSpeedInBytesPerMillisecond());
    328   } else {
    329     PrintF("steps_count=%d ", current_.incremental_marking_steps);
    330     PrintF("steps_took=%.1f ", current_.incremental_marking_duration);
    331     PrintF("longest_step=%.1f ", current_.longest_incremental_marking_step);
    332     PrintF("incremental_marking_throughput=%" V8_PTR_PREFIX "d ",
    333            IncrementalMarkingSpeedInBytesPerMillisecond());
    334   }
    335 
    336   PrintF("\n");
    337 }
    338 
    339 
    340 double GCTracer::MeanDuration(const EventBuffer& events) const {
    341   if (events.empty()) return 0.0;
    342 
    343   double mean = 0.0;
    344   EventBuffer::const_iterator iter = events.begin();
    345   while (iter != events.end()) {
    346     mean += iter->end_time - iter->start_time;
    347     ++iter;
    348   }
    349 
    350   return mean / events.size();
    351 }
    352 
    353 
    354 double GCTracer::MaxDuration(const EventBuffer& events) const {
    355   if (events.empty()) return 0.0;
    356 
    357   double maximum = 0.0f;
    358   EventBuffer::const_iterator iter = events.begin();
    359   while (iter != events.end()) {
    360     maximum = Max(iter->end_time - iter->start_time, maximum);
    361     ++iter;
    362   }
    363 
    364   return maximum;
    365 }
    366 
    367 
    368 double GCTracer::MeanIncrementalMarkingDuration() const {
    369   if (cumulative_incremental_marking_steps_ == 0) return 0.0;
    370 
    371   // We haven't completed an entire round of incremental marking, yet.
    372   // Use data from GCTracer instead of data from event buffers.
    373   if (mark_compactor_events_.empty()) {
    374     return cumulative_incremental_marking_duration_ /
    375            cumulative_incremental_marking_steps_;
    376   }
    377 
    378   int steps = 0;
    379   double durations = 0.0;
    380   EventBuffer::const_iterator iter = mark_compactor_events_.begin();
    381   while (iter != mark_compactor_events_.end()) {
    382     steps += iter->incremental_marking_steps;
    383     durations += iter->incremental_marking_duration;
    384     ++iter;
    385   }
    386 
    387   if (steps == 0) return 0.0;
    388 
    389   return durations / steps;
    390 }
    391 
    392 
    393 double GCTracer::MaxIncrementalMarkingDuration() const {
    394   // We haven't completed an entire round of incremental marking, yet.
    395   // Use data from GCTracer instead of data from event buffers.
    396   if (mark_compactor_events_.empty()) return longest_incremental_marking_step_;
    397 
    398   double max_duration = 0.0;
    399   EventBuffer::const_iterator iter = mark_compactor_events_.begin();
    400   while (iter != mark_compactor_events_.end())
    401     max_duration = Max(iter->longest_incremental_marking_step, max_duration);
    402 
    403   return max_duration;
    404 }
    405 
    406 
    407 intptr_t GCTracer::IncrementalMarkingSpeedInBytesPerMillisecond() const {
    408   if (cumulative_incremental_marking_duration_ == 0.0) return 0;
    409 
    410   // We haven't completed an entire round of incremental marking, yet.
    411   // Use data from GCTracer instead of data from event buffers.
    412   if (mark_compactor_events_.empty()) {
    413     return static_cast<intptr_t>(cumulative_incremental_marking_bytes_ /
    414                                  cumulative_pure_incremental_marking_duration_);
    415   }
    416 
    417   intptr_t bytes = 0;
    418   double durations = 0.0;
    419   EventBuffer::const_iterator iter = mark_compactor_events_.begin();
    420   while (iter != mark_compactor_events_.end()) {
    421     bytes += iter->incremental_marking_bytes;
    422     durations += iter->pure_incremental_marking_duration;
    423     ++iter;
    424   }
    425 
    426   if (durations == 0.0) return 0;
    427 
    428   return static_cast<intptr_t>(bytes / durations);
    429 }
    430 
    431 
    432 intptr_t GCTracer::ScavengeSpeedInBytesPerMillisecond() const {
    433   intptr_t bytes = 0;
    434   double durations = 0.0;
    435   EventBuffer::const_iterator iter = scavenger_events_.begin();
    436   while (iter != scavenger_events_.end()) {
    437     bytes += iter->new_space_object_size;
    438     durations += iter->end_time - iter->start_time;
    439     ++iter;
    440   }
    441 
    442   if (durations == 0.0) return 0;
    443 
    444   return static_cast<intptr_t>(bytes / durations);
    445 }
    446 
    447 
    448 intptr_t GCTracer::MarkCompactSpeedInBytesPerMillisecond() const {
    449   intptr_t bytes = 0;
    450   double durations = 0.0;
    451   EventBuffer::const_iterator iter = mark_compactor_events_.begin();
    452   while (iter != mark_compactor_events_.end()) {
    453     bytes += iter->start_object_size;
    454     durations += iter->end_time - iter->start_time +
    455                  iter->pure_incremental_marking_duration;
    456     ++iter;
    457   }
    458 
    459   if (durations == 0.0) return 0;
    460 
    461   return static_cast<intptr_t>(bytes / durations);
    462 }
    463 
    464 
    465 intptr_t GCTracer::NewSpaceAllocationThroughputInBytesPerMillisecond() const {
    466   intptr_t bytes = 0;
    467   double durations = 0.0;
    468   AllocationEventBuffer::const_iterator iter = allocation_events_.begin();
    469   while (iter != allocation_events_.end()) {
    470     bytes += iter->allocation_in_bytes_;
    471     durations += iter->duration_;
    472     ++iter;
    473   }
    474 
    475   if (durations == 0.0) return 0;
    476 
    477   return static_cast<intptr_t>(bytes / durations);
    478 }
    479 }
    480 }  // namespace v8::internal
    481