From 3187ef6ce823680b85433bfb49d1a9082e3ad3e0 Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Tue, 29 Mar 2011 06:18:16 +0000 Subject: [PATCH] Build and pass all tests on FreeBSD. Review URL: http://codereview.chromium.org/6726050 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7397 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/platform-freebsd.cc | 275 +++++++++++++++++++++++++++++------------------- 1 file changed, 164 insertions(+), 111 deletions(-) diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc index 06f832b..2a73b6e 100644 --- a/src/platform-freebsd.cc +++ b/src/platform-freebsd.cc @@ -75,6 +75,9 @@ double ceiling(double x) { } +static Mutex* limit_mutex = NULL; + + void OS::Setup() { // Seed the random number generator. // Convert the current time to a 64-bit integer first, before converting it @@ -83,6 +86,7 @@ void OS::Setup() { // call this setup code within the same millisecond. uint64_t seed = static_cast(TimeCurrentMillis()); srandom(static_cast(seed)); + limit_mutex = CreateMutex(); } @@ -131,6 +135,9 @@ static void* highest_ever_allocated = reinterpret_cast(0); static void UpdateAllocatedSpaceLimits(void* address, int size) { + ASSERT(limit_mutex != NULL); + ScopedLock lock(limit_mutex); + lowest_ever_allocated = Min(lowest_ever_allocated, address); highest_ever_allocated = Max(highest_ever_allocated, @@ -300,7 +307,7 @@ void OS::LogSharedLibraryAddresses() { // There may be no filename in this line. Skip to next. if (start_of_path == NULL) continue; buffer[bytes_read] = 0; - LOG(SharedLibraryEvent(start_of_path, start, end)); + LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end)); } close(fd); #endif @@ -619,10 +626,6 @@ Semaphore* OS::CreateSemaphore(int count) { #ifdef ENABLE_LOGGING_AND_PROFILING -static Sampler* active_sampler_ = NULL; -static pthread_t vm_tid_ = NULL; - - static pthread_t GetThreadID() { pthread_t thread_id = pthread_self(); return thread_id; @@ -631,81 +634,34 @@ static pthread_t GetThreadID() { class Sampler::PlatformData : public Malloced { public: - enum SleepInterval { - FULL_INTERVAL, - HALF_INTERVAL - }; + PlatformData() : vm_tid_(GetThreadID()) {} - explicit PlatformData(Sampler* sampler) - : sampler_(sampler), - signal_handler_installed_(false), - signal_sender_launched_(false) { - } + pthread_t vm_tid() const { return vm_tid_; } - void SignalSender() { - while (sampler_->IsActive()) { - if (rate_limiter_.SuspendIfNecessary()) continue; - if (sampler_->IsProfiling() && RuntimeProfiler::IsEnabled()) { - Sleep(FULL_INTERVAL); - RuntimeProfiler::NotifyTick(); - } else { - if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick(); - Sleep(FULL_INTERVAL); - } - } - } - - void Sleep(SleepInterval full_or_half) { - // Convert ms to us and subtract 100 us to compensate delays - // occuring during signal delivery. - useconds_t interval = sampler_->interval_ * 1000 - 100; - if (full_or_half == HALF_INTERVAL) interval /= 2; - int result = usleep(interval); -#ifdef DEBUG - if (result != 0 && errno != EINTR) { - fprintf(stderr, - "SignalSender usleep error; interval = %u, errno = %d\n", - interval, - errno); - ASSERT(result == 0 || errno == EINTR); - } -#endif - USE(result); - } - - Sampler* sampler_; - bool signal_handler_installed_; - struct sigaction old_signal_handler_; - struct itimerval old_timer_value_; - bool signal_sender_launched_; - pthread_t signal_sender_thread_; - RuntimeProfilerRateLimiter rate_limiter_; + private: + pthread_t vm_tid_; }; static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { USE(info); if (signal != SIGPROF) return; - if (active_sampler_ == NULL) return; - if (!active_sampler_->IsActive()) { - // Restore old signal handler - Sampler::PlatformData* data = active_sampler_->data(); - if (data->signal_handler_installed_) { - sigaction(SIGPROF, &data->old_signal_handler_, 0); - data->signal_handler_installed_ = false; - } + Isolate* isolate = Isolate::UncheckedCurrent(); + if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) { + // We require a fully initialized and entered isolate. return; } - - if (vm_tid_ != GetThreadID()) return; + Sampler* sampler = isolate->logger()->sampler(); + if (sampler == NULL || !sampler->IsActive()) return; TickSample sample_obj; - TickSample* sample = CpuProfiler::TickSampleEvent(); + TickSample* sample = CpuProfiler::TickSampleEvent(isolate); if (sample == NULL) sample = &sample_obj; // Extracting the sample from the context is extremely machine dependent. ucontext_t* ucontext = reinterpret_cast(context); mcontext_t& mcontext = ucontext->uc_mcontext; + sample->state = isolate->current_vm_state(); #if V8_HOST_ARCH_IA32 sample->pc = reinterpret_cast
(mcontext.mc_eip); sample->sp = reinterpret_cast
(mcontext.mc_esp); @@ -719,17 +675,149 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sample->sp = reinterpret_cast
(mcontext.mc_r13); sample->fp = reinterpret_cast
(mcontext.mc_r11); #endif - active_sampler_->SampleStack(sample); - active_sampler_->Tick(sample); + sampler->SampleStack(sample); + sampler->Tick(sample); } -static void* SenderEntry(void* arg) { - Sampler::PlatformData* data = - reinterpret_cast(arg); - data->SignalSender(); - return 0; -} +class SignalSender : public Thread { + public: + enum SleepInterval { + HALF_INTERVAL, + FULL_INTERVAL + }; + + explicit SignalSender(int interval) + : Thread(NULL, "SignalSender"), + interval_(interval) {} + + static void AddActiveSampler(Sampler* sampler) { + ScopedLock lock(mutex_); + SamplerRegistry::AddActiveSampler(sampler); + if (instance_ == NULL) { + // Install a signal handler. + 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); + + // Start a thread that sends SIGPROF signal to VM threads. + instance_ = new SignalSender(sampler->interval()); + instance_->Start(); + } else { + ASSERT(instance_->interval_ == sampler->interval()); + } + } + + static void RemoveActiveSampler(Sampler* sampler) { + ScopedLock lock(mutex_); + SamplerRegistry::RemoveActiveSampler(sampler); + if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) { + RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown(); + instance_->Join(); + delete instance_; + instance_ = NULL; + + // Restore the old signal handler. + if (signal_handler_installed_) { + sigaction(SIGPROF, &old_signal_handler_, 0); + signal_handler_installed_ = false; + } + } + } + + // Implement Thread::Run(). + virtual void Run() { + 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(); + // 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 (!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); + } + } + } + + 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(pthread_t tid) { + if (!signal_handler_installed_) return; + pthread_kill(tid, SIGPROF); + } + + void Sleep(SleepInterval full_or_half) { + // Convert ms to us and subtract 100 us to compensate delays + // occuring during signal delivery. + useconds_t interval = interval_ * 1000 - 100; + if (full_or_half == HALF_INTERVAL) interval /= 2; + int result = usleep(interval); +#ifdef DEBUG + if (result != 0 && errno != EINTR) { + fprintf(stderr, + "SignalSender usleep error; interval = %u, errno = %d\n", + interval, + errno); + ASSERT(result == 0 || errno == EINTR); + } +#endif + USE(result); + } + + 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_; + + DISALLOW_COPY_AND_ASSIGN(SignalSender); +}; + +Mutex* SignalSender::mutex_ = OS::CreateMutex(); +SignalSender* SignalSender::instance_ = NULL; +struct sigaction SignalSender::old_signal_handler_; +bool SignalSender::signal_handler_installed_ = false; Sampler::Sampler(Isolate* isolate, int interval) @@ -738,62 +826,27 @@ Sampler::Sampler(Isolate* isolate, int interval) profiling_(false), active_(false), samples_taken_(0) { - data_ = new PlatformData(this); + data_ = new PlatformData; } Sampler::~Sampler() { + ASSERT(!IsActive()); delete data_; } void Sampler::Start() { - // There can only be one active sampler at the time on POSIX - // platforms. ASSERT(!IsActive()); - vm_tid_ = GetThreadID(); - - // Request profiling signals. - struct sigaction sa; - sa.sa_sigaction = ProfilerSignalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; - data_->signal_handler_installed_ = true; - - // Set the itimer to generate a tick for each interval. - itimerval itimer; - itimer.it_interval.tv_sec = interval_ / 1000; - itimer.it_interval.tv_usec = (interval_ % 1000) * 1000; - itimer.it_value.tv_sec = itimer.it_interval.tv_sec; - itimer.it_value.tv_usec = itimer.it_interval.tv_usec; - setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_); - - // Set this sampler as the active sampler. - active_sampler_ = this; SetActive(true); - - // There's no way to send a signal to a thread on FreeBSD, but we can - // start a thread that uses the stack guard to interrupt the JS thread. - if (pthread_create( - &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) { - data_->signal_sender_launched_ = true; - } + SignalSender::AddActiveSampler(this); } void Sampler::Stop() { - // This sampler is no longer the active sampler. - active_sampler_ = NULL; + ASSERT(IsActive()); + SignalSender::RemoveActiveSampler(this); SetActive(false); - - // Wait for signal sender termination (it will exit after setting - // active_ to false). - if (data_->signal_sender_launched_) { - Top::WakeUpRuntimeProfilerThreadBeforeShutdown(); - pthread_join(data_->signal_sender_thread_, NULL); - data_->signal_sender_launched_ = false; - } } #endif // ENABLE_LOGGING_AND_PROFILING -- 2.7.4