Index: src/platform-macos.cc |
diff --git a/src/platform-macos.cc b/src/platform-macos.cc |
index f5b64587e4b02ebcfc821abbd912a82e29fa9456..6e79de2095f8a4d591ecbed3bf874f63c33bbee5 100644 |
--- a/src/platform-macos.cc |
+++ b/src/platform-macos.cc |
@@ -38,6 +38,7 @@ |
#include <pthread.h> |
#include <semaphore.h> |
#include <signal.h> |
+#include <mach/mach.h> |
#include <mach/semaphore.h> |
#include <mach/task.h> |
#include <sys/time.h> |
@@ -475,63 +476,94 @@ Semaphore* OS::CreateSemaphore(int count) { |
#ifdef ENABLE_LOGGING_AND_PROFILING |
-static Sampler* active_sampler_ = NULL; |
- |
-static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { |
- USE(info); |
- if (signal != SIGPROF) return; |
- if (active_sampler_ == NULL) return; |
- |
- TickSample sample; |
+class Sampler::PlatformData : public Malloced { |
+ public: |
+ explicit PlatformData(Sampler* sampler) |
+ : sampler_(sampler), |
+ task_self_(mach_task_self()), |
+ profiled_thread_(0), |
+ sampler_thread_(0) { |
+ } |
- // 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; |
+ Sampler* sampler_; |
+ // Note: for profiled_thread_ Mach primitives are used instead of PThread's |
+ // because the latter doesn't provide thread manipulation primitives required. |
+ // For details, consult "Mac OS X Internals" book, Section 7.3. |
+ mach_port_t task_self_; |
+ thread_act_t profiled_thread_; |
+ pthread_t sampler_thread_; |
+ |
+ // Sampler thread handler. |
+ void Runner() { |
+ // Loop until the sampler is disengaged. |
+ while (sampler_->IsActive()) { |
+ TickSample sample; |
+ |
+ // If profiling, we record the pc and sp of the profiled thread. |
+ if (sampler_->IsProfiling() |
+ && KERN_SUCCESS == thread_suspend(profiled_thread_)) { |
#if V8_HOST_ARCH_X64 |
- UNIMPLEMENTED(); |
- USE(mcontext); |
- sample.pc = 0; |
- sample.sp = 0; |
- sample.fp = 0; |
+ thread_state_flavor_t flavor = x86_THREAD_STATE64; |
+ x86_thread_state64_t state; |
+ mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; |
+#elif V8_HOST_ARCH_IA32 |
+ thread_state_flavor_t flavor = i386_THREAD_STATE; |
+ i386_thread_state_t state; |
+ mach_msg_type_number_t count = i386_THREAD_STATE_COUNT; |
+#else |
+#error Unsupported Mac OS X host architecture. |
+#endif // V8_TARGET_ARCH_IA32 |
+ if (KERN_SUCCESS == thread_get_state(profiled_thread_, |
+ flavor, |
+ (natural_t*)&state, |
+ &count)) { |
+#if V8_HOST_ARCH_X64 |
+ UNIMPLEMENTED(); |
+ sample.pc = 0; |
+ sample.sp = 0; |
+ sample.fp = 0; |
#elif V8_HOST_ARCH_IA32 |
#if __DARWIN_UNIX03 |
- sample.pc = mcontext->__ss.__eip; |
- sample.sp = mcontext->__ss.__esp; |
- sample.fp = mcontext->__ss.__ebp; |
+ sample.pc = state.__eip; |
+ sample.sp = state.__esp; |
+ sample.fp = state.__ebp; |
#else // !__DARWIN_UNIX03 |
- sample.pc = mcontext->ss.eip; |
- sample.sp = mcontext->ss.esp; |
- sample.fp = mcontext->ss.ebp; |
+ sample.pc = state.eip; |
+ sample.sp = state.esp; |
+ sample.fp = state.ebp; |
#endif // __DARWIN_UNIX03 |
#else |
#error Unsupported Mac OS X host architecture. |
#endif // V8_HOST_ARCH_IA32 |
+ sampler_->SampleStack(&sample); |
+ } |
+ thread_resume(profiled_thread_); |
+ } |
+ |
+ // We always sample the VM state. |
+ sample.state = Logger::state(); |
+ // Invoke tick handler with program counter and stack pointer. |
+ sampler_->Tick(&sample); |
+ |
+ // Wait until next sampling. |
+ usleep(sampler_->interval_ * 1000); |
+ } |
} |
+}; |
- // We always sample the VM state. |
- sample.state = Logger::state(); |
- active_sampler_->Tick(&sample); |
+// Entry point for sampler thread. |
+static void* SamplerEntry(void* arg) { |
+ Sampler::PlatformData* data = |
+ reinterpret_cast<Sampler::PlatformData*>(arg); |
+ data->Runner(); |
+ return 0; |
} |
-class Sampler::PlatformData : public Malloced { |
- public: |
- PlatformData() { |
- signal_handler_installed_ = false; |
- } |
- |
- bool signal_handler_installed_; |
- struct sigaction old_signal_handler_; |
- struct itimerval old_timer_value_; |
-}; |
- |
- |
Sampler::Sampler(int interval, bool profiling) |
: interval_(interval), profiling_(profiling), active_(false) { |
- data_ = new PlatformData(); |
+ data_ = new PlatformData(this); |
} |
@@ -541,43 +573,40 @@ Sampler::~Sampler() { |
void Sampler::Start() { |
- // There can only be one active sampler at the time on POSIX |
- // platforms. |
- if (active_sampler_ != NULL) return; |
- |
- // 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; |
+ // If we are profiling, we need to be able to access the calling |
+ // thread. |
+ if (IsProfiling()) { |
+ data_->profiled_thread_ = mach_thread_self(); |
+ } |
+ |
+ // Create sampler thread with high priority. |
+ // According to POSIX spec, when SCHED_FIFO policy is used, a thread |
+ // runs until it exits or blocks. |
+ pthread_attr_t sched_attr; |
+ sched_param fifo_param; |
+ pthread_attr_init(&sched_attr); |
+ pthread_attr_setinheritsched(&sched_attr, PTHREAD_EXPLICIT_SCHED); |
+ pthread_attr_setschedpolicy(&sched_attr, SCHED_FIFO); |
+ fifo_param.sched_priority = sched_get_priority_max(SCHED_FIFO); |
+ pthread_attr_setschedparam(&sched_attr, &fifo_param); |
+ |
active_ = true; |
+ pthread_create(&data_->sampler_thread_, &sched_attr, SamplerEntry, data_); |
} |
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; |
+ // Seting active to false triggers termination of the sampler |
+ // thread. |
active_ = false; |
+ |
+ // Wait for sampler thread to terminate. |
+ pthread_join(data_->sampler_thread_, NULL); |
+ |
+ // Deallocate Mach port for thread. |
+ if (IsProfiling()) { |
+ mach_port_deallocate(data_->task_self_, data_->profiled_thread_); |
+ } |
} |
#endif // ENABLE_LOGGING_AND_PROFILING |