|
|
@ -88,6 +88,7 @@ double ceiling(double x) { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static Mutex* limit_mutex = NULL; |
|
|
|
void OS::Setup() { |
|
|
|
// Seed the random number generator.
|
|
|
|
// Convert the current time to a 64-bit integer first, before converting it
|
|
|
@ -96,6 +97,7 @@ void OS::Setup() { |
|
|
|
// call this setup code within the same millisecond.
|
|
|
|
uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); |
|
|
|
srandom(static_cast<unsigned int>(seed)); |
|
|
|
limit_mutex = CreateMutex(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -145,6 +147,9 @@ static void* highest_ever_allocated = reinterpret_cast<void*>(0); |
|
|
|
|
|
|
|
|
|
|
|
static void UpdateAllocatedSpaceLimits(void* address, int size) { |
|
|
|
ASSERT(limit_mutex != NULL); |
|
|
|
ScopedLock lock(limit_mutex); |
|
|
|
|
|
|
|
lowest_ever_allocated = Min(lowest_ever_allocated, address); |
|
|
|
highest_ever_allocated = |
|
|
|
Max(highest_ever_allocated, |
|
|
@ -407,7 +412,6 @@ static void* ThreadEntry(void* arg) { |
|
|
|
// one) so we initialize it here too.
|
|
|
|
thread->data()->thread_ = pthread_self(); |
|
|
|
ASSERT(thread->data()->thread_ != kNoThread); |
|
|
|
Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate()); |
|
|
|
thread->Run(); |
|
|
|
return NULL; |
|
|
|
} |
|
|
@ -587,78 +591,172 @@ Semaphore* OS::CreateSemaphore(int count) { |
|
|
|
|
|
|
|
#ifdef ENABLE_LOGGING_AND_PROFILING |
|
|
|
|
|
|
|
static Sampler* active_sampler_ = NULL; |
|
|
|
static pthread_t vm_tid_ = 0; |
|
|
|
|
|
|
|
|
|
|
|
static pthread_t GetThreadID() { |
|
|
|
return pthread_self(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { |
|
|
|
USE(info); |
|
|
|
if (signal != SIGPROF) return; |
|
|
|
if (active_sampler_ == NULL || !active_sampler_->IsActive()) return; |
|
|
|
if (vm_tid_ != GetThreadID()) return; |
|
|
|
Isolate* isolate = Isolate::UncheckedCurrent(); |
|
|
|
if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) { |
|
|
|
// We require a fully initialized and entered isolate.
|
|
|
|
return; |
|
|
|
} |
|
|
|
if (v8::Locker::IsActive() && |
|
|
|
!isolate->thread_manager()->IsLockedByCurrentThread()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
Sampler* sampler = isolate->logger()->sampler(); |
|
|
|
if (sampler == NULL || !sampler->IsActive()) return; |
|
|
|
|
|
|
|
TickSample sample_obj; |
|
|
|
TickSample* sample = CpuProfiler::TickSampleEvent(); |
|
|
|
TickSample* sample = CpuProfiler::TickSampleEvent(isolate); |
|
|
|
if (sample == NULL) sample = &sample_obj; |
|
|
|
|
|
|
|
// Extracting the sample from the context is extremely machine dependent.
|
|
|
|
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
|
|
|
mcontext_t& mcontext = ucontext->uc_mcontext; |
|
|
|
sample->state = Top::current_vm_state(); |
|
|
|
sample->state = isolate->current_vm_state(); |
|
|
|
|
|
|
|
sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]); |
|
|
|
sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]); |
|
|
|
sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]); |
|
|
|
|
|
|
|
active_sampler_->SampleStack(sample); |
|
|
|
active_sampler_->Tick(sample); |
|
|
|
sampler->SampleStack(sample); |
|
|
|
sampler->Tick(sample); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class Sampler::PlatformData : public Malloced { |
|
|
|
public: |
|
|
|
PlatformData() : vm_tid_(GetThreadID()) {} |
|
|
|
|
|
|
|
pthread_t vm_tid() const { return vm_tid_; } |
|
|
|
|
|
|
|
private: |
|
|
|
pthread_t vm_tid_; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
class SignalSender : public Thread { |
|
|
|
public: |
|
|
|
enum SleepInterval { |
|
|
|
FULL_INTERVAL, |
|
|
|
HALF_INTERVAL |
|
|
|
HALF_INTERVAL, |
|
|
|
FULL_INTERVAL |
|
|
|
}; |
|
|
|
|
|
|
|
explicit PlatformData(Sampler* sampler) |
|
|
|
: sampler_(sampler), |
|
|
|
signal_handler_installed_(false), |
|
|
|
vm_tgid_(getpid()), |
|
|
|
signal_sender_launched_(false) { |
|
|
|
explicit SignalSender(int interval) |
|
|
|
: Thread("SignalSender"), |
|
|
|
interval_(interval) {} |
|
|
|
|
|
|
|
static void InstallSignalHandler() { |
|
|
|
struct sigaction sa; |
|
|
|
sa.sa_sigaction = ProfilerSignalHandler; |
|
|
|
sigemptyset(&sa.sa_mask); |
|
|
|
sa.sa_flags = SA_RESTART | SA_SIGINFO; |
|
|
|
signal_handler_installed_ = |
|
|
|
(sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); |
|
|
|
} |
|
|
|
|
|
|
|
static void RestoreSignalHandler() { |
|
|
|
if (signal_handler_installed_) { |
|
|
|
sigaction(SIGPROF, &old_signal_handler_, 0); |
|
|
|
signal_handler_installed_ = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void AddActiveSampler(Sampler* sampler) { |
|
|
|
ScopedLock lock(mutex_); |
|
|
|
SamplerRegistry::AddActiveSampler(sampler); |
|
|
|
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_->Start(); |
|
|
|
} else { |
|
|
|
ASSERT(instance_->interval_ == sampler->interval()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void RemoveActiveSampler(Sampler* sampler) { |
|
|
|
ScopedLock lock(mutex_); |
|
|
|
SamplerRegistry::RemoveActiveSampler(sampler); |
|
|
|
if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) { |
|
|
|
RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown(); |
|
|
|
instance_->Join(); |
|
|
|
delete instance_; |
|
|
|
instance_ = NULL; |
|
|
|
RestoreSignalHandler(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void SignalSender() { |
|
|
|
while (sampler_->IsActive()) { |
|
|
|
if (rate_limiter_.SuspendIfNecessary()) continue; |
|
|
|
if (sampler_->IsProfiling() && RuntimeProfiler::IsEnabled()) { |
|
|
|
SendProfilingSignal(); |
|
|
|
// Implement Thread::Run().
|
|
|
|
virtual void Run() { |
|
|
|
SamplerRegistry::State state; |
|
|
|
while ((state = SamplerRegistry::GetState()) != |
|
|
|
SamplerRegistry::HAS_NO_SAMPLERS) { |
|
|
|
bool cpu_profiling_enabled = |
|
|
|
(state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS); |
|
|
|
bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled(); |
|
|
|
if (cpu_profiling_enabled && !signal_handler_installed_) { |
|
|
|
InstallSignalHandler(); |
|
|
|
} else if (!cpu_profiling_enabled && signal_handler_installed_) { |
|
|
|
RestoreSignalHandler(); |
|
|
|
} |
|
|
|
|
|
|
|
// When CPU profiling is enabled both JavaScript and C++ code is
|
|
|
|
// profiled. We must not suspend.
|
|
|
|
if (!cpu_profiling_enabled) { |
|
|
|
if (rate_limiter_.SuspendIfNecessary()) continue; |
|
|
|
} |
|
|
|
if (cpu_profiling_enabled && runtime_profiler_enabled) { |
|
|
|
if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
Sleep(HALF_INTERVAL); |
|
|
|
RuntimeProfiler::NotifyTick(); |
|
|
|
if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
Sleep(HALF_INTERVAL); |
|
|
|
} else { |
|
|
|
if (sampler_->IsProfiling()) SendProfilingSignal(); |
|
|
|
if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick(); |
|
|
|
if (cpu_profiling_enabled) { |
|
|
|
if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, |
|
|
|
this)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
if (runtime_profiler_enabled) { |
|
|
|
if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, |
|
|
|
NULL)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
Sleep(FULL_INTERVAL); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void SendProfilingSignal() { |
|
|
|
static void DoCpuProfile(Sampler* sampler, void* raw_sender) { |
|
|
|
if (!sampler->IsProfiling()) return; |
|
|
|
SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender); |
|
|
|
sender->SendProfilingSignal(sampler->platform_data()->vm_tid()); |
|
|
|
} |
|
|
|
|
|
|
|
static void DoRuntimeProfile(Sampler* sampler, void* ignored) { |
|
|
|
if (!sampler->isolate()->IsInitialized()) return; |
|
|
|
sampler->isolate()->runtime_profiler()->NotifyTick(); |
|
|
|
} |
|
|
|
|
|
|
|
void SendProfilingSignal(pthread_t tid) { |
|
|
|
if (!signal_handler_installed_) return; |
|
|
|
pthread_kill(vm_tid_, SIGPROF); |
|
|
|
pthread_kill(tid, SIGPROF); |
|
|
|
} |
|
|
|
|
|
|
|
void Sleep(SleepInterval full_or_half) { |
|
|
|
// Convert ms to us and subtract 100 us to compensate delays
|
|
|
|
// occuring during signal delivery.
|
|
|
|
useconds_t interval = sampler_->interval_ * 1000 - 100; |
|
|
|
useconds_t interval = interval_ * 1000 - 100; |
|
|
|
if (full_or_half == HALF_INTERVAL) interval /= 2; |
|
|
|
int result = usleep(interval); |
|
|
|
#ifdef DEBUG |
|
|
@ -673,22 +771,22 @@ class Sampler::PlatformData : public Malloced { |
|
|
|
USE(result); |
|
|
|
} |
|
|
|
|
|
|
|
Sampler* sampler_; |
|
|
|
bool signal_handler_installed_; |
|
|
|
struct sigaction old_signal_handler_; |
|
|
|
int vm_tgid_; |
|
|
|
bool signal_sender_launched_; |
|
|
|
pthread_t signal_sender_thread_; |
|
|
|
const int interval_; |
|
|
|
RuntimeProfilerRateLimiter rate_limiter_; |
|
|
|
}; |
|
|
|
|
|
|
|
// Protects the process wide state below.
|
|
|
|
static Mutex* mutex_; |
|
|
|
static SignalSender* instance_; |
|
|
|
static bool signal_handler_installed_; |
|
|
|
static struct sigaction old_signal_handler_; |
|
|
|
|
|
|
|
static void* SenderEntry(void* arg) { |
|
|
|
Sampler::PlatformData* data = |
|
|
|
reinterpret_cast<Sampler::PlatformData*>(arg); |
|
|
|
data->SignalSender(); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
DISALLOW_COPY_AND_ASSIGN(SignalSender); |
|
|
|
}; |
|
|
|
|
|
|
|
Mutex* SignalSender::mutex_ = OS::CreateMutex(); |
|
|
|
SignalSender* SignalSender::instance_ = NULL; |
|
|
|
struct sigaction SignalSender::old_signal_handler_; |
|
|
|
bool SignalSender::signal_handler_installed_ = false; |
|
|
|
|
|
|
|
|
|
|
|
Sampler::Sampler(Isolate* isolate, int interval) |
|
|
@ -697,63 +795,27 @@ Sampler::Sampler(Isolate* isolate, int interval) |
|
|
|
profiling_(false), |
|
|
|
active_(false), |
|
|
|
samples_taken_(0) { |
|
|
|
data_ = new PlatformData(this); |
|
|
|
data_ = new PlatformData; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Sampler::~Sampler() { |
|
|
|
ASSERT(!data_->signal_sender_launched_); |
|
|
|
ASSERT(!IsActive()); |
|
|
|
delete data_; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Sampler::Start() { |
|
|
|
// There can only be one active sampler at the time on POSIX
|
|
|
|
// platforms.
|
|
|
|
ASSERT(!IsActive()); |
|
|
|
vm_tid_ = GetThreadID(); |
|
|
|
|
|
|
|
// Request profiling signals.
|
|
|
|
struct sigaction sa; |
|
|
|
sa.sa_sigaction = ProfilerSignalHandler; |
|
|
|
sigemptyset(&sa.sa_mask); |
|
|
|
sa.sa_flags = SA_RESTART | SA_SIGINFO; |
|
|
|
data_->signal_handler_installed_ = |
|
|
|
sigaction(SIGPROF, &sa, &data_->old_signal_handler_) == 0; |
|
|
|
|
|
|
|
// Start a thread that sends SIGPROF signal to VM thread.
|
|
|
|
// Sending the signal ourselves instead of relying on itimer provides
|
|
|
|
// much better accuracy.
|
|
|
|
SetActive(true); |
|
|
|
if (pthread_create( |
|
|
|
&data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) { |
|
|
|
data_->signal_sender_launched_ = true; |
|
|
|
} |
|
|
|
|
|
|
|
// Set this sampler as the active sampler.
|
|
|
|
active_sampler_ = this; |
|
|
|
SignalSender::AddActiveSampler(this); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Sampler::Stop() { |
|
|
|
ASSERT(IsActive()); |
|
|
|
SignalSender::RemoveActiveSampler(this); |
|
|
|
SetActive(false); |
|
|
|
|
|
|
|
// Wait for signal sender termination (it will exit after setting
|
|
|
|
// active_ to false).
|
|
|
|
if (data_->signal_sender_launched_) { |
|
|
|
Top::WakeUpRuntimeProfilerThreadBeforeShutdown(); |
|
|
|
pthread_join(data_->signal_sender_thread_, NULL); |
|
|
|
data_->signal_sender_launched_ = false; |
|
|
|
} |
|
|
|
|
|
|
|
// Restore old signal handler
|
|
|
|
if (data_->signal_handler_installed_) { |
|
|
|
sigaction(SIGPROF, &data_->old_signal_handler_, 0); |
|
|
|
data_->signal_handler_installed_ = false; |
|
|
|
} |
|
|
|
|
|
|
|
// This sampler is no longer the active sampler.
|
|
|
|
active_sampler_ = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
#endif // ENABLE_LOGGING_AND_PROFILING
|
|
|
|