Index: src/platform-freebsd.cc |
=================================================================== |
--- src/platform-freebsd.cc (revision 7178) |
+++ src/platform-freebsd.cc (working copy) |
@@ -42,6 +42,7 @@ |
#include <sys/stat.h> // open |
#include <sys/fcntl.h> // open |
#include <unistd.h> // getpagesize |
+// If you don't have execinfo.h then you need devel/libexecinfo from ports. |
#include <execinfo.h> // backtrace, backtrace_symbols |
#include <strings.h> // index |
#include <errno.h> |
@@ -526,6 +527,16 @@ |
return result; |
} |
+ virtual bool TryLock() { |
+ int result = pthread_mutex_trylock(&mutex_); |
+ // Return false if the lock is busy and locking failed. |
+ if (result == EBUSY) { |
+ return false; |
+ } |
+ ASSERT(result == 0); // Verify no other errors. |
+ return true; |
+ } |
+ |
private: |
pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms. |
}; |
@@ -595,60 +606,124 @@ |
#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; |
+} |
+ |
+ |
+class Sampler::PlatformData : public Malloced { |
+ public: |
+ enum SleepInterval { |
+ FULL_INTERVAL, |
+ HALF_INTERVAL |
+ }; |
+ |
+ PlatformData(Sampler* sampler) |
+ : sampler_(sampler), |
+ signal_handler_installed_(false), |
+ signal_sender_launched_(false) { |
+ } |
+ |
+ 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_; |
+}; |
+ |
+ |
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; |
+ } |
+ return; |
+ } |
- TickSample sample; |
- |
- // We always sample the VM state. |
- sample.state = VMState::current_state(); |
- |
- // If profiling, we extract the current pc and sp. |
- if (active_sampler_->IsProfiling()) { |
- // Extracting the sample from the context is extremely machine dependent. |
- ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
- mcontext_t& mcontext = ucontext->uc_mcontext; |
+ if (vm_tid_ != GetThreadID()) return; |
+ |
+ TickSample sample_obj; |
+ TickSample* sample = CpuProfiler::TickSampleEvent(); |
+ if (sample == NULL) sample = &sample_obj; |
+ |
+ // Extracting the sample from the context is extremely machine dependent. |
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
+ mcontext_t& mcontext = ucontext->uc_mcontext; |
#if V8_HOST_ARCH_IA32 |
- sample.pc = reinterpret_cast<Address>(mcontext.mc_eip); |
- sample.sp = reinterpret_cast<Address>(mcontext.mc_esp); |
- sample.fp = reinterpret_cast<Address>(mcontext.mc_ebp); |
+ sample->pc = reinterpret_cast<Address>(mcontext.mc_eip); |
+ sample->sp = reinterpret_cast<Address>(mcontext.mc_esp); |
+ sample->fp = reinterpret_cast<Address>(mcontext.mc_ebp); |
#elif V8_HOST_ARCH_X64 |
- sample.pc = reinterpret_cast<Address>(mcontext.mc_rip); |
- sample.sp = reinterpret_cast<Address>(mcontext.mc_rsp); |
- sample.fp = reinterpret_cast<Address>(mcontext.mc_rbp); |
+ sample->pc = reinterpret_cast<Address>(mcontext.mc_rip); |
+ sample->sp = reinterpret_cast<Address>(mcontext.mc_rsp); |
+ sample->fp = reinterpret_cast<Address>(mcontext.mc_rbp); |
#elif V8_HOST_ARCH_ARM |
- sample.pc = reinterpret_cast<Address>(mcontext.mc_r15); |
- sample.sp = reinterpret_cast<Address>(mcontext.mc_r13); |
- sample.fp = reinterpret_cast<Address>(mcontext.mc_r11); |
+ sample->pc = reinterpret_cast<Address>(mcontext.mc_r15); |
+ sample->sp = reinterpret_cast<Address>(mcontext.mc_r13); |
+ sample->fp = reinterpret_cast<Address>(mcontext.mc_r11); |
#endif |
- active_sampler_->SampleStack(&sample); |
- } |
- |
- active_sampler_->Tick(&sample); |
+ active_sampler_->SampleStack(sample); |
+ active_sampler_->Tick(sample); |
} |
-class Sampler::PlatformData : public Malloced { |
- public: |
- PlatformData() { |
- signal_handler_installed_ = false; |
- } |
+static void* SenderEntry(void* arg) { |
+ Sampler::PlatformData* data = |
+ reinterpret_cast<Sampler::PlatformData*>(arg); |
+ data->SignalSender(); |
+ return 0; |
+} |
- bool signal_handler_installed_; |
- struct sigaction old_signal_handler_; |
- struct itimerval old_timer_value_; |
-}; |
- |
Sampler::Sampler(int interval) |
: interval_(interval), |
profiling_(false), |
active_(false), |
samples_taken_(0) { |
- data_ = new PlatformData(); |
+ data_ = new PlatformData(this); |
} |
@@ -660,7 +735,8 @@ |
void Sampler::Start() { |
// There can only be one active sampler at the time on POSIX |
// platforms. |
- if (active_sampler_ != NULL) return; |
+ ASSERT(!IsActive()); |
+ vm_tid_ = GetThreadID(); |
// Request profiling signals. |
struct sigaction sa; |
@@ -680,21 +756,30 @@ |
// Set this sampler as the active sampler. |
active_sampler_ = this; |
- active_ = true; |
+ 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; |
+ } |
} |
void Sampler::Stop() { |
- // Restore old signal handler |
- if (data_->signal_handler_installed_) { |
- setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL); |
- sigaction(SIGPROF, &data_->old_signal_handler_, 0); |
- data_->signal_handler_installed_ = false; |
+ // This sampler is no longer the active sampler. |
+ active_sampler_ = NULL; |
+ 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; |
} |
- // This sampler is no longer the active sampler. |
- active_sampler_ = NULL; |
- active_ = false; |
} |
#endif // ENABLE_LOGGING_AND_PROFILING |