Index: src/sampler.cc |
diff --git a/src/sampler.cc b/src/sampler.cc |
index 446f9963b48927a9c01df96b559500f8919f0c05..55b943c42da74f8f82ef4f0c7264db46f3c99b16 100644 |
--- a/src/sampler.cc |
+++ b/src/sampler.cc |
@@ -26,17 +26,19 @@ |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ |
- || defined(__NetBSD__) || defined(__sun) |
+ || defined(__NetBSD__) || defined(__sun) || defined(__ANDROID__) |
+ |
+#define USE_SIGNALS |
#include <errno.h> |
#include <pthread.h> |
#include <signal.h> |
#include <sys/time.h> |
#include <sys/syscall.h> |
+#if !defined(__ANDROID__) || defined(__BIONIC_HAVE_UCONTEXT_T) |
#include <ucontext.h> |
-#include <unistd.h> |
- |
#endif |
+#include <unistd.h> |
// GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'. |
// Old versions of the C library <signal.h> didn't define the type. |
@@ -45,6 +47,16 @@ |
#include <asm/sigcontext.h> |
#endif |
+#elif defined(__MACH__) |
+ |
+#include <mach/mach.h> |
+ |
+#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) |
+ |
+#include "win32-headers.h" |
+ |
+#endif |
+ |
#include "v8.h" |
#include "log.h" |
@@ -58,7 +70,7 @@ namespace internal { |
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ |
- || defined(__NetBSD__) || defined(__sun) |
+ || defined(__NetBSD__) || defined(__sun) || defined(__ANDROID__) |
#if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T) |
@@ -153,13 +165,16 @@ class Sampler::PlatformData : public Malloced { |
public: |
PlatformData() |
: vm_tid_(GetThreadID()), |
+ vm_tgid_(getpid()), |
profiled_thread_id_(ThreadId::Current()) {} |
pthread_t vm_tid() const { return vm_tid_; } |
+ int vm_tgid() const { return vm_tgid_; } |
ThreadId profiled_thread_id() { return profiled_thread_id_; } |
private: |
pthread_t vm_tid_; |
+ const int vm_tgid_; |
ThreadId profiled_thread_id_; |
}; |
@@ -216,7 +231,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { |
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
mcontext_t& mcontext = ucontext->uc_mcontext; |
sample->state = isolate->current_vm_state(); |
-#if defined(__linux__) |
+#if defined(__linux__) || defined(__ANDROID__) |
#if V8_HOST_ARCH_IA32 |
sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]); |
sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]); |
@@ -291,19 +306,80 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { |
#endif // __native_client__ |
} |
+#elif defined(__MACH__) |
+class Sampler::PlatformData : public Malloced { |
+ public: |
+ PlatformData() |
+ : profiled_thread_(mach_thread_self()), |
+ profiled_thread_id_(ThreadId::Current()) {} |
+ |
+ ~PlatformData() { |
+ // Deallocate Mach port for thread. |
+ mach_port_deallocate(mach_task_self(), profiled_thread_); |
+ } |
-class SignalSender : public Thread { |
+ thread_act_t profiled_thread() { return profiled_thread_; } |
+ ThreadId profiled_thread_id() { return profiled_thread_id_; } |
+ |
+ private: |
+ // 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. |
+ thread_act_t profiled_thread_; |
+ ThreadId profiled_thread_id_; |
+}; |
+ |
+#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) |
+ |
+// ---------------------------------------------------------------------------- |
+// Win32 profiler support. On Cygwin we use the same sampler implementation as |
+// on Win32. |
+ |
+class Sampler::PlatformData : public Malloced { |
public: |
- static const int kSignalSenderStackSize = 64 * KB; |
+ // Get a handle to the calling thread. This is the thread that we are |
+ // going to profile. We need to make a copy of the handle because we are |
+ // going to use it in the sampler thread. Using GetThreadHandle() will |
+ // not work in this case. We're using OpenThread because DuplicateHandle |
+ // for some reason doesn't work in Chrome's sandbox. |
+ PlatformData() |
+ : profiled_thread_(OpenThread(THREAD_GET_CONTEXT | |
+ THREAD_SUSPEND_RESUME | |
+ THREAD_QUERY_INFORMATION, |
+ false, |
+ GetCurrentThreadId())), |
+ profiled_thread_id_(ThreadId::Current()) {} |
- explicit SignalSender(int interval) |
- : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)), |
- vm_tgid_(getpid()), |
+ ~PlatformData() { |
+ if (profiled_thread_ != NULL) { |
+ CloseHandle(profiled_thread_); |
+ profiled_thread_ = NULL; |
+ } |
+ } |
+ |
+ HANDLE profiled_thread() { return profiled_thread_; } |
+ ThreadId profiled_thread_id() { return profiled_thread_id_; } |
+ |
+ private: |
+ HANDLE profiled_thread_; |
+ ThreadId profiled_thread_id_; |
+}; |
+ |
+#endif |
+ |
+ |
+class SamplerThread : public Thread { |
+ public: |
+ static const int kSamplerThreadStackSize = 64 * KB; |
+ |
+ explicit SamplerThread(int interval) |
+ : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)), |
interval_(interval) {} |
static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); } |
static void TearDown() { delete mutex_; } |
+#if defined(USE_SIGNALS) |
static void InstallSignalHandler() { |
struct sigaction sa; |
sa.sa_sigaction = ProfilerSignalHandler; |
@@ -319,6 +395,7 @@ class SignalSender : public Thread { |
signal_handler_installed_ = false; |
} |
} |
+#endif |
static void AddActiveSampler(Sampler* sampler) { |
ScopedLock lock(mutex_); |
@@ -326,7 +403,7 @@ class SignalSender : public Thread { |
if (instance_ == NULL) { |
// Start a thread that will send SIGPROF signal to VM threads, |
// when CPU profiling will be enabled. |
- instance_ = new SignalSender(sampler->interval()); |
+ instance_ = new SamplerThread(sampler->interval()); |
instance_->StartSynchronously(); |
} else { |
ASSERT(instance_->interval_ == sampler->interval()); |
@@ -340,7 +417,9 @@ class SignalSender : public Thread { |
instance_->Join(); |
delete instance_; |
instance_ = NULL; |
+#if defined(USE_SIGNALS) |
RestoreSignalHandler(); |
+#endif |
} |
} |
@@ -352,31 +431,39 @@ class SignalSender : public Thread { |
// When CPU profiling is enabled both JavaScript and C++ code is |
// profiled. We must not suspend. |
if (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS) { |
+#if defined(USE_SIGNALS) |
if (!signal_handler_installed_) InstallSignalHandler(); |
+#endif |
SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this); |
} else { |
+#if defined(USE_SIGNALS) |
if (signal_handler_installed_) RestoreSignalHandler(); |
+#endif |
} |
Sleep(); // TODO(svenpanne) Figure out if OS:Sleep(interval_) is enough. |
} |
} |
static void DoCpuProfile(Sampler* sampler, void* raw_sender) { |
+ if (!sampler->isolate()->IsInitialized()) return; |
if (!sampler->IsProfiling()) return; |
- SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender); |
- sender->SendProfilingSignal(sampler->platform_data()->vm_tid()); |
+ SamplerThread* sender = reinterpret_cast<SamplerThread*>(raw_sender); |
+ sender->SampleContext(sampler); |
} |
- void SendProfilingSignal(int tid) { |
+#if defined(USE_SIGNALS) |
+ void SampleContext(Sampler* sampler) { |
if (!signal_handler_installed_) return; |
+ Sampler::PlatformData* platform_data = sampler->platform_data(); |
+ int tid = platform_data->vm_tid(); |
// Glibc doesn't provide a wrapper for tgkill(2). |
#if defined(ANDROID) |
- syscall(__NR_tgkill, vm_tgid_, tid, SIGPROF); |
+ syscall(__NR_tgkill, platform_data->vm_tgid(), tid, SIGPROF); |
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \ |
|| defined(__sun) |
pthread_kill(tid, SIGPROF); |
#else |
- int result = syscall(SYS_tgkill, vm_tgid_, tid, SIGPROF); |
+ int result = syscall(SYS_tgkill, platform_data->vm_tgid(), tid, SIGPROF); |
USE(result); |
ASSERT(result == 0); |
#endif |
@@ -393,7 +480,7 @@ class SignalSender : public Thread { |
#ifdef DEBUG |
if (result != 0 && errno != EINTR) { |
fprintf(stderr, |
- "SignalSender usleep error; interval = %u, errno = %d\n", |
+ "SamplerThread usleep error; interval = %u, errno = %d\n", |
interval, |
errno); |
ASSERT(result == 0 || errno == EINTR); |
@@ -403,33 +490,176 @@ class SignalSender : public Thread { |
#endif // ANDROID |
} |
- const int vm_tgid_; |
+#elif defined(__MACH__) |
+ |
+ void SampleContext(Sampler* sampler) { |
+ thread_act_t profiled_thread = sampler->platform_data()->profiled_thread(); |
+ Isolate* isolate = sampler->isolate(); |
+#if defined(USE_SIMULATOR) |
+#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS |
+ ThreadId thread_id = sampler->platform_data()->profiled_thread_id(); |
+ Isolate::PerIsolateThreadData* per_thread_data = isolate-> |
+ FindPerThreadDataForThread(thread_id); |
+ if (!per_thread_data) return; |
+ Simulator* sim = per_thread_data->simulator(); |
+ // Check if there is active simulator before allocating TickSample. |
+ if (!sim) return; |
+#endif |
+#endif // USE_SIMULATOR |
+ TickSample sample_obj; |
+ TickSample* sample = isolate->cpu_profiler()->TickSampleEvent(); |
+ if (sample == NULL) sample = &sample_obj; |
+ |
+ if (KERN_SUCCESS != thread_suspend(profiled_thread)) return; |
+ |
+#if V8_HOST_ARCH_X64 |
+ thread_state_flavor_t flavor = x86_THREAD_STATE64; |
+ x86_thread_state64_t state; |
+ mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; |
+#if __DARWIN_UNIX03 |
+#define REGISTER_FIELD(name) __r ## name |
+#else |
+#define REGISTER_FIELD(name) r ## name |
+#endif // __DARWIN_UNIX03 |
+#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; |
+#if __DARWIN_UNIX03 |
+#define REGISTER_FIELD(name) __e ## name |
+#else |
+#define REGISTER_FIELD(name) e ## name |
+#endif // __DARWIN_UNIX03 |
+#else |
+#error Unsupported Mac OS X host architecture. |
+#endif // V8_HOST_ARCH |
+ |
+ if (thread_get_state(profiled_thread, |
+ flavor, |
+ reinterpret_cast<natural_t*>(&state), |
+ &count) == KERN_SUCCESS) { |
+ sample->state = isolate->current_vm_state(); |
+#if defined(USE_SIMULATOR) |
+#if V8_TARGET_ARCH_ARM |
+ sample->pc = reinterpret_cast<Address>(sim->get_register(Simulator::pc)); |
+ sample->sp = reinterpret_cast<Address>(sim->get_register(Simulator::sp)); |
+ sample->fp = reinterpret_cast<Address>(sim->get_register(Simulator::r11)); |
+#elif V8_TARGET_ARCH_MIPS |
+ sample->pc = reinterpret_cast<Address>(sim->get_register(Simulator::pc)); |
+ sample->sp = reinterpret_cast<Address>(sim->get_register(Simulator::sp)); |
+ sample->fp = reinterpret_cast<Address>(sim->get_register(Simulator::fp)); |
+#endif |
+#else |
+ sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip)); |
+ sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp)); |
+ sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp)); |
+#endif // USE_SIMULATOR |
+#undef REGISTER_FIELD |
+ sampler->SampleStack(sample); |
+ sampler->Tick(sample); |
+ } |
+ thread_resume(profiled_thread); |
+ } |
+ |
+ void Sleep() { |
+ OS::Sleep(interval_); |
+ } |
+ |
+#elif defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) |
+ |
+ void SampleContext(Sampler* sampler) { |
+ HANDLE profiled_thread = sampler->platform_data()->profiled_thread(); |
+ if (profiled_thread == NULL) return; |
+ |
+ // Context used for sampling the register state of the profiled thread. |
+ CONTEXT context; |
+ memset(&context, 0, sizeof(context)); |
+ |
+ Isolate* isolate = sampler->isolate(); |
+#if defined(USE_SIMULATOR) |
+#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_MIPS |
+ ThreadId thread_id = sampler->platform_data()->profiled_thread_id(); |
+ Isolate::PerIsolateThreadData* per_thread_data = isolate-> |
+ FindPerThreadDataForThread(thread_id); |
+ if (!per_thread_data) return; |
+ Simulator* sim = per_thread_data->simulator(); |
+ // Check if there is active simulator before allocating TickSample. |
+ if (!sim) return; |
+#endif |
+#endif // USE_SIMULATOR |
+ TickSample sample_obj; |
+ TickSample* sample = isolate->cpu_profiler()->TickSampleEvent(); |
+ if (sample == NULL) sample = &sample_obj; |
+ |
+ static const DWORD kSuspendFailed = static_cast<DWORD>(-1); |
+ if (SuspendThread(profiled_thread) == kSuspendFailed) return; |
+ sample->state = isolate->current_vm_state(); |
+ |
+ context.ContextFlags = CONTEXT_FULL; |
+ if (GetThreadContext(profiled_thread, &context) != 0) { |
+#if defined(USE_SIMULATOR) |
+#if V8_TARGET_ARCH_ARM |
+ sample->pc = reinterpret_cast<Address>(sim->get_register(Simulator::pc)); |
+ sample->sp = reinterpret_cast<Address>(sim->get_register(Simulator::sp)); |
+ sample->fp = reinterpret_cast<Address>(sim->get_register(Simulator::r11)); |
+#elif V8_TARGET_ARCH_MIPS |
+ sample->pc = reinterpret_cast<Address>(sim->get_register(Simulator::pc)); |
+ sample->sp = reinterpret_cast<Address>(sim->get_register(Simulator::sp)); |
+ sample->fp = reinterpret_cast<Address>(sim->get_register(Simulator::fp)); |
+#endif |
+#else |
+#if V8_HOST_ARCH_X64 |
+ sample->pc = reinterpret_cast<Address>(context.Rip); |
+ sample->sp = reinterpret_cast<Address>(context.Rsp); |
+ sample->fp = reinterpret_cast<Address>(context.Rbp); |
+#else |
+ sample->pc = reinterpret_cast<Address>(context.Eip); |
+ sample->sp = reinterpret_cast<Address>(context.Esp); |
+ sample->fp = reinterpret_cast<Address>(context.Ebp); |
+#endif |
+#endif // USE_SIMULATOR |
+ sampler->SampleStack(sample); |
+ sampler->Tick(sample); |
+ } |
+ ResumeThread(profiled_thread); |
+ } |
+ |
+ void Sleep() { |
+ OS::Sleep(interval_); |
+ } |
+ |
+#endif // USE_SIGNALS |
+ |
const int interval_; |
// Protects the process wide state below. |
static Mutex* mutex_; |
- static SignalSender* instance_; |
+ static SamplerThread* instance_; |
+#if defined(USE_SIGNALS) |
static bool signal_handler_installed_; |
static struct sigaction old_signal_handler_; |
+#endif |
private: |
- DISALLOW_COPY_AND_ASSIGN(SignalSender); |
+ DISALLOW_COPY_AND_ASSIGN(SamplerThread); |
}; |
-Mutex* SignalSender::mutex_ = NULL; |
-SignalSender* SignalSender::instance_ = NULL; |
-struct sigaction SignalSender::old_signal_handler_; |
-bool SignalSender::signal_handler_installed_ = false; |
+Mutex* SamplerThread::mutex_ = NULL; |
+SamplerThread* SamplerThread::instance_ = NULL; |
+#if defined(USE_SIGNALS) |
+struct sigaction SamplerThread::old_signal_handler_; |
+bool SamplerThread::signal_handler_installed_ = false; |
+#endif |
void Sampler::SetUp() { |
- SignalSender::SetUp(); |
+ SamplerThread::SetUp(); |
} |
void Sampler::TearDown() { |
- SignalSender::TearDown(); |
+ SamplerThread::TearDown(); |
} |
@@ -451,27 +681,16 @@ Sampler::~Sampler() { |
void Sampler::Start() { |
ASSERT(!IsActive()); |
SetActive(true); |
- SignalSender::AddActiveSampler(this); |
+ SamplerThread::AddActiveSampler(this); |
} |
void Sampler::Stop() { |
ASSERT(IsActive()); |
- SignalSender::RemoveActiveSampler(this); |
+ SamplerThread::RemoveActiveSampler(this); |
SetActive(false); |
} |
-#else |
- |
-void Sampler::SetUp() { |
-} |
- |
- |
-void Sampler::TearDown() { |
-} |
- |
-#endif // __linux__ || _*BSD__ || __sun |
- |
void Sampler::SampleStack(TickSample* sample) { |
StackTracer::Trace(isolate_, sample); |
if (++samples_taken_ < 0) samples_taken_ = 0; |