vendor/v8/src/runtime-profiler.cc in mustang-0.0.1 vs vendor/v8/src/runtime-profiler.cc in mustang-0.1.0

- old
+ new

@@ -33,12 +33,13 @@ #include "code-stubs.h" #include "compilation-cache.h" #include "deoptimizer.h" #include "execution.h" #include "global-handles.h" +#include "mark-compact.h" +#include "platform.h" #include "scopeinfo.h" -#include "top.h" namespace v8 { namespace internal { @@ -66,20 +67,13 @@ Handle<Object> function_; // Weak handle. int64_t start_; }; -enum SamplerState { - IN_NON_JS_STATE = 0, - IN_JS_STATE = 1 -}; - - // Optimization sampler constants. static const int kSamplerFrameCount = 2; static const int kSamplerFrameWeight[kSamplerFrameCount] = { 2, 1 }; -static const int kSamplerWindowSize = 16; static const int kSamplerTicksBetweenThresholdAdjustment = 32; static const int kSamplerThresholdInit = 3; static const int kSamplerThresholdMin = 1; @@ -89,60 +83,69 @@ static const int kSamplerThresholdSizeFactorMin = 1; static const int kSamplerThresholdSizeFactorDelta = 1; static const int kSizeLimit = 1500; -static int sampler_threshold = kSamplerThresholdInit; -static int sampler_threshold_size_factor = kSamplerThresholdSizeFactorInit; -static int sampler_ticks_until_threshold_adjustment = - kSamplerTicksBetweenThresholdAdjustment; - -// The ratio of ticks spent in JS code in percent. -static Atomic32 js_ratio; - -// The JSFunctions in the sampler window are not GC safe. Old-space -// pointers are not cleared during mark-sweep collection and therefore -// the window might contain stale pointers. The window is updated on -// scavenges and (parts of it) cleared on mark-sweep and -// mark-sweep-compact. -static Object* sampler_window[kSamplerWindowSize] = { NULL, }; -static int sampler_window_position = 0; -static int sampler_window_weight[kSamplerWindowSize] = { 0, }; - - -// Support for pending 'optimize soon' requests. -static PendingListNode* optimize_soon_list = NULL; - - PendingListNode::PendingListNode(JSFunction* function) : next_(NULL) { - function_ = GlobalHandles::Create(function); + GlobalHandles* global_handles = Isolate::Current()->global_handles(); + function_ = global_handles->Create(function); start_ = OS::Ticks(); - GlobalHandles::MakeWeak(function_.location(), this, &WeakCallback); + global_handles->MakeWeak(function_.location(), this, &WeakCallback); } void PendingListNode::Destroy() { if (!IsValid()) return; - GlobalHandles::Destroy(function_.location()); + GlobalHandles* global_handles = Isolate::Current()->global_handles(); + global_handles->Destroy(function_.location()); function_= Handle<Object>::null(); } void PendingListNode::WeakCallback(v8::Persistent<v8::Value>, void* data) { reinterpret_cast<PendingListNode*>(data)->Destroy(); } static bool IsOptimizable(JSFunction* function) { - if (Heap::InNewSpace(function)) return false; Code* code = function->code(); return code->kind() == Code::FUNCTION && code->optimizable(); } -static void Optimize(JSFunction* function, bool eager, int delay) { +Atomic32 RuntimeProfiler::state_ = 0; +// TODO(isolates): Create the semaphore lazily and clean it up when no +// longer required. +#ifdef ENABLE_LOGGING_AND_PROFILING +Semaphore* RuntimeProfiler::semaphore_ = OS::CreateSemaphore(0); +#endif + + +RuntimeProfiler::RuntimeProfiler(Isolate* isolate) + : isolate_(isolate), + sampler_threshold_(kSamplerThresholdInit), + sampler_threshold_size_factor_(kSamplerThresholdSizeFactorInit), + sampler_ticks_until_threshold_adjustment_( + kSamplerTicksBetweenThresholdAdjustment), + js_ratio_(0), + sampler_window_position_(0), + optimize_soon_list_(NULL), + state_window_position_(0) { + state_counts_[0] = kStateWindowSize; + state_counts_[1] = 0; + memset(state_window_, 0, sizeof(state_window_)); + ClearSampleBuffer(); +} + + +bool RuntimeProfiler::IsEnabled() { + return V8::UseCrankshaft() && FLAG_opt; +} + + +void RuntimeProfiler::Optimize(JSFunction* function, bool eager, int delay) { ASSERT(IsOptimizable(function)); if (FLAG_trace_opt) { PrintF("[marking (%s) ", eager ? "eagerly" : "lazily"); function->PrintName(); PrintF(" for recompilation"); @@ -155,15 +158,17 @@ // The next call to the function will trigger optimization. function->MarkForLazyRecompilation(); } -static void AttemptOnStackReplacement(JSFunction* function) { +void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) { // See AlwaysFullCompiler (in compiler.cc) comment on why we need // Debug::has_break_points(). ASSERT(function->IsMarkedForLazyRecompilation()); - if (!FLAG_use_osr || Debug::has_break_points() || function->IsBuiltin()) { + if (!FLAG_use_osr || + isolate_->debug()->has_break_points() || + function->IsBuiltin()) { return; } SharedFunctionInfo* shared = function->shared(); // If the code is not optimizable or references context slots, don't try OSR. @@ -189,63 +194,52 @@ // prepared to generate it, but we don't expect to have to. StackCheckStub check_stub; Object* check_code; MaybeObject* maybe_check_code = check_stub.TryGetCode(); if (maybe_check_code->ToObject(&check_code)) { - Code* replacement_code = Builtins::builtin(Builtins::OnStackReplacement); + Code* replacement_code = + isolate_->builtins()->builtin(Builtins::kOnStackReplacement); Code* unoptimized_code = shared->code(); Deoptimizer::PatchStackCheckCode(unoptimized_code, Code::cast(check_code), replacement_code); } } -static void ClearSampleBuffer() { - for (int i = 0; i < kSamplerWindowSize; i++) { - sampler_window[i] = NULL; - sampler_window_weight[i] = 0; - } +void RuntimeProfiler::ClearSampleBuffer() { + memset(sampler_window_, 0, sizeof(sampler_window_)); + memset(sampler_window_weight_, 0, sizeof(sampler_window_weight_)); } -static void ClearSampleBufferNewSpaceEntries() { - for (int i = 0; i < kSamplerWindowSize; i++) { - if (Heap::InNewSpace(sampler_window[i])) { - sampler_window[i] = NULL; - sampler_window_weight[i] = 0; - } - } -} - - -static int LookupSample(JSFunction* function) { +int RuntimeProfiler::LookupSample(JSFunction* function) { int weight = 0; for (int i = 0; i < kSamplerWindowSize; i++) { - Object* sample = sampler_window[i]; + Object* sample = sampler_window_[i]; if (sample != NULL) { if (function == sample) { - weight += sampler_window_weight[i]; + weight += sampler_window_weight_[i]; } } } return weight; } -static void AddSample(JSFunction* function, int weight) { +void RuntimeProfiler::AddSample(JSFunction* function, int weight) { ASSERT(IsPowerOf2(kSamplerWindowSize)); - sampler_window[sampler_window_position] = function; - sampler_window_weight[sampler_window_position] = weight; - sampler_window_position = (sampler_window_position + 1) & + sampler_window_[sampler_window_position_] = function; + sampler_window_weight_[sampler_window_position_] = weight; + sampler_window_position_ = (sampler_window_position_ + 1) & (kSamplerWindowSize - 1); } void RuntimeProfiler::OptimizeNow() { - HandleScope scope; - PendingListNode* current = optimize_soon_list; + HandleScope scope(isolate_); + PendingListNode* current = optimize_soon_list_; while (current != NULL) { PendingListNode* next = current->next(); if (current->IsValid()) { Handle<JSFunction> function = current->function(); int delay = current->Delay(); @@ -254,11 +248,11 @@ } } delete current; current = next; } - optimize_soon_list = NULL; + optimize_soon_list_ = NULL; // Run through the JavaScript frames and collect them. If we already // have a sample of the function, we mark it for optimizations // (eagerly or lazily). JSFunction* samples[kSamplerFrameCount]; @@ -270,18 +264,18 @@ JavaScriptFrame* frame = it.frame(); JSFunction* function = JSFunction::cast(frame->function()); // Adjust threshold each time we have processed // a certain number of ticks. - if (sampler_ticks_until_threshold_adjustment > 0) { - sampler_ticks_until_threshold_adjustment--; - if (sampler_ticks_until_threshold_adjustment <= 0) { + if (sampler_ticks_until_threshold_adjustment_ > 0) { + sampler_ticks_until_threshold_adjustment_--; + if (sampler_ticks_until_threshold_adjustment_ <= 0) { // If the threshold is not already at the minimum // modify and reset the ticks until next adjustment. - if (sampler_threshold > kSamplerThresholdMin) { - sampler_threshold -= kSamplerThresholdDelta; - sampler_ticks_until_threshold_adjustment = + if (sampler_threshold_ > kSamplerThresholdMin) { + sampler_threshold_ -= kSamplerThresholdDelta; + sampler_ticks_until_threshold_adjustment_ = kSamplerTicksBetweenThresholdAdjustment; } } } @@ -297,15 +291,15 @@ if (!IsOptimizable(function)) continue; samples[sample_count++] = function; int function_size = function->shared()->SourceSize(); int threshold_size_factor = (function_size > kSizeLimit) - ? sampler_threshold_size_factor + ? sampler_threshold_size_factor_ : 1; - int threshold = sampler_threshold * threshold_size_factor; - int current_js_ratio = NoBarrier_Load(&js_ratio); + int threshold = sampler_threshold_ * threshold_size_factor; + int current_js_ratio = NoBarrier_Load(&js_ratio_); // Adjust threshold depending on the ratio of time spent // in JS code. if (current_js_ratio < 20) { // If we spend less than 20% of the time in JS code, @@ -317,11 +311,12 @@ threshold *= 3; } if (LookupSample(function) >= threshold) { Optimize(function, false, 0); - CompilationCache::MarkForEagerOptimizing(Handle<JSFunction>(function)); + isolate_->compilation_cache()->MarkForEagerOptimizing( + Handle<JSFunction>(function)); } } // Add the collected functions as samples. It's important not to do // this as part of collecting them because this will interfere with @@ -333,112 +328,147 @@ void RuntimeProfiler::OptimizeSoon(JSFunction* function) { if (!IsOptimizable(function)) return; PendingListNode* node = new PendingListNode(function); - node->set_next(optimize_soon_list); - optimize_soon_list = node; + node->set_next(optimize_soon_list_); + optimize_soon_list_ = node; } #ifdef ENABLE_LOGGING_AND_PROFILING -static void UpdateStateRatio(SamplerState current_state) { - static const int kStateWindowSize = 128; - static SamplerState state_window[kStateWindowSize]; - static int state_window_position = 0; - static int state_counts[2] = { kStateWindowSize, 0 }; - - SamplerState old_state = state_window[state_window_position]; - state_counts[old_state]--; - state_window[state_window_position] = current_state; - state_counts[current_state]++; +void RuntimeProfiler::UpdateStateRatio(SamplerState current_state) { + SamplerState old_state = state_window_[state_window_position_]; + state_counts_[old_state]--; + state_window_[state_window_position_] = current_state; + state_counts_[current_state]++; ASSERT(IsPowerOf2(kStateWindowSize)); - state_window_position = (state_window_position + 1) & + state_window_position_ = (state_window_position_ + 1) & (kStateWindowSize - 1); - NoBarrier_Store(&js_ratio, state_counts[IN_JS_STATE] * 100 / + NoBarrier_Store(&js_ratio_, state_counts_[IN_JS_STATE] * 100 / kStateWindowSize); } #endif void RuntimeProfiler::NotifyTick() { #ifdef ENABLE_LOGGING_AND_PROFILING // Record state sample. - SamplerState state = Top::IsInJSState() + SamplerState state = IsSomeIsolateInJS() ? IN_JS_STATE : IN_NON_JS_STATE; UpdateStateRatio(state); - StackGuard::RequestRuntimeProfilerTick(); + isolate_->stack_guard()->RequestRuntimeProfilerTick(); #endif } -void RuntimeProfiler::MarkCompactPrologue(bool is_compacting) { - if (is_compacting) { - // Clear all samples before mark-sweep-compact because every - // function might move. - ClearSampleBuffer(); - } else { - // Clear only new space entries on mark-sweep since none of the - // old-space functions will move. - ClearSampleBufferNewSpaceEntries(); - } -} - - -bool IsEqual(void* first, void* second) { - return first == second; -} - - void RuntimeProfiler::Setup() { ClearSampleBuffer(); // If the ticker hasn't already started, make sure to do so to get // the ticks for the runtime profiler. - if (IsEnabled()) Logger::EnsureTickerStarted(); + if (IsEnabled()) isolate_->logger()->EnsureTickerStarted(); } void RuntimeProfiler::Reset() { - sampler_threshold = kSamplerThresholdInit; - sampler_ticks_until_threshold_adjustment = + sampler_threshold_ = kSamplerThresholdInit; + sampler_threshold_size_factor_ = kSamplerThresholdSizeFactorInit; + sampler_ticks_until_threshold_adjustment_ = kSamplerTicksBetweenThresholdAdjustment; - sampler_threshold_size_factor = kSamplerThresholdSizeFactorInit; } void RuntimeProfiler::TearDown() { // Nothing to do. } -Object** RuntimeProfiler::SamplerWindowAddress() { - return sampler_window; +int RuntimeProfiler::SamplerWindowSize() { + return kSamplerWindowSize; } -int RuntimeProfiler::SamplerWindowSize() { - return kSamplerWindowSize; +// Update the pointers in the sampler window after a GC. +void RuntimeProfiler::UpdateSamplesAfterScavenge() { + for (int i = 0; i < kSamplerWindowSize; i++) { + Object* function = sampler_window_[i]; + if (function != NULL && isolate_->heap()->InNewSpace(function)) { + MapWord map_word = HeapObject::cast(function)->map_word(); + if (map_word.IsForwardingAddress()) { + sampler_window_[i] = map_word.ToForwardingAddress(); + } else { + sampler_window_[i] = NULL; + } + } + } } +void RuntimeProfiler::HandleWakeUp(Isolate* isolate) { +#ifdef ENABLE_LOGGING_AND_PROFILING + // The profiler thread must still be waiting. + ASSERT(NoBarrier_Load(&state_) >= 0); + // In IsolateEnteredJS we have already incremented the counter and + // undid the decrement done by the profiler thread. Increment again + // to get the right count of active isolates. + NoBarrier_AtomicIncrement(&state_, 1); + semaphore_->Signal(); + isolate->ResetEagerOptimizingData(); +#endif +} + + +bool RuntimeProfiler::IsSomeIsolateInJS() { + return NoBarrier_Load(&state_) > 0; +} + + +bool RuntimeProfiler::WaitForSomeIsolateToEnterJS() { +#ifdef ENABLE_LOGGING_AND_PROFILING + Atomic32 old_state = NoBarrier_CompareAndSwap(&state_, 0, -1); + ASSERT(old_state >= -1); + if (old_state != 0) return false; + semaphore_->Wait(); +#endif + return true; +} + + +void RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown() { +#ifdef ENABLE_LOGGING_AND_PROFILING + semaphore_->Signal(); +#endif +} + + +void RuntimeProfiler::RemoveDeadSamples() { + for (int i = 0; i < kSamplerWindowSize; i++) { + Object* function = sampler_window_[i]; + if (function != NULL && !HeapObject::cast(function)->IsMarked()) { + sampler_window_[i] = NULL; + } + } +} + + +void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) { + for (int i = 0; i < kSamplerWindowSize; i++) { + visitor->VisitPointer(&sampler_window_[i]); + } +} + + bool RuntimeProfilerRateLimiter::SuspendIfNecessary() { #ifdef ENABLE_LOGGING_AND_PROFILING static const int kNonJSTicksThreshold = 100; - // We suspend the runtime profiler thread when not running - // JavaScript. If the CPU profiler is active we must not do this - // because it samples both JavaScript and C++ code. - if (RuntimeProfiler::IsEnabled() && - !CpuProfiler::is_profiling() && - !(FLAG_prof && FLAG_prof_auto)) { - if (Top::IsInJSState()) { - non_js_ticks_ = 0; + if (RuntimeProfiler::IsSomeIsolateInJS()) { + non_js_ticks_ = 0; + } else { + if (non_js_ticks_ < kNonJSTicksThreshold) { + ++non_js_ticks_; } else { - if (non_js_ticks_ < kNonJSTicksThreshold) { - ++non_js_ticks_; - } else { - if (Top::WaitForJSState()) return true; - } + return RuntimeProfiler::WaitForSomeIsolateToEnterJS(); } } #endif return false; }