From dfb4218a048f9c98059c9c384f6ddde020fd4036 Mon Sep 17 00:00:00 2001 From: "jkummerow@chromium.org" Date: Tue, 2 Oct 2012 09:58:11 +0000 Subject: [PATCH] Moving cpu profiling into its own thread. BUG=None Review URL: https://codereview.chromium.org/10857035 Patch from Sergey Rogulenko . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12649 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/cpu-profiler.cc | 53 +++++++++----- src/cpu-profiler.h | 10 ++- src/flag-definitions.h | 4 ++ src/platform-cygwin.cc | 5 ++ src/platform-freebsd.cc | 5 ++ src/platform-linux.cc | 145 +++++++++++++++++++-------------------- src/platform-macos.cc | 5 ++ src/platform-openbsd.cc | 5 ++ src/platform-solaris.cc | 5 ++ src/platform-win32.cc | 5 ++ src/platform.h | 3 + test/cctest/test-cpu-profiler.cc | 8 +-- 12 files changed, 156 insertions(+), 97 deletions(-) diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc index 3cbac77..0caead0 100644 --- a/src/cpu-profiler.cc +++ b/src/cpu-profiler.cc @@ -45,10 +45,14 @@ static const int kTickSamplesBufferChunksCount = 16; static const int kProfilerStackSize = 64 * KB; -ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator) +ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator, + Sampler* sampler, + int period_in_useconds) : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)), generator_(generator), + sampler_(sampler), running_(true), + period_in_useconds_(period_in_useconds), ticks_buffer_(sizeof(TickSampleEventRecord), kTickSamplesBufferChunkSize, kTickSamplesBufferChunksCount), @@ -206,8 +210,9 @@ bool ProfilerEventsProcessor::ProcessCodeEvent(unsigned* dequeue_order) { } -bool ProfilerEventsProcessor::ProcessTicks(unsigned dequeue_order) { - while (true) { +bool ProfilerEventsProcessor::ProcessTicks(int64_t stop_time, + unsigned dequeue_order) { + while (stop_time == -1 || OS::Ticks() < stop_time) { if (!ticks_from_vm_buffer_.IsEmpty() && ticks_from_vm_buffer_.Peek()->order == dequeue_order) { TickSampleEventRecord record; @@ -236,6 +241,19 @@ bool ProfilerEventsProcessor::ProcessTicks(unsigned dequeue_order) { return true; } } + return false; +} + + +void ProfilerEventsProcessor::ProcessEventsQueue(int64_t stop_time, + unsigned* dequeue_order) { + while (OS::Ticks() < stop_time) { + if (ProcessTicks(stop_time, *dequeue_order)) { + // All ticks of the current dequeue_order are processed, + // proceed to the next code event. + ProcessCodeEvent(dequeue_order); + } + } } @@ -243,19 +261,18 @@ void ProfilerEventsProcessor::Run() { unsigned dequeue_order = 0; while (running_) { - // Process ticks until we have any. - if (ProcessTicks(dequeue_order)) { - // All ticks of the current dequeue_order are processed, - // proceed to the next code event. - ProcessCodeEvent(&dequeue_order); + int64_t stop_time = OS::Ticks() + period_in_useconds_; + if (sampler_ != NULL) { + sampler_->DoSample(); } - YieldCPU(); + ProcessEventsQueue(stop_time, &dequeue_order); } // Process remaining tick events. ticks_buffer_.FlushResidualRecords(); // Perform processing until we have tick events, skip remaining code events. - while (ProcessTicks(dequeue_order) && ProcessCodeEvent(&dequeue_order)) { } + while (ProcessTicks(-1, dequeue_order) && ProcessCodeEvent(&dequeue_order)) { + } } @@ -486,13 +503,15 @@ void CpuProfiler::StartProcessorIfNotStarted() { if (processor_ == NULL) { Isolate* isolate = Isolate::Current(); + Sampler* sampler = isolate->logger()->sampler(); // Disable logging when using the new implementation. saved_logging_nesting_ = isolate->logger()->logging_nesting_; isolate->logger()->logging_nesting_ = 0; generator_ = new ProfileGenerator(profiles_); - processor_ = new ProfilerEventsProcessor(generator_); + processor_ = new ProfilerEventsProcessor(generator_, + sampler, + FLAG_cpu_profiler_sampling_period); NoBarrier_Store(&is_profiling_, true); - processor_->Start(); // Enumerate stuff we already have in the heap. if (isolate->heap()->HasBeenSetUp()) { if (!FLAG_prof_browser_mode) { @@ -505,12 +524,12 @@ void CpuProfiler::StartProcessorIfNotStarted() { isolate->logger()->LogAccessorCallbacks(); } // Enable stack sampling. - Sampler* sampler = reinterpret_cast(isolate->logger()->ticker_); if (!sampler->IsActive()) { sampler->Start(); need_to_stop_sampler_ = true; } sampler->IncreaseProfilingDepth(); + processor_->Start(); } } @@ -545,16 +564,16 @@ void CpuProfiler::StopProcessorIfLastProfile(const char* title) { void CpuProfiler::StopProcessor() { + NoBarrier_Store(&is_profiling_, false); + processor_->Stop(); + processor_->Join(); Logger* logger = Isolate::Current()->logger(); - Sampler* sampler = reinterpret_cast(logger->ticker_); + Sampler* sampler = logger->sampler(); sampler->DecreaseProfilingDepth(); if (need_to_stop_sampler_) { sampler->Stop(); need_to_stop_sampler_ = false; } - NoBarrier_Store(&is_profiling_, false); - processor_->Stop(); - processor_->Join(); delete processor_; delete generator_; processor_ = NULL; diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h index 9cd4484..d335ded 100644 --- a/src/cpu-profiler.h +++ b/src/cpu-profiler.h @@ -124,7 +124,9 @@ class TickSampleEventRecord { // methods called by event producers: VM and stack sampler threads. class ProfilerEventsProcessor : public Thread { public: - explicit ProfilerEventsProcessor(ProfileGenerator* generator); + explicit ProfilerEventsProcessor(ProfileGenerator* generator, + Sampler* sampler, + int period_in_useconds); virtual ~ProfilerEventsProcessor() {} // Thread control. @@ -172,12 +174,16 @@ class ProfilerEventsProcessor : public Thread { // Called from events processing thread (Run() method.) bool ProcessCodeEvent(unsigned* dequeue_order); - bool ProcessTicks(unsigned dequeue_order); + bool ProcessTicks(int64_t stop_time, unsigned dequeue_order); + void ProcessEventsQueue(int64_t stop_time, unsigned* dequeue_order); INLINE(static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag)); ProfileGenerator* generator_; + Sampler* sampler_; bool running_; + // Sampling period in microseconds. + const int period_in_useconds_; UnboundQueue events_buffer_; SamplingCircularQueue ticks_buffer_; UnboundQueue ticks_from_vm_buffer_; diff --git a/src/flag-definitions.h b/src/flag-definitions.h index d9fa126..81ec78a 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -335,6 +335,10 @@ DEFINE_bool(compilation_cache, true, "enable compilation cache") DEFINE_bool(cache_prototype_transitions, true, "cache prototype transitions") +// cpu-profiler.cc +DEFINE_int(cpu_profiler_sampling_period, 1000, + "CPU profiler sampling period in microseconds") + // debug.cc DEFINE_bool(trace_debug_json, false, "trace debugging JSON request/response") DEFINE_bool(debugger_auto_break, true, diff --git a/src/platform-cygwin.cc b/src/platform-cygwin.cc index 089ea38..702b1d2 100644 --- a/src/platform-cygwin.cc +++ b/src/platform-cygwin.cc @@ -768,6 +768,11 @@ Sampler::~Sampler() { } +void Sampler::DoSample() { + // TODO(rogulenko): implement +} + + void Sampler::Start() { ASSERT(!IsActive()); SetActive(true); diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc index 511759c..87c252c 100644 --- a/src/platform-freebsd.cc +++ b/src/platform-freebsd.cc @@ -884,6 +884,11 @@ Sampler::~Sampler() { } +void Sampler::DoSample() { + // TODO(rogulenko): implement +} + + void Sampler::Start() { ASSERT(!IsActive()); SetActive(true); diff --git a/src/platform-linux.cc b/src/platform-linux.cc index b9ce9d9..308cdf8 100644 --- a/src/platform-linux.cc +++ b/src/platform-linux.cc @@ -1055,13 +1055,70 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { } +class CpuProfilerSignalHandler { + public: + static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); } + static void TearDown() { delete mutex_; } + + static void InstallSignalHandler() { + struct sigaction sa; + ScopedLock lock(mutex_); + if (signal_handler_installed_counter_ > 0) { + signal_handler_installed_counter_++; + return; + } + sa.sa_sigaction = ProfilerSignalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0) { + signal_handler_installed_counter_++; + } + } + + static void RestoreSignalHandler() { + ScopedLock lock(mutex_); + if (signal_handler_installed_counter_ == 0) + return; + if (signal_handler_installed_counter_ == 1) { + sigaction(SIGPROF, &old_signal_handler_, 0); + } + signal_handler_installed_counter_--; + } + + static bool signal_handler_installed() { + return signal_handler_installed_counter_ > 0; + } + + private: + static int signal_handler_installed_counter_; + static struct sigaction old_signal_handler_; + static Mutex* mutex_; +}; + + +int CpuProfilerSignalHandler::signal_handler_installed_counter_ = 0; +struct sigaction CpuProfilerSignalHandler::old_signal_handler_; +Mutex* CpuProfilerSignalHandler::mutex_ = NULL; + + class Sampler::PlatformData : public Malloced { public: - PlatformData() : vm_tid_(GetThreadID()) {} + PlatformData() + : vm_tgid_(getpid()), + vm_tid_(GetThreadID()) {} - int vm_tid() const { return vm_tid_; } + void SendProfilingSignal() { + if (!CpuProfilerSignalHandler::signal_handler_installed()) return; + // Glibc doesn't provide a wrapper for tgkill(2). +#if defined(ANDROID) + syscall(__NR_tgkill, vm_tgid_, vm_tid_, SIGPROF); +#else + syscall(SYS_tgkill, vm_tgid_, vm_tid_, SIGPROF); +#endif + } private: + const int vm_tgid_; const int vm_tid_; }; @@ -1077,28 +1134,11 @@ class SignalSender : public Thread { explicit SignalSender(int interval) : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)), - vm_tgid_(getpid()), interval_(interval) {} static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); } static void TearDown() { delete mutex_; } - static void InstallSignalHandler() { - struct sigaction sa; - sa.sa_sigaction = ProfilerSignalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_SIGINFO; - signal_handler_installed_ = - (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); - } - - static void RestoreSignalHandler() { - if (signal_handler_installed_) { - sigaction(SIGPROF, &old_signal_handler_, 0); - signal_handler_installed_ = false; - } - } - static void AddActiveSampler(Sampler* sampler) { ScopedLock lock(mutex_); SamplerRegistry::AddActiveSampler(sampler); @@ -1119,7 +1159,6 @@ class SignalSender : public Thread { RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_); delete instance_; instance_ = NULL; - RestoreSignalHandler(); } } @@ -1128,67 +1167,21 @@ class SignalSender : public Thread { SamplerRegistry::State state; while ((state = SamplerRegistry::GetState()) != SamplerRegistry::HAS_NO_SAMPLERS) { - bool cpu_profiling_enabled = - (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS); - bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled(); - if (cpu_profiling_enabled && !signal_handler_installed_) { - InstallSignalHandler(); - } else if (!cpu_profiling_enabled && signal_handler_installed_) { - RestoreSignalHandler(); - } - // When CPU profiling is enabled both JavaScript and C++ code is - // profiled. We must not suspend. - if (!cpu_profiling_enabled) { - if (rate_limiter_.SuspendIfNecessary()) continue; - } - if (cpu_profiling_enabled && runtime_profiler_enabled) { - if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) { - return; - } - Sleep(HALF_INTERVAL); + if (rate_limiter_.SuspendIfNecessary()) continue; + if (RuntimeProfiler::IsEnabled()) { if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) { return; } - Sleep(HALF_INTERVAL); - } else { - if (cpu_profiling_enabled) { - if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, - this)) { - return; - } - } - if (runtime_profiler_enabled) { - if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, - NULL)) { - return; - } - } - Sleep(FULL_INTERVAL); } + Sleep(FULL_INTERVAL); } } - static void DoCpuProfile(Sampler* sampler, void* raw_sender) { - if (!sampler->IsProfiling()) return; - SignalSender* sender = reinterpret_cast(raw_sender); - sender->SendProfilingSignal(sampler->platform_data()->vm_tid()); - } - static void DoRuntimeProfile(Sampler* sampler, void* ignored) { if (!sampler->isolate()->IsInitialized()) return; sampler->isolate()->runtime_profiler()->NotifyTick(); } - void SendProfilingSignal(int tid) { - if (!signal_handler_installed_) return; - // Glibc doesn't provide a wrapper for tgkill(2). -#if defined(ANDROID) - syscall(__NR_tgkill, vm_tgid_, tid, SIGPROF); -#else - syscall(SYS_tgkill, vm_tgid_, tid, SIGPROF); -#endif - } - void Sleep(SleepInterval full_or_half) { // Convert ms to us and subtract 100 us to compensate delays // occuring during signal delivery. @@ -1211,15 +1204,12 @@ class SignalSender : public Thread { #endif // ANDROID } - const int vm_tgid_; const int interval_; RuntimeProfilerRateLimiter rate_limiter_; // Protects the process wide state below. static Mutex* mutex_; static SignalSender* instance_; - static bool signal_handler_installed_; - static struct sigaction old_signal_handler_; private: DISALLOW_COPY_AND_ASSIGN(SignalSender); @@ -1228,8 +1218,6 @@ class SignalSender : public Thread { Mutex* SignalSender::mutex_ = NULL; SignalSender* SignalSender::instance_ = NULL; -struct sigaction SignalSender::old_signal_handler_; -bool SignalSender::signal_handler_installed_ = false; void OS::SetUp() { @@ -1257,11 +1245,13 @@ void OS::SetUp() { } #endif SignalSender::SetUp(); + CpuProfilerSignalHandler::SetUp(); } void OS::TearDown() { SignalSender::TearDown(); + CpuProfilerSignalHandler::TearDown(); delete limit_mutex; } @@ -1282,8 +1272,14 @@ Sampler::~Sampler() { } +void Sampler::DoSample() { + platform_data()->SendProfilingSignal(); +} + + void Sampler::Start() { ASSERT(!IsActive()); + CpuProfilerSignalHandler::InstallSignalHandler(); SetActive(true); SignalSender::AddActiveSampler(this); } @@ -1291,6 +1287,7 @@ void Sampler::Start() { void Sampler::Stop() { ASSERT(IsActive()); + CpuProfilerSignalHandler::RestoreSignalHandler(); SignalSender::RemoveActiveSampler(this); SetActive(false); } diff --git a/src/platform-macos.cc b/src/platform-macos.cc index a216f6e..704fe12 100644 --- a/src/platform-macos.cc +++ b/src/platform-macos.cc @@ -910,6 +910,11 @@ Sampler::~Sampler() { } +void Sampler::DoSample() { + // TODO(rogulenko): implement +} + + void Sampler::Start() { ASSERT(!IsActive()); SetActive(true); diff --git a/src/platform-openbsd.cc b/src/platform-openbsd.cc index 408d4dc..cf149cd 100644 --- a/src/platform-openbsd.cc +++ b/src/platform-openbsd.cc @@ -964,6 +964,11 @@ Sampler::~Sampler() { } +void Sampler::DoSample() { + // TODO(rogulenko): implement +} + + void Sampler::Start() { ASSERT(!IsActive()); SetActive(true); diff --git a/src/platform-solaris.cc b/src/platform-solaris.cc index 4248ea2..d6fa415 100644 --- a/src/platform-solaris.cc +++ b/src/platform-solaris.cc @@ -887,6 +887,11 @@ Sampler::~Sampler() { } +void Sampler::DoSample() { + // TODO(rogulenko): implement +} + + void Sampler::Start() { ASSERT(!IsActive()); SetActive(true); diff --git a/src/platform-win32.cc b/src/platform-win32.cc index 49463be..4ea4f92 100644 --- a/src/platform-win32.cc +++ b/src/platform-win32.cc @@ -2114,6 +2114,11 @@ Sampler::~Sampler() { } +void Sampler::DoSample() { + // TODO(rogulenko): implement +} + + void Sampler::Start() { ASSERT(!IsActive()); SetActive(true); diff --git a/src/platform.h b/src/platform.h index f50e713..4f8fdc3 100644 --- a/src/platform.h +++ b/src/platform.h @@ -741,6 +741,9 @@ class Sampler { IncSamplesTaken(); } + // Performs platform-specific stack sampling. + void DoSample(); + // This method is called for each sampling period with the current // program counter. virtual void Tick(TickSample* sample) = 0; diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc index b10e688..20af070 100644 --- a/test/cctest/test-cpu-profiler.cc +++ b/test/cctest/test-cpu-profiler.cc @@ -20,7 +20,7 @@ using i::TokenEnumerator; TEST(StartStop) { CpuProfilesCollection profiles; ProfileGenerator generator(&profiles); - ProfilerEventsProcessor processor(&generator); + ProfilerEventsProcessor processor(&generator, NULL, 1000); processor.Start(); processor.Stop(); processor.Join(); @@ -81,7 +81,7 @@ TEST(CodeEvents) { CpuProfilesCollection profiles; profiles.StartProfiling("", 1); ProfileGenerator generator(&profiles); - ProfilerEventsProcessor processor(&generator); + ProfilerEventsProcessor processor(&generator, NULL, 1000); processor.Start(); // Enqueue code creation events. @@ -142,7 +142,7 @@ TEST(TickEvents) { CpuProfilesCollection profiles; profiles.StartProfiling("", 1); ProfileGenerator generator(&profiles); - ProfilerEventsProcessor processor(&generator); + ProfilerEventsProcessor processor(&generator, NULL, 1000); processor.Start(); processor.CodeCreateEvent(i::Logger::BUILTIN_TAG, @@ -232,7 +232,7 @@ TEST(Issue1398) { CpuProfilesCollection profiles; profiles.StartProfiling("", 1); ProfileGenerator generator(&profiles); - ProfilerEventsProcessor processor(&generator); + ProfilerEventsProcessor processor(&generator, NULL, 1000); processor.Start(); processor.CodeCreateEvent(i::Logger::BUILTIN_TAG, -- 2.7.4