• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2010 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 "code-stubs.h"
34 #include "compilation-cache.h"
35 #include "deoptimizer.h"
36 #include "execution.h"
37 #include "global-handles.h"
38 #include "mark-compact.h"
39 #include "platform.h"
40 #include "scopeinfo.h"
41 
42 namespace v8 {
43 namespace internal {
44 
45 
46 class PendingListNode : public Malloced {
47  public:
48   explicit PendingListNode(JSFunction* function);
~PendingListNode()49   ~PendingListNode() { Destroy(); }
50 
next() const51   PendingListNode* next() const { return next_; }
set_next(PendingListNode * node)52   void set_next(PendingListNode* node) { next_ = node; }
function()53   Handle<JSFunction> function() { return Handle<JSFunction>::cast(function_); }
54 
55   // If the function is garbage collected before we've had the chance
56   // to optimize it the weak handle will be null.
IsValid()57   bool IsValid() { return !function_.is_null(); }
58 
59   // Returns the number of microseconds this node has been pending.
Delay() const60   int Delay() const { return static_cast<int>(OS::Ticks() - start_); }
61 
62  private:
63   void Destroy();
64   static void WeakCallback(v8::Persistent<v8::Value> object, void* data);
65 
66   PendingListNode* next_;
67   Handle<Object> function_;  // Weak handle.
68   int64_t start_;
69 };
70 
71 
72 // Optimization sampler constants.
73 static const int kSamplerFrameCount = 2;
74 static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 };
75 
76 static const int kSamplerTicksBetweenThresholdAdjustment = 32;
77 
78 static const int kSamplerThresholdInit = 3;
79 static const int kSamplerThresholdMin = 1;
80 static const int kSamplerThresholdDelta = 1;
81 
82 static const int kSamplerThresholdSizeFactorInit = 3;
83 static const int kSamplerThresholdSizeFactorMin = 1;
84 static const int kSamplerThresholdSizeFactorDelta = 1;
85 
86 static const int kSizeLimit = 1500;
87 
88 
PendingListNode(JSFunction * function)89 PendingListNode::PendingListNode(JSFunction* function) : next_(NULL) {
90   GlobalHandles* global_handles = Isolate::Current()->global_handles();
91   function_ = global_handles->Create(function);
92   start_ = OS::Ticks();
93   global_handles->MakeWeak(function_.location(), this, &WeakCallback);
94 }
95 
96 
Destroy()97 void PendingListNode::Destroy() {
98   if (!IsValid()) return;
99   GlobalHandles* global_handles = Isolate::Current()->global_handles();
100   global_handles->Destroy(function_.location());
101   function_= Handle<Object>::null();
102 }
103 
104 
WeakCallback(v8::Persistent<v8::Value>,void * data)105 void PendingListNode::WeakCallback(v8::Persistent<v8::Value>, void* data) {
106   reinterpret_cast<PendingListNode*>(data)->Destroy();
107 }
108 
109 
110 Atomic32 RuntimeProfiler::state_ = 0;
111 // TODO(isolates): Create the semaphore lazily and clean it up when no
112 // longer required.
113 #ifdef ENABLE_LOGGING_AND_PROFILING
114 Semaphore* RuntimeProfiler::semaphore_ = OS::CreateSemaphore(0);
115 #endif
116 
117 #ifdef DEBUG
118 bool RuntimeProfiler::has_been_globally_setup_ = false;
119 #endif
120 bool RuntimeProfiler::enabled_ = false;
121 
122 
RuntimeProfiler(Isolate * isolate)123 RuntimeProfiler::RuntimeProfiler(Isolate* isolate)
124     : isolate_(isolate),
125       sampler_threshold_(kSamplerThresholdInit),
126       sampler_threshold_size_factor_(kSamplerThresholdSizeFactorInit),
127       sampler_ticks_until_threshold_adjustment_(
128         kSamplerTicksBetweenThresholdAdjustment),
129       js_ratio_(0),
130       sampler_window_position_(0),
131       optimize_soon_list_(NULL),
132       state_window_position_(0),
133       state_window_ticks_(0) {
134   state_counts_[IN_NON_JS_STATE] = kStateWindowSize;
135   state_counts_[IN_JS_STATE] = 0;
136   STATIC_ASSERT(IN_NON_JS_STATE == 0);
137   memset(state_window_, 0, sizeof(state_window_));
138   ClearSampleBuffer();
139 }
140 
141 
GlobalSetup()142 void RuntimeProfiler::GlobalSetup() {
143   ASSERT(!has_been_globally_setup_);
144   enabled_ = V8::UseCrankshaft() && FLAG_opt;
145 #ifdef DEBUG
146   has_been_globally_setup_ = true;
147 #endif
148 }
149 
150 
Optimize(JSFunction * function,bool eager,int delay)151 void RuntimeProfiler::Optimize(JSFunction* function, bool eager, int delay) {
152   ASSERT(function->IsOptimizable());
153   if (FLAG_trace_opt) {
154     PrintF("[marking (%s) ", eager ? "eagerly" : "lazily");
155     function->PrintName();
156     PrintF(" for recompilation");
157     if (delay > 0) {
158       PrintF(" (delayed %0.3f ms)", static_cast<double>(delay) / 1000);
159     }
160     PrintF("]\n");
161   }
162 
163   // The next call to the function will trigger optimization.
164   function->MarkForLazyRecompilation();
165 }
166 
167 
AttemptOnStackReplacement(JSFunction * function)168 void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) {
169   // See AlwaysFullCompiler (in compiler.cc) comment on why we need
170   // Debug::has_break_points().
171   ASSERT(function->IsMarkedForLazyRecompilation());
172   if (!FLAG_use_osr ||
173       isolate_->debug()->has_break_points() ||
174       function->IsBuiltin()) {
175     return;
176   }
177 
178   SharedFunctionInfo* shared = function->shared();
179   // If the code is not optimizable or references context slots, don't try OSR.
180   if (!shared->code()->optimizable() || !shared->allows_lazy_compilation()) {
181     return;
182   }
183 
184   // We are not prepared to do OSR for a function that already has an
185   // allocated arguments object.  The optimized code would bypass it for
186   // arguments accesses, which is unsound.  Don't try OSR.
187   if (shared->scope_info()->HasArgumentsShadow()) return;
188 
189   // We're using on-stack replacement: patch the unoptimized code so that
190   // any back edge in any unoptimized frame will trigger on-stack
191   // replacement for that frame.
192   if (FLAG_trace_osr) {
193     PrintF("[patching stack checks in ");
194     function->PrintName();
195     PrintF(" for on-stack replacement]\n");
196   }
197 
198   // Get the stack check stub code object to match against.  We aren't
199   // prepared to generate it, but we don't expect to have to.
200   StackCheckStub check_stub;
201   Object* check_code;
202   MaybeObject* maybe_check_code = check_stub.TryGetCode();
203   if (maybe_check_code->ToObject(&check_code)) {
204     Code* replacement_code =
205         isolate_->builtins()->builtin(Builtins::kOnStackReplacement);
206     Code* unoptimized_code = shared->code();
207     Deoptimizer::PatchStackCheckCode(unoptimized_code,
208                                      Code::cast(check_code),
209                                      replacement_code);
210   }
211 }
212 
213 
ClearSampleBuffer()214 void RuntimeProfiler::ClearSampleBuffer() {
215   memset(sampler_window_, 0, sizeof(sampler_window_));
216   memset(sampler_window_weight_, 0, sizeof(sampler_window_weight_));
217 }
218 
219 
LookupSample(JSFunction * function)220 int RuntimeProfiler::LookupSample(JSFunction* function) {
221   int weight = 0;
222   for (int i = 0; i < kSamplerWindowSize; i++) {
223     Object* sample = sampler_window_[i];
224     if (sample != NULL) {
225       if (function == sample) {
226         weight += sampler_window_weight_[i];
227       }
228     }
229   }
230   return weight;
231 }
232 
233 
AddSample(JSFunction * function,int weight)234 void RuntimeProfiler::AddSample(JSFunction* function, int weight) {
235   ASSERT(IsPowerOf2(kSamplerWindowSize));
236   sampler_window_[sampler_window_position_] = function;
237   sampler_window_weight_[sampler_window_position_] = weight;
238   sampler_window_position_ = (sampler_window_position_ + 1) &
239       (kSamplerWindowSize - 1);
240 }
241 
242 
OptimizeNow()243 void RuntimeProfiler::OptimizeNow() {
244   HandleScope scope(isolate_);
245   PendingListNode* current = optimize_soon_list_;
246   while (current != NULL) {
247     PendingListNode* next = current->next();
248     if (current->IsValid()) {
249       Handle<JSFunction> function = current->function();
250       int delay = current->Delay();
251       if (function->IsOptimizable()) {
252         Optimize(*function, true, delay);
253       }
254     }
255     delete current;
256     current = next;
257   }
258   optimize_soon_list_ = NULL;
259 
260   // Run through the JavaScript frames and collect them. If we already
261   // have a sample of the function, we mark it for optimizations
262   // (eagerly or lazily).
263   JSFunction* samples[kSamplerFrameCount];
264   int sample_count = 0;
265   int frame_count = 0;
266   for (JavaScriptFrameIterator it(isolate_);
267        frame_count++ < kSamplerFrameCount && !it.done();
268        it.Advance()) {
269     JavaScriptFrame* frame = it.frame();
270     JSFunction* function = JSFunction::cast(frame->function());
271 
272     // Adjust threshold each time we have processed
273     // a certain number of ticks.
274     if (sampler_ticks_until_threshold_adjustment_ > 0) {
275       sampler_ticks_until_threshold_adjustment_--;
276       if (sampler_ticks_until_threshold_adjustment_ <= 0) {
277         // If the threshold is not already at the minimum
278         // modify and reset the ticks until next adjustment.
279         if (sampler_threshold_ > kSamplerThresholdMin) {
280           sampler_threshold_ -= kSamplerThresholdDelta;
281           sampler_ticks_until_threshold_adjustment_ =
282               kSamplerTicksBetweenThresholdAdjustment;
283         }
284       }
285     }
286 
287     if (function->IsMarkedForLazyRecompilation()) {
288       Code* unoptimized = function->shared()->code();
289       int nesting = unoptimized->allow_osr_at_loop_nesting_level();
290       if (nesting == 0) AttemptOnStackReplacement(function);
291       int new_nesting = Min(nesting + 1, Code::kMaxLoopNestingMarker);
292       unoptimized->set_allow_osr_at_loop_nesting_level(new_nesting);
293     }
294 
295     // Do not record non-optimizable functions.
296     if (!function->IsOptimizable()) continue;
297     samples[sample_count++] = function;
298 
299     int function_size = function->shared()->SourceSize();
300     int threshold_size_factor = (function_size > kSizeLimit)
301         ? sampler_threshold_size_factor_
302         : 1;
303 
304     int threshold = sampler_threshold_ * threshold_size_factor;
305     int current_js_ratio = NoBarrier_Load(&js_ratio_);
306 
307     // Adjust threshold depending on the ratio of time spent
308     // in JS code.
309     if (current_js_ratio < 20) {
310       // If we spend less than 20% of the time in JS code,
311       // do not optimize.
312       continue;
313     } else if (current_js_ratio < 75) {
314       // Below 75% of time spent in JS code, only optimize very
315       // frequently used functions.
316       threshold *= 3;
317     }
318 
319     if (LookupSample(function) >= threshold) {
320       Optimize(function, false, 0);
321       isolate_->compilation_cache()->MarkForEagerOptimizing(
322           Handle<JSFunction>(function));
323     }
324   }
325 
326   // Add the collected functions as samples. It's important not to do
327   // this as part of collecting them because this will interfere with
328   // the sample lookup in case of recursive functions.
329   for (int i = 0; i < sample_count; i++) {
330     AddSample(samples[i], kSamplerFrameWeight[i]);
331   }
332 }
333 
334 
OptimizeSoon(JSFunction * function)335 void RuntimeProfiler::OptimizeSoon(JSFunction* function) {
336   if (!function->IsOptimizable()) return;
337   PendingListNode* node = new PendingListNode(function);
338   node->set_next(optimize_soon_list_);
339   optimize_soon_list_ = node;
340 }
341 
342 
343 #ifdef ENABLE_LOGGING_AND_PROFILING
UpdateStateRatio(SamplerState current_state)344 void RuntimeProfiler::UpdateStateRatio(SamplerState current_state) {
345   SamplerState old_state = state_window_[state_window_position_];
346   state_counts_[old_state]--;
347   state_window_[state_window_position_] = current_state;
348   state_counts_[current_state]++;
349   ASSERT(IsPowerOf2(kStateWindowSize));
350   state_window_position_ = (state_window_position_ + 1) &
351       (kStateWindowSize - 1);
352   // Note: to calculate correct ratio we have to track how many valid
353   // ticks are actually in the state window, because on profiler
354   // startup this number can be less than the window size.
355   state_window_ticks_ = Min(kStateWindowSize, state_window_ticks_ + 1);
356   NoBarrier_Store(&js_ratio_, state_counts_[IN_JS_STATE] * 100 /
357                   state_window_ticks_);
358 }
359 #endif
360 
361 
NotifyTick()362 void RuntimeProfiler::NotifyTick() {
363 #ifdef ENABLE_LOGGING_AND_PROFILING
364   // Record state sample.
365   SamplerState state = IsSomeIsolateInJS()
366       ? IN_JS_STATE
367       : IN_NON_JS_STATE;
368   UpdateStateRatio(state);
369   isolate_->stack_guard()->RequestRuntimeProfilerTick();
370 #endif
371 }
372 
373 
Setup()374 void RuntimeProfiler::Setup() {
375   ASSERT(has_been_globally_setup_);
376   ClearSampleBuffer();
377   // If the ticker hasn't already started, make sure to do so to get
378   // the ticks for the runtime profiler.
379   if (IsEnabled()) isolate_->logger()->EnsureTickerStarted();
380 }
381 
382 
Reset()383 void RuntimeProfiler::Reset() {
384   sampler_threshold_ = kSamplerThresholdInit;
385   sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit;
386   sampler_ticks_until_threshold_adjustment_ =
387       kSamplerTicksBetweenThresholdAdjustment;
388 }
389 
390 
TearDown()391 void RuntimeProfiler::TearDown() {
392   // Nothing to do.
393 }
394 
395 
SamplerWindowSize()396 int RuntimeProfiler::SamplerWindowSize() {
397   return kSamplerWindowSize;
398 }
399 
400 
401 // Update the pointers in the sampler window after a GC.
UpdateSamplesAfterScavenge()402 void RuntimeProfiler::UpdateSamplesAfterScavenge() {
403   for (int i = 0; i < kSamplerWindowSize; i++) {
404     Object* function = sampler_window_[i];
405     if (function != NULL && isolate_->heap()->InNewSpace(function)) {
406       MapWord map_word = HeapObject::cast(function)->map_word();
407       if (map_word.IsForwardingAddress()) {
408         sampler_window_[i] = map_word.ToForwardingAddress();
409       } else {
410         sampler_window_[i] = NULL;
411       }
412     }
413   }
414 }
415 
416 
HandleWakeUp(Isolate * isolate)417 void RuntimeProfiler::HandleWakeUp(Isolate* isolate) {
418 #ifdef ENABLE_LOGGING_AND_PROFILING
419   // The profiler thread must still be waiting.
420   ASSERT(NoBarrier_Load(&state_) >= 0);
421   // In IsolateEnteredJS we have already incremented the counter and
422   // undid the decrement done by the profiler thread. Increment again
423   // to get the right count of active isolates.
424   NoBarrier_AtomicIncrement(&state_, 1);
425   semaphore_->Signal();
426   isolate->ResetEagerOptimizingData();
427 #endif
428 }
429 
430 
IsSomeIsolateInJS()431 bool RuntimeProfiler::IsSomeIsolateInJS() {
432   return NoBarrier_Load(&state_) > 0;
433 }
434 
435 
WaitForSomeIsolateToEnterJS()436 bool RuntimeProfiler::WaitForSomeIsolateToEnterJS() {
437 #ifdef ENABLE_LOGGING_AND_PROFILING
438   Atomic32 old_state = NoBarrier_CompareAndSwap(&state_, 0, -1);
439   ASSERT(old_state >= -1);
440   if (old_state != 0) return false;
441   semaphore_->Wait();
442 #endif
443   return true;
444 }
445 
446 
WakeUpRuntimeProfilerThreadBeforeShutdown()447 void RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown() {
448 #ifdef ENABLE_LOGGING_AND_PROFILING
449   semaphore_->Signal();
450 #endif
451 }
452 
453 
RemoveDeadSamples()454 void RuntimeProfiler::RemoveDeadSamples() {
455   for (int i = 0; i < kSamplerWindowSize; i++) {
456     Object* function = sampler_window_[i];
457     if (function != NULL && !HeapObject::cast(function)->IsMarked()) {
458       sampler_window_[i] = NULL;
459     }
460   }
461 }
462 
463 
UpdateSamplesAfterCompact(ObjectVisitor * visitor)464 void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) {
465   for (int i = 0; i < kSamplerWindowSize; i++) {
466     visitor->VisitPointer(&sampler_window_[i]);
467   }
468 }
469 
470 
SuspendIfNecessary()471 bool RuntimeProfilerRateLimiter::SuspendIfNecessary() {
472 #ifdef ENABLE_LOGGING_AND_PROFILING
473   static const int kNonJSTicksThreshold = 100;
474   if (RuntimeProfiler::IsSomeIsolateInJS()) {
475     non_js_ticks_ = 0;
476   } else {
477     if (non_js_ticks_ < kNonJSTicksThreshold) {
478       ++non_js_ticks_;
479     } else {
480       return RuntimeProfiler::WaitForSomeIsolateToEnterJS();
481     }
482   }
483 #endif
484   return false;
485 }
486 
487 
488 } }  // namespace v8::internal
489