v8  3.11.10(node0.8.26)
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 "global-handles.h"
38 #include "isolate-inl.h"
39 #include "mark-compact.h"
40 #include "platform.h"
41 #include "scopeinfo.h"
42 
43 namespace v8 {
44 namespace internal {
45 
46 
47 // Optimization sampler constants.
48 static const int kSamplerFrameCount = 2;
49 
50 // Constants for statistical profiler.
51 static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 };
52 
53 static const int kSamplerTicksBetweenThresholdAdjustment = 32;
54 
55 static const int kSamplerThresholdInit = 3;
56 static const int kSamplerThresholdMin = 1;
57 static const int kSamplerThresholdDelta = 1;
58 
59 static const int kSamplerThresholdSizeFactorInit = 3;
60 
61 static const int kSizeLimit = 1500;
62 
63 // Constants for counter based profiler.
64 
65 // Number of times a function has to be seen on the stack before it is
66 // optimized.
67 static const int kProfilerTicksBeforeOptimization = 2;
68 // If the function optimization was disabled due to high deoptimization count,
69 // but the function is hot and has been seen on the stack this number of times,
70 // then we try to reenable optimization for this function.
71 static const int kProfilerTicksBeforeReenablingOptimization = 250;
72 // If a function does not have enough type info (according to
73 // FLAG_type_info_threshold), but has seen a huge number of ticks,
74 // optimize it as it is.
75 static const int kTicksWhenNotEnoughTypeInfo = 100;
76 // We only have one byte to store the number of ticks.
77 STATIC_ASSERT(kProfilerTicksBeforeOptimization < 256);
78 STATIC_ASSERT(kProfilerTicksBeforeReenablingOptimization < 256);
79 STATIC_ASSERT(kTicksWhenNotEnoughTypeInfo < 256);
80 
81 
82 // Maximum size in bytes of generated code for a function to be optimized
83 // the very first time it is seen on the stack.
84 static const int kMaxSizeEarlyOpt = 500;
85 
86 
87 Atomic32 RuntimeProfiler::state_ = 0;
88 
89 // TODO(isolates): Clean up the semaphore when it is no longer required.
91 
92 #ifdef DEBUG
93 bool RuntimeProfiler::has_been_globally_set_up_ = false;
94 #endif
95 bool RuntimeProfiler::enabled_ = false;
96 
97 
99  : isolate_(isolate),
100  sampler_threshold_(kSamplerThresholdInit),
101  sampler_threshold_size_factor_(kSamplerThresholdSizeFactorInit),
102  sampler_ticks_until_threshold_adjustment_(
103  kSamplerTicksBetweenThresholdAdjustment),
104  sampler_window_position_(0),
105  any_ic_changed_(false),
106  code_generated_(false) {
107  ClearSampleBuffer();
108 }
109 
110 
112  ASSERT(!has_been_globally_set_up_);
113  enabled_ = V8::UseCrankshaft() && FLAG_opt;
114 #ifdef DEBUG
115  has_been_globally_set_up_ = true;
116 #endif
117 }
118 
119 
120 static void GetICCounts(JSFunction* function,
121  int* ic_with_type_info_count,
122  int* ic_total_count,
123  int* percentage) {
124  *ic_total_count = 0;
125  *ic_with_type_info_count = 0;
126  Object* raw_info =
127  function->shared()->code()->type_feedback_info();
128  if (raw_info->IsTypeFeedbackInfo()) {
129  TypeFeedbackInfo* info = TypeFeedbackInfo::cast(raw_info);
130  *ic_with_type_info_count = info->ic_with_type_info_count();
131  *ic_total_count = info->ic_total_count();
132  }
133  *percentage = *ic_total_count > 0
134  ? 100 * *ic_with_type_info_count / *ic_total_count
135  : 100;
136 }
137 
138 
139 void RuntimeProfiler::Optimize(JSFunction* function, const char* reason) {
140  ASSERT(function->IsOptimizable());
141  if (FLAG_trace_opt) {
142  PrintF("[marking ");
143  function->PrintName();
144  PrintF(" 0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(function->address()));
145  PrintF(" for recompilation, reason: %s", reason);
146  if (FLAG_type_info_threshold > 0) {
147  int typeinfo, total, percentage;
148  GetICCounts(function, &typeinfo, &total, &percentage);
149  PrintF(", ICs with typeinfo: %d/%d (%d%%)", typeinfo, total, percentage);
150  }
151  PrintF("]\n");
152  }
153 
154  // The next call to the function will trigger optimization.
155  function->MarkForLazyRecompilation();
156 }
157 
158 
160  // See AlwaysFullCompiler (in compiler.cc) comment on why we need
161  // Debug::has_break_points().
162  ASSERT(function->IsMarkedForLazyRecompilation());
163  if (!FLAG_use_osr ||
164  isolate_->DebuggerHasBreakPoints() ||
165  function->IsBuiltin()) {
166  return;
167  }
168 
169  SharedFunctionInfo* shared = function->shared();
170  // If the code is not optimizable, don't try OSR.
171  if (!shared->code()->optimizable()) return;
172 
173  // We are not prepared to do OSR for a function that already has an
174  // allocated arguments object. The optimized code would bypass it for
175  // arguments accesses, which is unsound. Don't try OSR.
176  if (shared->uses_arguments()) return;
177 
178  // We're using on-stack replacement: patch the unoptimized code so that
179  // any back edge in any unoptimized frame will trigger on-stack
180  // replacement for that frame.
181  if (FLAG_trace_osr) {
182  PrintF("[patching stack checks in ");
183  function->PrintName();
184  PrintF(" for on-stack replacement]\n");
185  }
186 
187  // Get the stack check stub code object to match against. We aren't
188  // prepared to generate it, but we don't expect to have to.
189  bool found_code = false;
190  Code* stack_check_code = NULL;
191  if (FLAG_count_based_interrupts) {
192  InterruptStub interrupt_stub;
193  found_code = interrupt_stub.FindCodeInCache(&stack_check_code);
194  } else // NOLINT
195  { // NOLINT
196  StackCheckStub check_stub;
197  found_code = check_stub.FindCodeInCache(&stack_check_code);
198  }
199  if (found_code) {
200  Code* replacement_code =
201  isolate_->builtins()->builtin(Builtins::kOnStackReplacement);
202  Code* unoptimized_code = shared->code();
203  Deoptimizer::PatchStackCheckCode(unoptimized_code,
204  stack_check_code,
205  replacement_code);
206  }
207 }
208 
209 
210 void RuntimeProfiler::ClearSampleBuffer() {
211  memset(sampler_window_, 0, sizeof(sampler_window_));
212  memset(sampler_window_weight_, 0, sizeof(sampler_window_weight_));
213 }
214 
215 
216 int RuntimeProfiler::LookupSample(JSFunction* function) {
217  int weight = 0;
218  for (int i = 0; i < kSamplerWindowSize; i++) {
219  Object* sample = sampler_window_[i];
220  if (sample != NULL) {
221  if (function == sample) {
222  weight += sampler_window_weight_[i];
223  }
224  }
225  }
226  return weight;
227 }
228 
229 
230 void RuntimeProfiler::AddSample(JSFunction* function, int weight) {
231  ASSERT(IsPowerOf2(kSamplerWindowSize));
232  sampler_window_[sampler_window_position_] = function;
233  sampler_window_weight_[sampler_window_position_] = weight;
234  sampler_window_position_ = (sampler_window_position_ + 1) &
235  (kSamplerWindowSize - 1);
236 }
237 
238 
240  HandleScope scope(isolate_);
241 
242  // Run through the JavaScript frames and collect them. If we already
243  // have a sample of the function, we mark it for optimizations
244  // (eagerly or lazily).
245  JSFunction* samples[kSamplerFrameCount];
246  int sample_count = 0;
247  int frame_count = 0;
248  int frame_count_limit = FLAG_watch_ic_patching ? FLAG_frame_count
249  : kSamplerFrameCount;
250  for (JavaScriptFrameIterator it(isolate_);
251  frame_count++ < frame_count_limit && !it.done();
252  it.Advance()) {
253  JavaScriptFrame* frame = it.frame();
254  JSFunction* function = JSFunction::cast(frame->function());
255 
256  if (!FLAG_watch_ic_patching) {
257  // Adjust threshold each time we have processed
258  // a certain number of ticks.
259  if (sampler_ticks_until_threshold_adjustment_ > 0) {
260  sampler_ticks_until_threshold_adjustment_--;
261  if (sampler_ticks_until_threshold_adjustment_ <= 0) {
262  // If the threshold is not already at the minimum
263  // modify and reset the ticks until next adjustment.
264  if (sampler_threshold_ > kSamplerThresholdMin) {
265  sampler_threshold_ -= kSamplerThresholdDelta;
266  sampler_ticks_until_threshold_adjustment_ =
267  kSamplerTicksBetweenThresholdAdjustment;
268  }
269  }
270  }
271  }
272 
273  SharedFunctionInfo* shared = function->shared();
274  Code* shared_code = shared->code();
275 
276  if (shared_code->kind() != Code::FUNCTION) continue;
277 
278  if (function->IsMarkedForLazyRecompilation()) {
279  int nesting = shared_code->allow_osr_at_loop_nesting_level();
280  if (nesting == 0) AttemptOnStackReplacement(function);
281  int new_nesting = Min(nesting + 1, Code::kMaxLoopNestingMarker);
282  shared_code->set_allow_osr_at_loop_nesting_level(new_nesting);
283  }
284 
285  // Only record top-level code on top of the execution stack and
286  // avoid optimizing excessively large scripts since top-level code
287  // will be executed only once.
288  const int kMaxToplevelSourceSize = 10 * 1024;
289  if (shared->is_toplevel() &&
290  (frame_count > 1 || shared->SourceSize() > kMaxToplevelSourceSize)) {
291  continue;
292  }
293 
294  // Do not record non-optimizable functions.
295  if (shared->optimization_disabled()) {
296  if (shared->deopt_count() >= Compiler::kDefaultMaxOptCount) {
297  // If optimization was disabled due to many deoptimizations,
298  // then check if the function is hot and try to reenable optimization.
299  int ticks = shared_code->profiler_ticks();
300  if (ticks >= kProfilerTicksBeforeReenablingOptimization) {
301  shared_code->set_profiler_ticks(0);
302  shared->TryReenableOptimization();
303  } else {
304  shared_code->set_profiler_ticks(ticks + 1);
305  }
306  }
307  continue;
308  }
309  if (!function->IsOptimizable()) continue;
310 
311 
312 
313  if (FLAG_watch_ic_patching) {
314  int ticks = shared_code->profiler_ticks();
315 
316  if (ticks >= kProfilerTicksBeforeOptimization) {
317  int typeinfo, total, percentage;
318  GetICCounts(function, &typeinfo, &total, &percentage);
319  if (percentage >= FLAG_type_info_threshold) {
320  // If this particular function hasn't had any ICs patched for enough
321  // ticks, optimize it now.
322  Optimize(function, "hot and stable");
323  } else if (ticks >= kTicksWhenNotEnoughTypeInfo) {
324  Optimize(function, "not much type info but very hot");
325  } else {
326  shared_code->set_profiler_ticks(ticks + 1);
327  if (FLAG_trace_opt_verbose) {
328  PrintF("[not yet optimizing ");
329  function->PrintName();
330  PrintF(", not enough type info: %d/%d (%d%%)]\n",
331  typeinfo, total, percentage);
332  }
333  }
334  } else if (!any_ic_changed_ &&
335  shared_code->instruction_size() < kMaxSizeEarlyOpt) {
336  // If no IC was patched since the last tick and this function is very
337  // small, optimistically optimize it now.
338  Optimize(function, "small function");
339  } else {
340  shared_code->set_profiler_ticks(ticks + 1);
341  }
342  } else { // !FLAG_watch_ic_patching
343  samples[sample_count++] = function;
344 
345  int function_size = function->shared()->SourceSize();
346  int threshold_size_factor = (function_size > kSizeLimit)
347  ? sampler_threshold_size_factor_
348  : 1;
349 
350  int threshold = sampler_threshold_ * threshold_size_factor;
351 
352  if (LookupSample(function) >= threshold) {
353  Optimize(function, "sampler window lookup");
354  }
355  }
356  }
357  if (FLAG_watch_ic_patching) {
358  any_ic_changed_ = false;
359  } else { // !FLAG_watch_ic_patching
360  // Add the collected functions as samples. It's important not to do
361  // this as part of collecting them because this will interfere with
362  // the sample lookup in case of recursive functions.
363  for (int i = 0; i < sample_count; i++) {
364  AddSample(samples[i], kSamplerFrameWeight[i]);
365  }
366  }
367 }
368 
369 
371  if (FLAG_count_based_interrupts) return;
373 }
374 
375 
377  ASSERT(has_been_globally_set_up_);
378  if (!FLAG_watch_ic_patching) {
379  ClearSampleBuffer();
380  }
381  // If the ticker hasn't already started, make sure to do so to get
382  // the ticks for the runtime profiler.
383  if (IsEnabled()) isolate_->logger()->EnsureTickerStarted();
384 }
385 
386 
388  if (!FLAG_watch_ic_patching) {
389  sampler_threshold_ = kSamplerThresholdInit;
390  sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit;
391  sampler_ticks_until_threshold_adjustment_ =
392  kSamplerTicksBetweenThresholdAdjustment;
393  }
394 }
395 
396 
398  // Nothing to do.
399 }
400 
401 
403  return kSamplerWindowSize;
404 }
405 
406 
407 // Update the pointers in the sampler window after a GC.
409  for (int i = 0; i < kSamplerWindowSize; i++) {
410  Object* function = sampler_window_[i];
411  if (function != NULL && isolate_->heap()->InNewSpace(function)) {
412  MapWord map_word = HeapObject::cast(function)->map_word();
413  if (map_word.IsForwardingAddress()) {
414  sampler_window_[i] = map_word.ToForwardingAddress();
415  } else {
416  sampler_window_[i] = NULL;
417  }
418  }
419  }
420 }
421 
422 
423 void RuntimeProfiler::HandleWakeUp(Isolate* isolate) {
424  // The profiler thread must still be waiting.
425  ASSERT(NoBarrier_Load(&state_) >= 0);
426  // In IsolateEnteredJS we have already incremented the counter and
427  // undid the decrement done by the profiler thread. Increment again
428  // to get the right count of active isolates.
429  NoBarrier_AtomicIncrement(&state_, 1);
430  semaphore.Pointer()->Signal();
431 }
432 
433 
435  return NoBarrier_Load(&state_) > 0;
436 }
437 
438 
440  Atomic32 old_state = NoBarrier_CompareAndSwap(&state_, 0, -1);
441  ASSERT(old_state >= -1);
442  if (old_state != 0) return false;
443  semaphore.Pointer()->Wait();
444  return true;
445 }
446 
447 
449  // Do a fake increment. If the profiler is waiting on the semaphore,
450  // the returned state is 0, which can be left as an initial state in
451  // case profiling is restarted later. If the profiler is not
452  // waiting, the increment will prevent it from waiting, but has to
453  // be undone after the profiler is stopped.
454  Atomic32 new_state = NoBarrier_AtomicIncrement(&state_, 1);
455  ASSERT(new_state >= 0);
456  if (new_state == 0) {
457  // The profiler thread is waiting. Wake it up. It must check for
458  // stop conditions before attempting to wait again.
459  semaphore.Pointer()->Signal();
460  }
461  thread->Join();
462  // The profiler thread is now stopped. Undo the increment in case it
463  // was not waiting.
464  if (new_state != 0) {
465  NoBarrier_AtomicIncrement(&state_, -1);
466  }
467 }
468 
469 
471  for (int i = 0; i < kSamplerWindowSize; i++) {
472  Object* function = sampler_window_[i];
473  if (function != NULL &&
474  !Marking::MarkBitFrom(HeapObject::cast(function)).Get()) {
475  sampler_window_[i] = NULL;
476  }
477  }
478 }
479 
480 
481 void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) {
482  for (int i = 0; i < kSamplerWindowSize; i++) {
483  visitor->VisitPointer(&sampler_window_[i]);
484  }
485 }
486 
487 
488 bool RuntimeProfilerRateLimiter::SuspendIfNecessary() {
491  }
492  return false;
493 }
494 
495 
496 } } // namespace v8::internal
Code * builtin(Name name)
Definition: builtins.h:312
static const int kDefaultMaxOptCount
Definition: compiler.h:276
int allow_osr_at_loop_nesting_level()
Definition: objects-inl.h:3145
Object * function() const
Definition: frames-inl.h:231
#define V8PRIxPTR
Definition: globals.h:204
void PrintF(const char *format,...)
Definition: v8utils.cc:40
bool InNewSpace(Object *object)
Definition: heap-inl.h:292
static TypeFeedbackInfo * cast(Object *obj)
static bool UseCrankshaft()
Definition: v8.h:86
static HeapObject * cast(Object *obj)
Builtins * builtins()
Definition: isolate.h:909
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:1684
void set_profiler_ticks(int ticks)
Definition: objects-inl.h:3164
#define LAZY_SEMAPHORE_INITIALIZER
Definition: platform.h:631
static MarkBit MarkBitFrom(Address addr)
StackGuard * stack_guard()
Definition: isolate.h:819
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
static const int kMaxLoopNestingMarker
Definition: objects.h:4491
Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr)
JavaScriptFrameIteratorTemp< StackFrameIterator > JavaScriptFrameIterator
Definition: frames.h:773
bool DebuggerHasBreakPoints()
Definition: isolate-inl.h:62
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 trace on stack replacement optimize closures functions with arguments object optimize functions containing for in loops profiler considers IC stability primitive functions trigger their own optimization re try self optimization if it failed insert an interrupt check at function exit execution budget before interrupt is triggered call count before self optimization self_optimization count_based_interrupts weighted_back_edges trace_opt 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 enable use of ARMv7 instructions if enable use of MIPS FPU instructions if NULL
Definition: flags.cc:274
Logger * logger()
Definition: isolate.h:813
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:3151
FlagType type() const
Definition: flags.cc:1358
static JSFunction * cast(Object *obj)