| 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;
|
|
|