Author: [email protected]
Date: Wed Jul 1 01:46:59 2009
New Revision: 2315
Modified:
branches/bleeding_edge/src/log.cc
branches/bleeding_edge/src/platform-freebsd.cc
branches/bleeding_edge/src/platform-linux.cc
branches/bleeding_edge/src/platform-macos.cc
branches/bleeding_edge/src/platform-win32.cc
branches/bleeding_edge/src/platform.h
Log:
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
Modified: branches/bleeding_edge/src/log.cc
==============================================================================
--- branches/bleeding_edge/src/log.cc (original)
+++ branches/bleeding_edge/src/log.cc Wed Jul 1 01:46:59 2009
@@ -176,8 +176,11 @@
~Ticker() { if (IsActive()) Stop(); }
+ void SampleStack(TickSample* sample) {
+ StackTracer::Trace(sample);
+ }
+
void Tick(TickSample* sample) {
- if (IsProfiling()) StackTracer::Trace(sample);
if (profiler_) profiler_->Insert(sample);
if (window_) window_->AddState(sample->state);
}
Modified: branches/bleeding_edge/src/platform-freebsd.cc
==============================================================================
--- branches/bleeding_edge/src/platform-freebsd.cc (original)
+++ branches/bleeding_edge/src/platform-freebsd.cc Wed Jul 1 01:46:59 2009
@@ -561,6 +561,7 @@
sample.sp = mcontext.mc_esp;
sample.fp = mcontext.mc_ebp;
#endif
+ active_sampler_->SampleStack(&sample);
}
// We always sample the VM state.
Modified: branches/bleeding_edge/src/platform-linux.cc
==============================================================================
--- branches/bleeding_edge/src/platform-linux.cc (original)
+++ branches/bleeding_edge/src/platform-linux.cc Wed Jul 1 01:46:59 2009
@@ -639,6 +639,7 @@
sample.fp = mcontext.arm_fp;
#endif
#endif
+ active_sampler_->SampleStack(&sample);
}
// We always sample the VM state.
Modified: branches/bleeding_edge/src/platform-macos.cc
==============================================================================
--- branches/bleeding_edge/src/platform-macos.cc (original)
+++ branches/bleeding_edge/src/platform-macos.cc Wed Jul 1 01:46:59 2009
@@ -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 @@
#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;
-
- // 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;
+class Sampler::PlatformData : public Malloced {
+ public:
+ explicit PlatformData(Sampler* sampler)
+ : sampler_(sampler),
+ task_self_(mach_task_self()),
+ 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;
+
+ // 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 @@
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_);
+ // 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);
- // Set this sampler as the active sampler.
- active_sampler_ = this;
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
Modified: branches/bleeding_edge/src/platform-win32.cc
==============================================================================
--- branches/bleeding_edge/src/platform-win32.cc (original)
+++ branches/bleeding_edge/src/platform-win32.cc Wed Jul 1 01:46:59 2009
@@ -1776,31 +1776,29 @@
TickSample sample;
// If profiling, we record the pc and sp of the profiled thread.
- if (sampler_->IsProfiling()) {
- // Pause the profiled thread and get its context.
- SuspendThread(profiled_thread_);
+ if (sampler_->IsProfiling()
+ && SuspendThread(profiled_thread_) != (DWORD)-1) {
context.ContextFlags = CONTEXT_FULL;
- GetThreadContext(profiled_thread_, &context);
- // Invoke tick handler with program counter and stack pointer.
+ if (GetThreadContext(profiled_thread_, &context) != 0) {
#if V8_HOST_ARCH_X64
- UNIMPLEMENTED();
- sample.pc = context.Rip;
- sample.sp = context.Rsp;
- sample.fp = context.Rbp;
+ UNIMPLEMENTED();
+ sample.pc = context.Rip;
+ sample.sp = context.Rsp;
+ sample.fp = context.Rbp;
#else
- sample.pc = context.Eip;
- sample.sp = context.Esp;
- sample.fp = context.Ebp;
+ sample.pc = context.Eip;
+ sample.sp = context.Esp;
+ sample.fp = context.Ebp;
#endif
+ sampler_->SampleStack(&sample);
+ }
+ ResumeThread(profiled_thread_);
}
// We always sample the VM state.
sample.state = Logger::state();
+ // Invoke tick handler with program counter and stack pointer.
sampler_->Tick(&sample);
-
- if (sampler_->IsProfiling()) {
- ResumeThread(profiled_thread_);
- }
// Wait until next sampling.
Sleep(sampler_->interval_);
Modified: branches/bleeding_edge/src/platform.h
==============================================================================
--- branches/bleeding_edge/src/platform.h (original)
+++ branches/bleeding_edge/src/platform.h Wed Jul 1 01:46:59 2009
@@ -510,6 +510,9 @@
explicit Sampler(int interval, bool profiling);
virtual ~Sampler();
+ // Performs stack sampling.
+ virtual void SampleStack(TickSample* sample) = 0;
+
// This method is called for each sampling period with the current
// program counter.
virtual void Tick(TickSample* sample) = 0;
@@ -527,8 +530,8 @@
class PlatformData;
private:
- int interval_;
- bool profiling_;
+ const int interval_;
+ const bool profiling_;
bool active_;
PlatformData* data_; // Platform specific data.
DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler);
--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---