Reimplement profiler sampler on Mac OS X to get it working under Chromium.

Previous implementation of sampler for OS X was copied from the Linux one. But BSD (OS X) and Linux has a very important difference in signal handling. LinuxThreads doesn't support the notion of process-directed signals. So, the SIGPROF signal was directed to the thread that installed the handler---the V8 thread. But on BSD, signal handling is implemented according to POSIX spec, where process-directed signal is to be handled by an arbitrary selected thread. By a coincidence, in V8's sample shell and in Chromium's test shell, V8's thread was picked almost every time, so sampling seemed working. But not in case of Chromium.

So, I've changed the implementation of profiler sampler to use the same scheme as on Windows---a dedicated thread with high priority is used to periodically pause and sample V8's thread.

Review URL: http://codereview.chromium.org/147150

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2315 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 65e71082
...@@ -176,8 +176,11 @@ class Ticker: public Sampler { ...@@ -176,8 +176,11 @@ class Ticker: public Sampler {
~Ticker() { if (IsActive()) Stop(); } ~Ticker() { if (IsActive()) Stop(); }
void SampleStack(TickSample* sample) {
StackTracer::Trace(sample);
}
void Tick(TickSample* sample) { void Tick(TickSample* sample) {
if (IsProfiling()) StackTracer::Trace(sample);
if (profiler_) profiler_->Insert(sample); if (profiler_) profiler_->Insert(sample);
if (window_) window_->AddState(sample->state); if (window_) window_->AddState(sample->state);
} }
......
...@@ -561,6 +561,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { ...@@ -561,6 +561,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
sample.sp = mcontext.mc_esp; sample.sp = mcontext.mc_esp;
sample.fp = mcontext.mc_ebp; sample.fp = mcontext.mc_ebp;
#endif #endif
active_sampler_->SampleStack(&sample);
} }
// We always sample the VM state. // We always sample the VM state.
......
...@@ -639,6 +639,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { ...@@ -639,6 +639,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
sample.fp = mcontext.arm_fp; sample.fp = mcontext.arm_fp;
#endif #endif
#endif #endif
active_sampler_->SampleStack(&sample);
} }
// We always sample the VM state. // We always sample the VM state.
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <pthread.h> #include <pthread.h>
#include <semaphore.h> #include <semaphore.h>
#include <signal.h> #include <signal.h>
#include <mach/mach.h>
#include <mach/semaphore.h> #include <mach/semaphore.h>
#include <mach/task.h> #include <mach/task.h>
#include <sys/time.h> #include <sys/time.h>
...@@ -475,63 +476,94 @@ Semaphore* OS::CreateSemaphore(int count) { ...@@ -475,63 +476,94 @@ Semaphore* OS::CreateSemaphore(int count) {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
static Sampler* active_sampler_ = NULL; class Sampler::PlatformData : public Malloced {
public:
static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { explicit PlatformData(Sampler* sampler)
USE(info); : sampler_(sampler),
if (signal != SIGPROF) return; task_self_(mach_task_self()),
if (active_sampler_ == NULL) return; profiled_thread_(0),
sampler_thread_(0) {
}
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; TickSample sample;
// If profiling, we extract the current pc and sp. // If profiling, we record the pc and sp of the profiled thread.
if (active_sampler_->IsProfiling()) { if (sampler_->IsProfiling()
// Extracting the sample from the context is extremely machine dependent. && KERN_SUCCESS == thread_suspend(profiled_thread_)) {
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); #if V8_HOST_ARCH_X64
mcontext_t& mcontext = ucontext->uc_mcontext; 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 #if V8_HOST_ARCH_X64
UNIMPLEMENTED(); UNIMPLEMENTED();
USE(mcontext);
sample.pc = 0; sample.pc = 0;
sample.sp = 0; sample.sp = 0;
sample.fp = 0; sample.fp = 0;
#elif V8_HOST_ARCH_IA32 #elif V8_HOST_ARCH_IA32
#if __DARWIN_UNIX03 #if __DARWIN_UNIX03
sample.pc = mcontext->__ss.__eip; sample.pc = state.__eip;
sample.sp = mcontext->__ss.__esp; sample.sp = state.__esp;
sample.fp = mcontext->__ss.__ebp; sample.fp = state.__ebp;
#else // !__DARWIN_UNIX03 #else // !__DARWIN_UNIX03
sample.pc = mcontext->ss.eip; sample.pc = state.eip;
sample.sp = mcontext->ss.esp; sample.sp = state.esp;
sample.fp = mcontext->ss.ebp; sample.fp = state.ebp;
#endif // __DARWIN_UNIX03 #endif // __DARWIN_UNIX03
#else #else
#error Unsupported Mac OS X host architecture. #error Unsupported Mac OS X host architecture.
#endif // V8_HOST_ARCH_IA32 #endif // V8_HOST_ARCH_IA32
sampler_->SampleStack(&sample);
}
thread_resume(profiled_thread_);
} }
// We always sample the VM state. // We always sample the VM state.
sample.state = Logger::state(); sample.state = Logger::state();
// Invoke tick handler with program counter and stack pointer.
sampler_->Tick(&sample);
active_sampler_->Tick(&sample); // Wait until next sampling.
} usleep(sampler_->interval_ * 1000);
}
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_;
}; };
// Entry point for sampler thread.
static void* SamplerEntry(void* arg) {
Sampler::PlatformData* data =
reinterpret_cast<Sampler::PlatformData*>(arg);
data->Runner();
return 0;
}
Sampler::Sampler(int interval, bool profiling) Sampler::Sampler(int interval, bool profiling)
: interval_(interval), profiling_(profiling), active_(false) { : interval_(interval), profiling_(profiling), active_(false) {
data_ = new PlatformData(); data_ = new PlatformData(this);
} }
...@@ -541,43 +573,40 @@ Sampler::~Sampler() { ...@@ -541,43 +573,40 @@ Sampler::~Sampler() {
void Sampler::Start() { void Sampler::Start() {
// There can only be one active sampler at the time on POSIX // If we are profiling, we need to be able to access the calling
// platforms. // thread.
if (active_sampler_ != NULL) return; if (IsProfiling()) {
data_->profiled_thread_ = mach_thread_self();
// Request profiling signals. }
struct sigaction sa;
sa.sa_sigaction = ProfilerSignalHandler; // Create sampler thread with high priority.
sigemptyset(&sa.sa_mask); // According to POSIX spec, when SCHED_FIFO policy is used, a thread
sa.sa_flags = SA_SIGINFO; // runs until it exits or blocks.
if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; pthread_attr_t sched_attr;
data_->signal_handler_installed_ = true; sched_param fifo_param;
pthread_attr_init(&sched_attr);
// Set the itimer to generate a tick for each interval. pthread_attr_setinheritsched(&sched_attr, PTHREAD_EXPLICIT_SCHED);
itimerval itimer; pthread_attr_setschedpolicy(&sched_attr, SCHED_FIFO);
itimer.it_interval.tv_sec = interval_ / 1000; fifo_param.sched_priority = sched_get_priority_max(SCHED_FIFO);
itimer.it_interval.tv_usec = (interval_ % 1000) * 1000; pthread_attr_setschedparam(&sched_attr, &fifo_param);
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;
active_ = true; active_ = true;
pthread_create(&data_->sampler_thread_, &sched_attr, SamplerEntry, data_);
} }
void Sampler::Stop() { void Sampler::Stop() {
// Restore old signal handler // Seting active to false triggers termination of the sampler
if (data_->signal_handler_installed_) { // thread.
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;
active_ = false; 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 #endif // ENABLE_LOGGING_AND_PROFILING
......
...@@ -1776,12 +1776,10 @@ class Sampler::PlatformData : public Malloced { ...@@ -1776,12 +1776,10 @@ class Sampler::PlatformData : public Malloced {
TickSample sample; TickSample sample;
// If profiling, we record the pc and sp of the profiled thread. // If profiling, we record the pc and sp of the profiled thread.
if (sampler_->IsProfiling()) { if (sampler_->IsProfiling()
// Pause the profiled thread and get its context. && SuspendThread(profiled_thread_) != (DWORD)-1) {
SuspendThread(profiled_thread_);
context.ContextFlags = CONTEXT_FULL; context.ContextFlags = CONTEXT_FULL;
GetThreadContext(profiled_thread_, &context); if (GetThreadContext(profiled_thread_, &context) != 0) {
// Invoke tick handler with program counter and stack pointer.
#if V8_HOST_ARCH_X64 #if V8_HOST_ARCH_X64
UNIMPLEMENTED(); UNIMPLEMENTED();
sample.pc = context.Rip; sample.pc = context.Rip;
...@@ -1792,16 +1790,16 @@ class Sampler::PlatformData : public Malloced { ...@@ -1792,16 +1790,16 @@ class Sampler::PlatformData : public Malloced {
sample.sp = context.Esp; sample.sp = context.Esp;
sample.fp = context.Ebp; sample.fp = context.Ebp;
#endif #endif
sampler_->SampleStack(&sample);
}
ResumeThread(profiled_thread_);
} }
// We always sample the VM state. // We always sample the VM state.
sample.state = Logger::state(); sample.state = Logger::state();
// Invoke tick handler with program counter and stack pointer.
sampler_->Tick(&sample); sampler_->Tick(&sample);
if (sampler_->IsProfiling()) {
ResumeThread(profiled_thread_);
}
// Wait until next sampling. // Wait until next sampling.
Sleep(sampler_->interval_); Sleep(sampler_->interval_);
} }
......
...@@ -510,6 +510,9 @@ class Sampler { ...@@ -510,6 +510,9 @@ class Sampler {
explicit Sampler(int interval, bool profiling); explicit Sampler(int interval, bool profiling);
virtual ~Sampler(); virtual ~Sampler();
// Performs stack sampling.
virtual void SampleStack(TickSample* sample) = 0;
// This method is called for each sampling period with the current // This method is called for each sampling period with the current
// program counter. // program counter.
virtual void Tick(TickSample* sample) = 0; virtual void Tick(TickSample* sample) = 0;
...@@ -527,8 +530,8 @@ class Sampler { ...@@ -527,8 +530,8 @@ class Sampler {
class PlatformData; class PlatformData;
private: private:
int interval_; const int interval_;
bool profiling_; const bool profiling_;
bool active_; bool active_;
PlatformData* data_; // Platform specific data. PlatformData* data_; // Platform specific data.
DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler); DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment