blob: 051dc51193b83c9a36411a31887e308a7f1c8448 [file] [log] [blame]
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001// 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 "scopeinfo.h"
39#include "top.h"
40
41namespace v8 {
42namespace internal {
43
44
45class PendingListNode : public Malloced {
46 public:
47 explicit PendingListNode(JSFunction* function);
48 ~PendingListNode() { Destroy(); }
49
50 PendingListNode* next() const { return next_; }
51 void set_next(PendingListNode* node) { next_ = node; }
52 Handle<JSFunction> function() { return Handle<JSFunction>::cast(function_); }
53
54 // If the function is garbage collected before we've had the chance
55 // to optimize it the weak handle will be null.
56 bool IsValid() { return !function_.is_null(); }
57
58 // Returns the number of microseconds this node has been pending.
59 int Delay() const { return static_cast<int>(OS::Ticks() - start_); }
60
61 private:
62 void Destroy();
63 static void WeakCallback(v8::Persistent<v8::Value> object, void* data);
64
65 PendingListNode* next_;
66 Handle<Object> function_; // Weak handle.
67 int64_t start_;
68};
69
70
71// Optimization sampler constants.
72static const int kSamplerFrameCount = 2;
73static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 };
74static const int kSamplerWindowSize = 16;
75
76static const int kSamplerTicksDelta = 32;
77
78static const int kSamplerThresholdInit = 3;
79static const int kSamplerThresholdMin = 1;
80static const int kSamplerThresholdDelta = 1;
81
82static const int kSamplerThresholdSizeFactorInit = 3;
83static const int kSamplerThresholdSizeFactorMin = 1;
84static const int kSamplerThresholdSizeFactorDelta = 1;
85
86static const int kSizeLimit = 1500;
87
88static int sampler_threshold = kSamplerThresholdInit;
89static int sampler_threshold_size_factor = kSamplerThresholdSizeFactorInit;
90
91
92// The JSFunctions in the sampler window are not GC safe. Old-space
93// pointers are not cleared during mark-sweep collection and therefore
94// the window might contain stale pointers. The window is updated on
95// scavenges and (parts of it) cleared on mark-sweep and
96// mark-sweep-compact.
97static Object* sampler_window[kSamplerWindowSize] = { NULL, };
98static int sampler_window_position = 0;
99static int sampler_window_weight[kSamplerWindowSize] = { 0, };
100
101
102// Support for pending 'optimize soon' requests.
103static PendingListNode* optimize_soon_list = NULL;
104
105
106PendingListNode::PendingListNode(JSFunction* function) : next_(NULL) {
107 function_ = GlobalHandles::Create(function);
108 start_ = OS::Ticks();
109 GlobalHandles::MakeWeak(function_.location(), this, &WeakCallback);
110}
111
112
113void PendingListNode::Destroy() {
114 if (!IsValid()) return;
115 GlobalHandles::Destroy(function_.location());
116 function_= Handle<Object>::null();
117}
118
119
120void PendingListNode::WeakCallback(v8::Persistent<v8::Value>, void* data) {
121 reinterpret_cast<PendingListNode*>(data)->Destroy();
122}
123
124
125static bool IsOptimizable(JSFunction* function) {
126 Code* code = function->code();
127 return code->kind() == Code::FUNCTION && code->optimizable();
128}
129
130
131static void Optimize(JSFunction* function, bool eager, int delay) {
132 ASSERT(IsOptimizable(function));
133 if (FLAG_trace_opt) {
134 PrintF("[marking (%s) ", eager ? "eagerly" : "lazily");
135 function->PrintName();
136 PrintF(" for recompilation");
137 if (delay > 0) {
138 PrintF(" (delayed %0.3f ms)", static_cast<double>(delay) / 1000);
139 }
140 PrintF("]\n");
141 }
142
143 // The next call to the function will trigger optimization.
144 function->MarkForLazyRecompilation();
145}
146
147
148static void AttemptOnStackReplacement(JSFunction* function) {
149 // See AlwaysFullCompiler (in compiler.cc) comment on why we need
150 // Debug::has_break_points().
151 ASSERT(function->IsMarkedForLazyRecompilation());
152 if (!FLAG_use_osr || Debug::has_break_points() || function->IsBuiltin()) {
153 return;
154 }
155
156 SharedFunctionInfo* shared = function->shared();
157 // If the code is not optimizable, don't try OSR.
158 if (!shared->code()->optimizable()) return;
159
160 // We are not prepared to do OSR for a function that already has an
161 // allocated arguments object. The optimized code would bypass it for
162 // arguments accesses, which is unsound. Don't try OSR.
163 if (shared->scope_info()->HasArgumentsShadow()) return;
164
165 // We're using on-stack replacement: patch the unoptimized code so that
166 // any back edge in any unoptimized frame will trigger on-stack
167 // replacement for that frame.
168 if (FLAG_trace_osr) {
169 PrintF("[patching stack checks in ");
170 function->PrintName();
171 PrintF(" for on-stack replacement]\n");
172 }
173
174 // Get the stack check stub code object to match against. We aren't
175 // prepared to generate it, but we don't expect to have to.
176 StackCheckStub check_stub;
177 Object* check_code;
178 MaybeObject* maybe_check_code = check_stub.TryGetCode();
179 if (maybe_check_code->ToObject(&check_code)) {
180 Code* replacement_code = Builtins::builtin(Builtins::OnStackReplacement);
181 Code* unoptimized_code = shared->code();
182 // Iterate the unoptimized code and patch every stack check except at
183 // the function entry. This code assumes the function entry stack
184 // check appears first i.e., is not deferred or otherwise reordered.
185 bool first = true;
186 for (RelocIterator it(unoptimized_code, RelocInfo::kCodeTargetMask);
187 !it.done();
188 it.next()) {
189 RelocInfo* rinfo = it.rinfo();
190 if (rinfo->target_address() == Code::cast(check_code)->entry()) {
191 if (first) {
192 first = false;
193 } else {
194 Deoptimizer::PatchStackCheckCode(rinfo, replacement_code);
195 }
196 }
197 }
198 }
199}
200
201
202static void ClearSampleBuffer() {
203 for (int i = 0; i < kSamplerWindowSize; i++) {
204 sampler_window[i] = NULL;
205 sampler_window_weight[i] = 0;
206 }
207}
208
209
210static void ClearSampleBufferNewSpaceEntries() {
211 for (int i = 0; i < kSamplerWindowSize; i++) {
212 if (Heap::InNewSpace(sampler_window[i])) {
213 sampler_window[i] = NULL;
214 sampler_window_weight[i] = 0;
215 }
216 }
217}
218
219
220static int 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
234static void 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
243void RuntimeProfiler::OptimizeNow() {
244 HandleScope scope;
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 (IsOptimizable(*function)) {
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 count = 0;
265 for (JavaScriptFrameIterator it;
266 count < kSamplerFrameCount && !it.done();
267 it.Advance()) {
268 JavaScriptFrame* frame = it.frame();
269 JSFunction* function = JSFunction::cast(frame->function());
270 int function_size = function->shared()->SourceSize();
271 int threshold_size_factor;
272 if (function_size > kSizeLimit) {
273 threshold_size_factor = sampler_threshold_size_factor;
274 } else {
275 threshold_size_factor = 1;
276 }
277
278 int threshold = sampler_threshold * threshold_size_factor;
279 samples[count++] = function;
280 if (function->IsMarkedForLazyRecompilation()) {
281 Code* unoptimized = function->shared()->code();
282 int nesting = unoptimized->allow_osr_at_loop_nesting_level();
283 if (nesting == 0) AttemptOnStackReplacement(function);
284 int new_nesting = Min(nesting + 1, Code::kMaxLoopNestingMarker);
285 unoptimized->set_allow_osr_at_loop_nesting_level(new_nesting);
286 } else if (LookupSample(function) >= threshold) {
287 if (IsOptimizable(function)) {
288 Optimize(function, false, 0);
289 CompilationCache::MarkForEagerOptimizing(Handle<JSFunction>(function));
290 }
291 }
292 }
293
294 // Add the collected functions as samples. It's important not to do
295 // this as part of collecting them because this will interfere with
296 // the sample lookup in case of recursive functions.
297 for (int i = 0; i < count; i++) {
298 AddSample(samples[i], kSamplerFrameWeight[i]);
299 }
300}
301
302
303void RuntimeProfiler::OptimizeSoon(JSFunction* function) {
304 if (!IsOptimizable(function)) return;
305 PendingListNode* node = new PendingListNode(function);
306 node->set_next(optimize_soon_list);
307 optimize_soon_list = node;
308}
309
310
311void RuntimeProfiler::NotifyTick() {
312 StackGuard::RequestRuntimeProfilerTick();
313}
314
315
316void RuntimeProfiler::MarkCompactPrologue(bool is_compacting) {
317 if (is_compacting) {
318 // Clear all samples before mark-sweep-compact because every
319 // function might move.
320 ClearSampleBuffer();
321 } else {
322 // Clear only new space entries on mark-sweep since none of the
323 // old-space functions will move.
324 ClearSampleBufferNewSpaceEntries();
325 }
326}
327
328
329bool IsEqual(void* first, void* second) {
330 return first == second;
331}
332
333
334void RuntimeProfiler::Setup() {
335 ClearSampleBuffer();
336 // If the ticker hasn't already started, make sure to do so to get
337 // the ticks for the runtime profiler.
338 if (IsEnabled()) Logger::EnsureTickerStarted();
339}
340
341
342void RuntimeProfiler::Reset() {
343 sampler_threshold = kSamplerThresholdInit;
344 sampler_threshold_size_factor = kSamplerThresholdSizeFactorInit;
345}
346
347
348void RuntimeProfiler::TearDown() {
349 // Nothing to do.
350}
351
352
353Object** RuntimeProfiler::SamplerWindowAddress() {
354 return sampler_window;
355}
356
357
358int RuntimeProfiler::SamplerWindowSize() {
359 return kSamplerWindowSize;
360}
361
362
363bool RuntimeProfilerRateLimiter::SuspendIfNecessary() {
364 static const int kNonJSTicksThreshold = 100;
365 // We suspend the runtime profiler thread when not running
366 // JavaScript. If the CPU profiler is active we must not do this
367 // because it samples both JavaScript and C++ code.
368 if (RuntimeProfiler::IsEnabled() &&
369 !CpuProfiler::is_profiling() &&
370 !(FLAG_prof && FLAG_prof_auto)) {
371 if (Top::IsInJSState()) {
372 non_js_ticks_ = 0;
373 } else {
374 if (non_js_ticks_ < kNonJSTicksThreshold) {
375 ++non_js_ticks_;
376 } else {
377 if (Top::WaitForJSState()) return true;
378 }
379 }
380 }
381 return false;
382}
383
384
385} } // namespace v8::internal