Home | History | Annotate | Download | only in src
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include "v8.h"
     29 
     30 #include "runtime-profiler.h"
     31 
     32 #include "assembler.h"
     33 #include "bootstrapper.h"
     34 #include "code-stubs.h"
     35 #include "compilation-cache.h"
     36 #include "deoptimizer.h"
     37 #include "execution.h"
     38 #include "full-codegen.h"
     39 #include "global-handles.h"
     40 #include "isolate-inl.h"
     41 #include "mark-compact.h"
     42 #include "platform.h"
     43 #include "scopeinfo.h"
     44 
     45 namespace v8 {
     46 namespace internal {
     47 
     48 
     49 // Optimization sampler constants.
     50 static const int kSamplerFrameCount = 2;
     51 
     52 // Constants for statistical profiler.
     53 static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 };
     54 
     55 static const int kSamplerTicksBetweenThresholdAdjustment = 32;
     56 
     57 static const int kSamplerThresholdInit = 3;
     58 static const int kSamplerThresholdMin = 1;
     59 static const int kSamplerThresholdDelta = 1;
     60 
     61 static const int kSamplerThresholdSizeFactorInit = 3;
     62 
     63 static const int kSizeLimit = 1500;
     64 
     65 // Constants for counter based profiler.
     66 
     67 // Number of times a function has to be seen on the stack before it is
     68 // optimized.
     69 static const int kProfilerTicksBeforeOptimization = 2;
     70 // If the function optimization was disabled due to high deoptimization count,
     71 // but the function is hot and has been seen on the stack this number of times,
     72 // then we try to reenable optimization for this function.
     73 static const int kProfilerTicksBeforeReenablingOptimization = 250;
     74 // If a function does not have enough type info (according to
     75 // FLAG_type_info_threshold), but has seen a huge number of ticks,
     76 // optimize it as it is.
     77 static const int kTicksWhenNotEnoughTypeInfo = 100;
     78 // We only have one byte to store the number of ticks.
     79 STATIC_ASSERT(kProfilerTicksBeforeOptimization < 256);
     80 STATIC_ASSERT(kProfilerTicksBeforeReenablingOptimization < 256);
     81 STATIC_ASSERT(kTicksWhenNotEnoughTypeInfo < 256);
     82 
     83 // Maximum size in bytes of generate code for a function to allow OSR.
     84 static const int kOSRCodeSizeAllowanceBase =
     85     100 * FullCodeGenerator::kCodeSizeMultiplier;
     86 
     87 static const int kOSRCodeSizeAllowancePerTick =
     88     3 * FullCodeGenerator::kCodeSizeMultiplier;
     89 
     90 // Maximum size in bytes of generated code for a function to be optimized
     91 // the very first time it is seen on the stack.
     92 static const int kMaxSizeEarlyOpt =
     93     5 * FullCodeGenerator::kCodeSizeMultiplier;
     94 
     95 
     96 RuntimeProfiler::RuntimeProfiler(Isolate* isolate)
     97     : isolate_(isolate),
     98       sampler_threshold_(kSamplerThresholdInit),
     99       sampler_threshold_size_factor_(kSamplerThresholdSizeFactorInit),
    100       sampler_ticks_until_threshold_adjustment_(
    101           kSamplerTicksBetweenThresholdAdjustment),
    102       sampler_window_position_(0),
    103       any_ic_changed_(false),
    104       code_generated_(false) {
    105   ClearSampleBuffer();
    106 }
    107 
    108 
    109 static void GetICCounts(Code* shared_code,
    110                         int* ic_with_type_info_count,
    111                         int* ic_total_count,
    112                         int* percentage) {
    113   *ic_total_count = 0;
    114   *ic_with_type_info_count = 0;
    115   Object* raw_info = shared_code->type_feedback_info();
    116   if (raw_info->IsTypeFeedbackInfo()) {
    117     TypeFeedbackInfo* info = TypeFeedbackInfo::cast(raw_info);
    118     *ic_with_type_info_count = info->ic_with_type_info_count();
    119     *ic_total_count = info->ic_total_count();
    120   }
    121   *percentage = *ic_total_count > 0
    122       ? 100 * *ic_with_type_info_count / *ic_total_count
    123       : 100;
    124 }
    125 
    126 
    127 void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) {
    128   ASSERT(function->IsOptimizable());
    129 
    130   if (FLAG_trace_opt && function->PassesHydrogenFilter()) {
    131     PrintF("[marking ");
    132     function->ShortPrint();
    133     PrintF(" for recompilation, reason: %s", reason);
    134     if (FLAG_type_info_threshold > 0) {
    135       int typeinfo, total, percentage;
    136       GetICCounts(function->shared()->code(), &typeinfo, &total, &percentage);
    137       PrintF(", ICs with typeinfo: %d/%d (%d%%)", typeinfo, total, percentage);
    138     }
    139     PrintF("]\n");
    140   }
    141 
    142   if (FLAG_parallel_recompilation && !isolate_->bootstrapper()->IsActive()) {
    143     ASSERT(!function->IsMarkedForInstallingRecompiledCode());
    144     ASSERT(!function->IsInRecompileQueue());
    145     function->MarkForParallelRecompilation();
    146   } else {
    147     // The next call to the function will trigger optimization.
    148     function->MarkForLazyRecompilation();
    149   }
    150 }
    151 
    152 
    153 void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) {
    154   // See AlwaysFullCompiler (in compiler.cc) comment on why we need
    155   // Debug::has_break_points().
    156   if (!FLAG_use_osr ||
    157       isolate_->DebuggerHasBreakPoints() ||
    158       function->IsBuiltin()) {
    159     return;
    160   }
    161 
    162   SharedFunctionInfo* shared = function->shared();
    163   // If the code is not optimizable, don't try OSR.
    164   if (!shared->code()->optimizable()) return;
    165 
    166   // We are not prepared to do OSR for a function that already has an
    167   // allocated arguments object.  The optimized code would bypass it for
    168   // arguments accesses, which is unsound.  Don't try OSR.
    169   if (shared->uses_arguments()) return;
    170 
    171   // We're using on-stack replacement: patch the unoptimized code so that
    172   // any back edge in any unoptimized frame will trigger on-stack
    173   // replacement for that frame.
    174   if (FLAG_trace_osr) {
    175     PrintF("[patching back edges in ");
    176     function->PrintName();
    177     PrintF(" for on-stack replacement]\n");
    178   }
    179 
    180   // Get the interrupt stub code object to match against.  We aren't
    181   // prepared to generate it, but we don't expect to have to.
    182   Code* interrupt_code = NULL;
    183   InterruptStub interrupt_stub;
    184   bool found_code = interrupt_stub.FindCodeInCache(&interrupt_code, isolate_);
    185   if (found_code) {
    186     Code* replacement_code =
    187         isolate_->builtins()->builtin(Builtins::kOnStackReplacement);
    188     Code* unoptimized_code = shared->code();
    189     Deoptimizer::PatchInterruptCode(
    190         unoptimized_code, interrupt_code, replacement_code);
    191   }
    192 }
    193 
    194 
    195 void RuntimeProfiler::ClearSampleBuffer() {
    196   memset(sampler_window_, 0, sizeof(sampler_window_));
    197   memset(sampler_window_weight_, 0, sizeof(sampler_window_weight_));
    198 }
    199 
    200 
    201 int RuntimeProfiler::LookupSample(JSFunction* function) {
    202   int weight = 0;
    203   for (int i = 0; i < kSamplerWindowSize; i++) {
    204     Object* sample = sampler_window_[i];
    205     if (sample != NULL) {
    206       bool fits = FLAG_lookup_sample_by_shared
    207           ? (function->shared() == JSFunction::cast(sample)->shared())
    208           : (function == JSFunction::cast(sample));
    209       if (fits) {
    210         weight += sampler_window_weight_[i];
    211       }
    212     }
    213   }
    214   return weight;
    215 }
    216 
    217 
    218 void RuntimeProfiler::AddSample(JSFunction* function, int weight) {
    219   ASSERT(IsPowerOf2(kSamplerWindowSize));
    220   sampler_window_[sampler_window_position_] = function;
    221   sampler_window_weight_[sampler_window_position_] = weight;
    222   sampler_window_position_ = (sampler_window_position_ + 1) &
    223       (kSamplerWindowSize - 1);
    224 }
    225 
    226 
    227 void RuntimeProfiler::OptimizeNow() {
    228   HandleScope scope(isolate_);
    229 
    230   if (isolate_->DebuggerHasBreakPoints()) return;
    231 
    232   if (FLAG_parallel_recompilation) {
    233     // Take this as opportunity to process the optimizing compiler thread's
    234     // output queue so that it does not unnecessarily keep objects alive.
    235     isolate_->optimizing_compiler_thread()->InstallOptimizedFunctions();
    236   }
    237 
    238   // Run through the JavaScript frames and collect them. If we already
    239   // have a sample of the function, we mark it for optimizations
    240   // (eagerly or lazily).
    241   JSFunction* samples[kSamplerFrameCount];
    242   int sample_count = 0;
    243   int frame_count = 0;
    244   int frame_count_limit = FLAG_watch_ic_patching ? FLAG_frame_count
    245                                                  : kSamplerFrameCount;
    246   for (JavaScriptFrameIterator it(isolate_);
    247        frame_count++ < frame_count_limit && !it.done();
    248        it.Advance()) {
    249     JavaScriptFrame* frame = it.frame();
    250     JSFunction* function = frame->function();
    251 
    252     if (!FLAG_watch_ic_patching) {
    253       // Adjust threshold each time we have processed
    254       // a certain number of ticks.
    255       if (sampler_ticks_until_threshold_adjustment_ > 0) {
    256         sampler_ticks_until_threshold_adjustment_--;
    257         if (sampler_ticks_until_threshold_adjustment_ <= 0) {
    258           // If the threshold is not already at the minimum
    259           // modify and reset the ticks until next adjustment.
    260           if (sampler_threshold_ > kSamplerThresholdMin) {
    261             sampler_threshold_ -= kSamplerThresholdDelta;
    262             sampler_ticks_until_threshold_adjustment_ =
    263                 kSamplerTicksBetweenThresholdAdjustment;
    264           }
    265         }
    266       }
    267     }
    268 
    269     SharedFunctionInfo* shared = function->shared();
    270     Code* shared_code = shared->code();
    271 
    272     if (shared_code->kind() != Code::FUNCTION) continue;
    273     if (function->IsInRecompileQueue()) continue;
    274 
    275     if (FLAG_always_osr &&
    276         shared_code->allow_osr_at_loop_nesting_level() == 0) {
    277       // Testing mode: always try an OSR compile for every function.
    278       for (int i = 0; i < Code::kMaxLoopNestingMarker; i++) {
    279         // TODO(titzer): fix AttemptOnStackReplacement to avoid this dumb loop.
    280         shared_code->set_allow_osr_at_loop_nesting_level(i);
    281         AttemptOnStackReplacement(function);
    282       }
    283       // Fall through and do a normal optimized compile as well.
    284     } else if (!frame->is_optimized() &&
    285         (function->IsMarkedForLazyRecompilation() ||
    286          function->IsMarkedForParallelRecompilation() ||
    287          function->IsOptimized())) {
    288       // Attempt OSR if we are still running unoptimized code even though the
    289       // the function has long been marked or even already been optimized.
    290       int ticks = shared_code->profiler_ticks();
    291       int allowance = kOSRCodeSizeAllowanceBase +
    292                       ticks * kOSRCodeSizeAllowancePerTick;
    293       if (shared_code->CodeSize() > allowance) {
    294         if (ticks < 255) shared_code->set_profiler_ticks(ticks + 1);
    295       } else {
    296         int nesting = shared_code->allow_osr_at_loop_nesting_level();
    297         if (nesting < Code::kMaxLoopNestingMarker) {
    298           int new_nesting = nesting + 1;
    299           shared_code->set_allow_osr_at_loop_nesting_level(new_nesting);
    300           AttemptOnStackReplacement(function);
    301         }
    302       }
    303       continue;
    304     }
    305 
    306     // Only record top-level code on top of the execution stack and
    307     // avoid optimizing excessively large scripts since top-level code
    308     // will be executed only once.
    309     const int kMaxToplevelSourceSize = 10 * 1024;
    310     if (shared->is_toplevel() &&
    311         (frame_count > 1 || shared->SourceSize() > kMaxToplevelSourceSize)) {
    312       continue;
    313     }
    314 
    315     // Do not record non-optimizable functions.
    316     if (shared->optimization_disabled()) {
    317       if (shared->deopt_count() >= FLAG_max_opt_count) {
    318         // If optimization was disabled due to many deoptimizations,
    319         // then check if the function is hot and try to reenable optimization.
    320         int ticks = shared_code->profiler_ticks();
    321         if (ticks >= kProfilerTicksBeforeReenablingOptimization) {
    322           shared_code->set_profiler_ticks(0);
    323           shared->TryReenableOptimization();
    324         } else {
    325           shared_code->set_profiler_ticks(ticks + 1);
    326         }
    327       }
    328       continue;
    329     }
    330     if (!function->IsOptimizable()) continue;
    331 
    332     if (FLAG_watch_ic_patching) {
    333       int ticks = shared_code->profiler_ticks();
    334 
    335       if (ticks >= kProfilerTicksBeforeOptimization) {
    336         int typeinfo, total, percentage;
    337         GetICCounts(shared_code, &typeinfo, &total, &percentage);
    338         if (percentage >= FLAG_type_info_threshold) {
    339           // If this particular function hasn't had any ICs patched for enough
    340           // ticks, optimize it now.
    341           Optimize(function, "hot and stable");
    342         } else if (ticks >= kTicksWhenNotEnoughTypeInfo) {
    343           Optimize(function, "not much type info but very hot");
    344         } else {
    345           shared_code->set_profiler_ticks(ticks + 1);
    346           if (FLAG_trace_opt_verbose) {
    347             PrintF("[not yet optimizing ");
    348             function->PrintName();
    349             PrintF(", not enough type info: %d/%d (%d%%)]\n",
    350                    typeinfo, total, percentage);
    351           }
    352         }
    353       } else if (!any_ic_changed_ &&
    354                  shared_code->instruction_size() < kMaxSizeEarlyOpt) {
    355         // If no IC was patched since the last tick and this function is very
    356         // small, optimistically optimize it now.
    357         Optimize(function, "small function");
    358       } else {
    359         shared_code->set_profiler_ticks(ticks + 1);
    360       }
    361     } else {  // !FLAG_watch_ic_patching
    362       samples[sample_count++] = function;
    363 
    364       int function_size = function->shared()->SourceSize();
    365       int threshold_size_factor = (function_size > kSizeLimit)
    366           ? sampler_threshold_size_factor_
    367           : 1;
    368 
    369       int threshold = sampler_threshold_ * threshold_size_factor;
    370 
    371       if (LookupSample(function) >= threshold) {
    372         Optimize(function, "sampler window lookup");
    373       }
    374     }
    375   }
    376   if (FLAG_watch_ic_patching) {
    377     any_ic_changed_ = false;
    378   } else {  // !FLAG_watch_ic_patching
    379     // Add the collected functions as samples. It's important not to do
    380     // this as part of collecting them because this will interfere with
    381     // the sample lookup in case of recursive functions.
    382     for (int i = 0; i < sample_count; i++) {
    383       AddSample(samples[i], kSamplerFrameWeight[i]);
    384     }
    385   }
    386 }
    387 
    388 
    389 void RuntimeProfiler::SetUp() {
    390   if (!FLAG_watch_ic_patching) {
    391     ClearSampleBuffer();
    392   }
    393 }
    394 
    395 
    396 void RuntimeProfiler::Reset() {
    397   if (!FLAG_watch_ic_patching) {
    398     sampler_threshold_ = kSamplerThresholdInit;
    399     sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit;
    400     sampler_ticks_until_threshold_adjustment_ =
    401         kSamplerTicksBetweenThresholdAdjustment;
    402   }
    403 }
    404 
    405 
    406 void RuntimeProfiler::TearDown() {
    407   // Nothing to do.
    408 }
    409 
    410 
    411 // Update the pointers in the sampler window after a GC.
    412 void RuntimeProfiler::UpdateSamplesAfterScavenge() {
    413   for (int i = 0; i < kSamplerWindowSize; i++) {
    414     Object* function = sampler_window_[i];
    415     if (function != NULL && isolate_->heap()->InNewSpace(function)) {
    416       MapWord map_word = HeapObject::cast(function)->map_word();
    417       if (map_word.IsForwardingAddress()) {
    418         sampler_window_[i] = map_word.ToForwardingAddress();
    419       } else {
    420         sampler_window_[i] = NULL;
    421       }
    422     }
    423   }
    424 }
    425 
    426 
    427 void RuntimeProfiler::RemoveDeadSamples() {
    428   for (int i = 0; i < kSamplerWindowSize; i++) {
    429     Object* function = sampler_window_[i];
    430     if (function != NULL &&
    431         !Marking::MarkBitFrom(HeapObject::cast(function)).Get()) {
    432       sampler_window_[i] = NULL;
    433     }
    434   }
    435 }
    436 
    437 
    438 void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) {
    439   for (int i = 0; i < kSamplerWindowSize; i++) {
    440     visitor->VisitPointer(&sampler_window_[i]);
    441   }
    442 }
    443 
    444 
    445 } }  // namespace v8::internal
    446