v8  3.14.5(node0.10.28)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
runtime-profiler.cc
Go to the documentation of this file.
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 "code-stubs.h"
34 #include "compilation-cache.h"
35 #include "deoptimizer.h"
36 #include "execution.h"
37 #include "full-codegen.h"
38 #include "global-handles.h"
39 #include "isolate-inl.h"
40 #include "mark-compact.h"
41 #include "platform.h"
42 #include "scopeinfo.h"
43 
44 namespace v8 {
45 namespace internal {
46 
47 
48 // Optimization sampler constants.
49 static const int kSamplerFrameCount = 2;
50 
51 // Constants for statistical profiler.
52 static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 };
53 
54 static const int kSamplerTicksBetweenThresholdAdjustment = 32;
55 
56 static const int kSamplerThresholdInit = 3;
57 static const int kSamplerThresholdMin = 1;
58 static const int kSamplerThresholdDelta = 1;
59 
60 static const int kSamplerThresholdSizeFactorInit = 3;
61 
62 static const int kSizeLimit = 1500;
63 
64 // Constants for counter based profiler.
65 
66 // Number of times a function has to be seen on the stack before it is
67 // optimized.
68 static const int kProfilerTicksBeforeOptimization = 2;
69 // If the function optimization was disabled due to high deoptimization count,
70 // but the function is hot and has been seen on the stack this number of times,
71 // then we try to reenable optimization for this function.
72 static const int kProfilerTicksBeforeReenablingOptimization = 250;
73 // If a function does not have enough type info (according to
74 // FLAG_type_info_threshold), but has seen a huge number of ticks,
75 // optimize it as it is.
76 static const int kTicksWhenNotEnoughTypeInfo = 100;
77 // We only have one byte to store the number of ticks.
78 STATIC_ASSERT(kProfilerTicksBeforeOptimization < 256);
79 STATIC_ASSERT(kProfilerTicksBeforeReenablingOptimization < 256);
80 STATIC_ASSERT(kTicksWhenNotEnoughTypeInfo < 256);
81 
82 
83 // Maximum size in bytes of generated code for a function to be optimized
84 // the very first time it is seen on the stack.
85 static const int kMaxSizeEarlyOpt =
86  5 * FullCodeGenerator::kBackEdgeDistanceUnit;
87 
88 
89 Atomic32 RuntimeProfiler::state_ = 0;
90 
91 // TODO(isolates): Clean up the semaphore when it is no longer required.
92 static LazySemaphore<0>::type semaphore = LAZY_SEMAPHORE_INITIALIZER;
93 
94 #ifdef DEBUG
95 bool RuntimeProfiler::has_been_globally_set_up_ = false;
96 #endif
97 bool RuntimeProfiler::enabled_ = false;
98 
99 
101  : isolate_(isolate),
102  sampler_threshold_(kSamplerThresholdInit),
103  sampler_threshold_size_factor_(kSamplerThresholdSizeFactorInit),
104  sampler_ticks_until_threshold_adjustment_(
105  kSamplerTicksBetweenThresholdAdjustment),
106  sampler_window_position_(0),
107  any_ic_changed_(false),
108  code_generated_(false) {
109  ClearSampleBuffer();
110 }
111 
112 
114  ASSERT(!has_been_globally_set_up_);
115  enabled_ = V8::UseCrankshaft() && FLAG_opt;
116 #ifdef DEBUG
117  has_been_globally_set_up_ = true;
118 #endif
119 }
120 
121 
122 static void GetICCounts(JSFunction* function,
123  int* ic_with_type_info_count,
124  int* ic_total_count,
125  int* percentage) {
126  *ic_total_count = 0;
127  *ic_with_type_info_count = 0;
128  Object* raw_info =
129  function->shared()->code()->type_feedback_info();
130  if (raw_info->IsTypeFeedbackInfo()) {
131  TypeFeedbackInfo* info = TypeFeedbackInfo::cast(raw_info);
132  *ic_with_type_info_count = info->ic_with_type_info_count();
133  *ic_total_count = info->ic_total_count();
134  }
135  *percentage = *ic_total_count > 0
136  ? 100 * *ic_with_type_info_count / *ic_total_count
137  : 100;
138 }
139 
140 
141 void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) {
142  ASSERT(function->IsOptimizable());
143  if (FLAG_trace_opt) {
144  PrintF("[marking ");
145  function->PrintName();
146  PrintF(" 0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(function->address()));
147  PrintF(" for recompilation, reason: %s", reason);
148  if (FLAG_type_info_threshold > 0) {
149  int typeinfo, total, percentage;
150  GetICCounts(function, &typeinfo, &total, &percentage);
151  PrintF(", ICs with typeinfo: %d/%d (%d%%)", typeinfo, total, percentage);
152  }
153  PrintF("]\n");
154  }
155 
156  if (FLAG_parallel_recompilation) {
157  function->MarkForParallelRecompilation();
158  } else {
159  // The next call to the function will trigger optimization.
160  function->MarkForLazyRecompilation();
161  }
162 }
163 
164 
166  // See AlwaysFullCompiler (in compiler.cc) comment on why we need
167  // Debug::has_break_points().
168  ASSERT(function->IsMarkedForLazyRecompilation() ||
169  function->IsMarkedForParallelRecompilation());
170  if (!FLAG_use_osr ||
171  isolate_->DebuggerHasBreakPoints() ||
172  function->IsBuiltin()) {
173  return;
174  }
175 
176  SharedFunctionInfo* shared = function->shared();
177  // If the code is not optimizable, don't try OSR.
178  if (!shared->code()->optimizable()) return;
179 
180  // We are not prepared to do OSR for a function that already has an
181  // allocated arguments object. The optimized code would bypass it for
182  // arguments accesses, which is unsound. Don't try OSR.
183  if (shared->uses_arguments()) return;
184 
185  // We're using on-stack replacement: patch the unoptimized code so that
186  // any back edge in any unoptimized frame will trigger on-stack
187  // replacement for that frame.
188  if (FLAG_trace_osr) {
189  PrintF("[patching stack checks in ");
190  function->PrintName();
191  PrintF(" for on-stack replacement]\n");
192  }
193 
194  // Get the stack check stub code object to match against. We aren't
195  // prepared to generate it, but we don't expect to have to.
196  bool found_code = false;
197  Code* stack_check_code = NULL;
198  if (FLAG_count_based_interrupts) {
199  InterruptStub interrupt_stub;
200  found_code = interrupt_stub.FindCodeInCache(&stack_check_code);
201  } else // NOLINT
202  { // NOLINT
203  StackCheckStub check_stub;
204  found_code = check_stub.FindCodeInCache(&stack_check_code);
205  }
206  if (found_code) {
207  Code* replacement_code =
208  isolate_->builtins()->builtin(Builtins::kOnStackReplacement);
209  Code* unoptimized_code = shared->code();
210  Deoptimizer::PatchStackCheckCode(unoptimized_code,
211  stack_check_code,
212  replacement_code);
213  }
214 }
215 
216 
217 void RuntimeProfiler::ClearSampleBuffer() {
218  memset(sampler_window_, 0, sizeof(sampler_window_));
219  memset(sampler_window_weight_, 0, sizeof(sampler_window_weight_));
220 }
221 
222 
223 int RuntimeProfiler::LookupSample(JSFunction* function) {
224  int weight = 0;
225  for (int i = 0; i < kSamplerWindowSize; i++) {
226  Object* sample = sampler_window_[i];
227  if (sample != NULL) {
228  bool fits = FLAG_lookup_sample_by_shared
229  ? (function->shared() == JSFunction::cast(sample)->shared())
230  : (function == JSFunction::cast(sample));
231  if (fits) {
232  weight += sampler_window_weight_[i];
233  }
234  }
235  }
236  return weight;
237 }
238 
239 
240 void RuntimeProfiler::AddSample(JSFunction* function, int weight) {
241  ASSERT(IsPowerOf2(kSamplerWindowSize));
242  sampler_window_[sampler_window_position_] = function;
243  sampler_window_weight_[sampler_window_position_] = weight;
244  sampler_window_position_ = (sampler_window_position_ + 1) &
245  (kSamplerWindowSize - 1);
246 }
247 
248 
250  HandleScope scope(isolate_);
251 
252  // Run through the JavaScript frames and collect them. If we already
253  // have a sample of the function, we mark it for optimizations
254  // (eagerly or lazily).
255  JSFunction* samples[kSamplerFrameCount];
256  int sample_count = 0;
257  int frame_count = 0;
258  int frame_count_limit = FLAG_watch_ic_patching ? FLAG_frame_count
259  : kSamplerFrameCount;
260  for (JavaScriptFrameIterator it(isolate_);
261  frame_count++ < frame_count_limit && !it.done();
262  it.Advance()) {
263  JavaScriptFrame* frame = it.frame();
264  JSFunction* function = JSFunction::cast(frame->function());
265 
266  if (!FLAG_watch_ic_patching) {
267  // Adjust threshold each time we have processed
268  // a certain number of ticks.
269  if (sampler_ticks_until_threshold_adjustment_ > 0) {
270  sampler_ticks_until_threshold_adjustment_--;
271  if (sampler_ticks_until_threshold_adjustment_ <= 0) {
272  // If the threshold is not already at the minimum
273  // modify and reset the ticks until next adjustment.
274  if (sampler_threshold_ > kSamplerThresholdMin) {
275  sampler_threshold_ -= kSamplerThresholdDelta;
276  sampler_ticks_until_threshold_adjustment_ =
277  kSamplerTicksBetweenThresholdAdjustment;
278  }
279  }
280  }
281  }
282 
283  SharedFunctionInfo* shared = function->shared();
284  Code* shared_code = shared->code();
285 
286  if (shared_code->kind() != Code::FUNCTION) continue;
287 
288  if (function->IsMarkedForLazyRecompilation() ||
289  function->IsMarkedForParallelRecompilation()) {
290  int nesting = shared_code->allow_osr_at_loop_nesting_level();
291  if (nesting == 0) AttemptOnStackReplacement(function);
292  int new_nesting = Min(nesting + 1, Code::kMaxLoopNestingMarker);
293  shared_code->set_allow_osr_at_loop_nesting_level(new_nesting);
294  }
295 
296  // Only record top-level code on top of the execution stack and
297  // avoid optimizing excessively large scripts since top-level code
298  // will be executed only once.
299  const int kMaxToplevelSourceSize = 10 * 1024;
300  if (shared->is_toplevel() &&
301  (frame_count > 1 || shared->SourceSize() > kMaxToplevelSourceSize)) {
302  continue;
303  }
304 
305  // Do not record non-optimizable functions.
306  if (shared->optimization_disabled()) {
307  if (shared->deopt_count() >= FLAG_max_opt_count) {
308  // If optimization was disabled due to many deoptimizations,
309  // then check if the function is hot and try to reenable optimization.
310  int ticks = shared_code->profiler_ticks();
311  if (ticks >= kProfilerTicksBeforeReenablingOptimization) {
312  shared_code->set_profiler_ticks(0);
313  shared->TryReenableOptimization();
314  } else {
315  shared_code->set_profiler_ticks(ticks + 1);
316  }
317  }
318  continue;
319  }
320  if (!function->IsOptimizable()) continue;
321 
322  if (FLAG_watch_ic_patching) {
323  int ticks = shared_code->profiler_ticks();
324 
325  if (ticks >= kProfilerTicksBeforeOptimization) {
326  int typeinfo, total, percentage;
327  GetICCounts(function, &typeinfo, &total, &percentage);
328  if (percentage >= FLAG_type_info_threshold) {
329  // If this particular function hasn't had any ICs patched for enough
330  // ticks, optimize it now.
331  Optimize(function, "hot and stable");
332  } else if (ticks >= kTicksWhenNotEnoughTypeInfo) {
333  Optimize(function, "not much type info but very hot");
334  } else {
335  shared_code->set_profiler_ticks(ticks + 1);
336  if (FLAG_trace_opt_verbose) {
337  PrintF("[not yet optimizing ");
338  function->PrintName();
339  PrintF(", not enough type info: %d/%d (%d%%)]\n",
340  typeinfo, total, percentage);
341  }
342  }
343  } else if (!any_ic_changed_ &&
344  shared_code->instruction_size() < kMaxSizeEarlyOpt) {
345  // If no IC was patched since the last tick and this function is very
346  // small, optimistically optimize it now.
347  Optimize(function, "small function");
348  } else {
349  shared_code->set_profiler_ticks(ticks + 1);
350  }
351  } else { // !FLAG_watch_ic_patching
352  samples[sample_count++] = function;
353 
354  int function_size = function->shared()->SourceSize();
355  int threshold_size_factor = (function_size > kSizeLimit)
356  ? sampler_threshold_size_factor_
357  : 1;
358 
359  int threshold = sampler_threshold_ * threshold_size_factor;
360 
361  if (LookupSample(function) >= threshold) {
362  Optimize(function, "sampler window lookup");
363  }
364  }
365  }
366  if (FLAG_watch_ic_patching) {
367  any_ic_changed_ = false;
368  } else { // !FLAG_watch_ic_patching
369  // Add the collected functions as samples. It's important not to do
370  // this as part of collecting them because this will interfere with
371  // the sample lookup in case of recursive functions.
372  for (int i = 0; i < sample_count; i++) {
373  AddSample(samples[i], kSamplerFrameWeight[i]);
374  }
375  }
376 }
377 
378 
380  if (FLAG_count_based_interrupts) return;
382 }
383 
384 
386  ASSERT(has_been_globally_set_up_);
387  if (!FLAG_watch_ic_patching) {
388  ClearSampleBuffer();
389  }
390  // If the ticker hasn't already started, make sure to do so to get
391  // the ticks for the runtime profiler.
392  if (IsEnabled()) isolate_->logger()->EnsureTickerStarted();
393 }
394 
395 
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 
407  // Nothing to do.
408 }
409 
410 
412  return kSamplerWindowSize;
413 }
414 
415 
416 // Update the pointers in the sampler window after a GC.
418  for (int i = 0; i < kSamplerWindowSize; i++) {
419  Object* function = sampler_window_[i];
420  if (function != NULL && isolate_->heap()->InNewSpace(function)) {
421  MapWord map_word = HeapObject::cast(function)->map_word();
422  if (map_word.IsForwardingAddress()) {
423  sampler_window_[i] = map_word.ToForwardingAddress();
424  } else {
425  sampler_window_[i] = NULL;
426  }
427  }
428  }
429 }
430 
431 
432 void RuntimeProfiler::HandleWakeUp(Isolate* isolate) {
433  // The profiler thread must still be waiting.
434  ASSERT(NoBarrier_Load(&state_) >= 0);
435  // In IsolateEnteredJS we have already incremented the counter and
436  // undid the decrement done by the profiler thread. Increment again
437  // to get the right count of active isolates.
438  NoBarrier_AtomicIncrement(&state_, 1);
439  semaphore.Pointer()->Signal();
440 }
441 
442 
444  return NoBarrier_Load(&state_) > 0;
445 }
446 
447 
449  Atomic32 old_state = NoBarrier_CompareAndSwap(&state_, 0, -1);
450  ASSERT(old_state >= -1);
451  if (old_state != 0) return false;
452  semaphore.Pointer()->Wait();
453  return true;
454 }
455 
456 
458  // Do a fake increment. If the profiler is waiting on the semaphore,
459  // the returned state is 0, which can be left as an initial state in
460  // case profiling is restarted later. If the profiler is not
461  // waiting, the increment will prevent it from waiting, but has to
462  // be undone after the profiler is stopped.
463  Atomic32 new_state = NoBarrier_AtomicIncrement(&state_, 1);
464  ASSERT(new_state >= 0);
465  if (new_state == 0) {
466  // The profiler thread is waiting. Wake it up. It must check for
467  // stop conditions before attempting to wait again.
468  semaphore.Pointer()->Signal();
469  }
470  thread->Join();
471  // The profiler thread is now stopped. Undo the increment in case it
472  // was not waiting.
473  if (new_state != 0) {
474  NoBarrier_AtomicIncrement(&state_, -1);
475  }
476 }
477 
478 
480  for (int i = 0; i < kSamplerWindowSize; i++) {
481  Object* function = sampler_window_[i];
482  if (function != NULL &&
483  !Marking::MarkBitFrom(HeapObject::cast(function)).Get()) {
484  sampler_window_[i] = NULL;
485  }
486  }
487 }
488 
489 
490 void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) {
491  for (int i = 0; i < kSamplerWindowSize; i++) {
492  visitor->VisitPointer(&sampler_window_[i]);
493  }
494 }
495 
496 
497 bool RuntimeProfilerRateLimiter::SuspendIfNecessary() {
500  }
501  return false;
502 }
503 
504 
505 } } // namespace v8::internal
Code * builtin(Name name)
Definition: builtins.h:320
int allow_osr_at_loop_nesting_level()
Definition: objects-inl.h:3292
Object * function() const
Definition: frames-inl.h:231
#define V8PRIxPTR
Definition: globals.h:189
void PrintF(const char *format,...)
Definition: v8utils.cc:40
bool InNewSpace(Object *object)
Definition: heap-inl.h:288
static TypeFeedbackInfo * cast(Object *obj)
static bool UseCrankshaft()
Definition: v8.h:86
static HeapObject * cast(Object *obj)
Builtins * builtins()
Definition: isolate.h:924
Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, Atomic32 old_value, Atomic32 new_value)
TickSample * sample
#define ASSERT(condition)
Definition: checks.h:270
void EnsureTickerStarted()
Definition: log.cc:1769
void set_profiler_ticks(int ticks)
Definition: objects-inl.h:3311
#define LAZY_SEMAPHORE_INITIALIZER
Definition: platform.h:658
static MarkBit MarkBitFrom(Address addr)
StackGuard * stack_guard()
Definition: isolate.h:834
static void StopRuntimeProfilerThreadBeforeShutdown(Thread *thread)
STATIC_ASSERT((FixedDoubleArray::kHeaderSize &kDoubleAlignmentMask)==0)
Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr, Atomic32 increment)
void UpdateSamplesAfterCompact(ObjectVisitor *visitor)
static void PatchStackCheckCode(Code *unoptimized_code, Code *check_code, Code *replacement_code)
bool IsPowerOf2(T x)
Definition: utils.h:50
activate correct semantics for inheriting readonliness false
Definition: flags.cc:141
static const int kMaxLoopNestingMarker
Definition: objects.h:4527
Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr)
JavaScriptFrameIteratorTemp< StackFrameIterator > JavaScriptFrameIterator
Definition: frames.h:775
bool DebuggerHasBreakPoints()
Definition: isolate-inl.h:62
Logger * logger()
Definition: isolate.h:828
activate correct semantics for inheriting readonliness enable harmony semantics for typeof enable harmony enable harmony proxies enable all harmony harmony_scoping harmony_proxies harmony_scoping tracks arrays with only smi values automatically unbox arrays of doubles use crankshaft use hydrogen range analysis use hydrogen global value numbering use function inlining maximum number of AST nodes considered for a single inlining loop invariant code motion print statistics for hydrogen trace generated IR for specified phases trace register allocator trace range analysis trace representation types environment for every instruction put a break point before deoptimizing polymorphic inlining perform array bounds checks elimination use dead code elimination trace on stack replacement optimize closures cache optimized code for closures functions with arguments object loop weight for representation inference allow uint32 values on optimize frames if they are used only in safe operations track parallel recompilation enable all profiler experiments number of stack frames inspected by the profiler call recompile stub directly when self optimizing trigger profiler ticks based on counting instead of timing weight back edges by jump distance for interrupt triggering percentage of ICs that must have type info to allow optimization watch_ic_patching retry_self_opt interrupt_at_exit extra verbose compilation tracing generate extra emit comments in code disassembly enable use of SSE3 instructions if available enable use of CMOV instruction if available enable use of SAHF instruction if enable use of VFP3 instructions if available this implies enabling ARMv7 and VFP2 enable use of VFP2 instructions if available enable use of SDIV and UDIV instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of MIPS FPU instructions if NULL
Definition: flags.cc:301
int32_t Atomic32
Definition: atomicops.h:57
T Min(T a, T b)
Definition: utils.h:229
void AttemptOnStackReplacement(JSFunction *function)
void set_allow_osr_at_loop_nesting_level(int level)
Definition: objects-inl.h:3298
static JSFunction * cast(Object *obj)