vendor/v8/src/platform-linux.cc in mustang-0.0.1 vs vendor/v8/src/platform-linux.cc in mustang-0.1.0

- old
+ new

@@ -56,11 +56,10 @@ #undef MAP_TYPE #include "v8.h" #include "platform.h" -#include "top.h" #include "v8threads.h" #include "vm-state-inl.h" namespace v8 { @@ -74,28 +73,36 @@ double ceiling(double x) { return ceil(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 // to an unsigned. Going directly can cause an overflow and the seed to be // set to all ones. The seed will be identical for different instances that // 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(); } uint64_t OS::CpuFeaturesImpliedByPlatform() { #if (defined(__VFP_FP__) && !defined(__SOFTFP__)) // Here gcc is telling us that we are on an ARM and gcc is assuming that we // have VFP3 instructions. If gcc can assume it then so can we. return 1u << VFP3; #elif CAN_USE_ARMV7_INSTRUCTIONS return 1u << ARMv7; +#elif(defined(__mips_hard_float) && __mips_hard_float != 0) + // Here gcc is telling us that we are on an MIPS and gcc is assuming that we + // have FPU instructions. If gcc can assume it then so can we. + return 1u << FPU; #else return 0; // Linux runs on anything. #endif } @@ -170,10 +177,62 @@ return false; } #endif // def __arm__ +#ifdef __mips__ +bool OS::MipsCpuHasFeature(CpuFeature feature) { + const char* search_string = NULL; + const char* file_name = "/proc/cpuinfo"; + // Simple detection of FPU at runtime for Linux. + // It is based on /proc/cpuinfo, which reveals hardware configuration + // to user-space applications. According to MIPS (early 2010), no similar + // facility is universally available on the MIPS architectures, + // so it's up to individual OSes to provide such. + // + // This is written as a straight shot one pass parser + // and not using STL string and ifstream because, + // on Linux, it's reading from a (non-mmap-able) + // character special device. + + switch (feature) { + case FPU: + search_string = "FPU"; + break; + default: + UNREACHABLE(); + } + + FILE* f = NULL; + const char* what = search_string; + + if (NULL == (f = fopen(file_name, "r"))) + return false; + + int k; + while (EOF != (k = fgetc(f))) { + if (k == *what) { + ++what; + while ((*what != '\0') && (*what == fgetc(f))) { + ++what; + } + if (*what == '\0') { + fclose(f); + return true; + } else { + what = search_string; + } + } + } + fclose(f); + + // Did not find string in the proc file. + return false; +} +#endif // def __mips__ + + int OS::ActivationFrameAlignment() { #ifdef V8_TARGET_ARCH_ARM // On EABI ARM targets this is required for fp correctness in the // runtime system. return 8; @@ -185,12 +244,13 @@ return 16; } void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { -#if defined(V8_TARGET_ARCH_ARM) && defined(__arm__) - // Only use on ARM hardware. +#if (defined(V8_TARGET_ARCH_ARM) && defined(__arm__)) || \ + (defined(V8_TARGET_ARCH_MIPS) && defined(__mips__)) + // Only use on ARM or MIPS hardware. MemoryBarrier(); #else __asm__ __volatile__("" : : : "memory"); // An x86 store acts as a release barrier. #endif @@ -224,10 +284,13 @@ static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); 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, reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); } @@ -249,11 +312,12 @@ // TODO(805): Port randomization of allocated executable memory to Linux. const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mbase == MAP_FAILED) { - LOG(StringEvent("OS::Allocate", "mmap failed")); + LOG(i::Isolate::Current(), + StringEvent("OS::Allocate", "mmap failed")); return NULL; } *allocated = msize; UpdateAllocatedSpaceLimits(mbase, msize); return mbase; @@ -370,10 +434,11 @@ // Allocate enough room to be able to store a full file name. const int kLibNameLen = FILENAME_MAX + 1; char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); + i::Isolate* isolate = ISOLATE; // This loop will terminate once the scanning hits an EOF. while (true) { uintptr_t start, end; char attr_r, attr_w, attr_x, attr_p; // Parse the addresses and permission bits at the beginning of the line. @@ -403,11 +468,11 @@ } else { // No library name found, just record the raw address range. snprintf(lib_name, kLibNameLen, "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); } - LOG(SharedLibraryEvent(lib_name, start, end)); + LOG(isolate, SharedLibraryEvent(lib_name, start, end)); } else { // Entry not describing executable data. Skip to end of line to setup // reading the next entry. do { c = getc(fp); @@ -563,16 +628,22 @@ bool ThreadHandle::IsValid() const { return data_->thread_ != kNoThread; } -Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) { - set_name("v8:<unknown>"); +Thread::Thread(Isolate* isolate, const Options& options) + : ThreadHandle(ThreadHandle::INVALID), + isolate_(isolate), + stack_size_(options.stack_size) { + set_name(options.name); } -Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) { +Thread::Thread(Isolate* isolate, const char* name) + : ThreadHandle(ThreadHandle::INVALID), + isolate_(isolate), + stack_size_(0) { set_name(name); } Thread::~Thread() { @@ -582,13 +653,16 @@ static void* ThreadEntry(void* arg) { Thread* thread = reinterpret_cast<Thread*>(arg); // This is also initialized by the first argument to pthread_create() but we // don't know which thread will run first (the original thread or the new // one) so we initialize it here too. - prctl(PR_SET_NAME, thread->name(), 0, 0, 0); + prctl(PR_SET_NAME, + reinterpret_cast<unsigned long>(thread->name()), // NOLINT + 0, 0, 0); thread->thread_handle_data()->thread_ = pthread_self(); ASSERT(thread->IsValid()); + Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate()); thread->Run(); return NULL; } @@ -597,11 +671,18 @@ name_[sizeof(name_) - 1] = '\0'; } void Thread::Start() { - pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this); + pthread_attr_t* attr_ptr = NULL; + pthread_attr_t attr; + if (stack_size_ > 0) { + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_)); + attr_ptr = &attr; + } + pthread_create(&thread_handle_data()->thread_, attr_ptr, ThreadEntry, this); ASSERT(IsValid()); } void Thread::Join() { @@ -758,14 +839,10 @@ } #ifdef ENABLE_LOGGING_AND_PROFILING -static Sampler* active_sampler_ = NULL; -static int vm_tid_ = 0; - - #if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__)) // Android runs a fairly new Linux kernel, so signal info is there, // but the C library doesn't have the structs defined. struct sigcontext { @@ -790,29 +867,38 @@ #endif static int GetThreadID() { // Glibc doesn't provide a wrapper for gettid(2). +#if defined(ANDROID) + return syscall(__NR_gettid); +#else return syscall(SYS_gettid); +#endif } static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { #ifndef V8_HOST_ARCH_MIPS 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; + } + 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(); #if V8_HOST_ARCH_IA32 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]); sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]); sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]); #elif V8_HOST_ARCH_X64 @@ -829,58 +915,145 @@ sample->pc = reinterpret_cast<Address>(mcontext.arm_pc); sample->sp = reinterpret_cast<Address>(mcontext.arm_sp); sample->fp = reinterpret_cast<Address>(mcontext.arm_fp); #endif #elif V8_HOST_ARCH_MIPS - // Implement this on MIPS. - UNIMPLEMENTED(); + sample.pc = reinterpret_cast<Address>(mcontext.pc); + sample.sp = reinterpret_cast<Address>(mcontext.gregs[29]); + sample.fp = reinterpret_cast<Address>(mcontext.gregs[30]); #endif - active_sampler_->SampleStack(sample); - active_sampler_->Tick(sample); + sampler->SampleStack(sample); + sampler->Tick(sample); #endif } class Sampler::PlatformData : public Malloced { public: + PlatformData() : vm_tid_(GetThreadID()) {} + + int vm_tid() const { return vm_tid_; } + + private: + const int 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), + explicit SignalSender(int interval) + : Thread(NULL, "SignalSender"), vm_tgid_(getpid()), - signal_sender_launched_(false) { + interval_(interval) {} + + static void AddActiveSampler(Sampler* sampler) { + ScopedLock lock(mutex_); + SamplerRegistry::AddActiveSampler(sampler); + if (instance_ == NULL) { + // Install a signal handler. + 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); + + // Start a thread that sends SIGPROF signal to VM threads. + instance_ = new SignalSender(sampler->interval()); + instance_->Start(); + } else { + ASSERT(instance_->interval_ == sampler->interval()); + } } - void SignalSender() { - while (sampler_->IsActive()) { - if (rate_limiter_.SuspendIfNecessary()) continue; - if (sampler_->IsProfiling() && RuntimeProfiler::IsEnabled()) { - SendProfilingSignal(); + 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; + + // Restore the old signal handler. + if (signal_handler_installed_) { + sigaction(SIGPROF, &old_signal_handler_, 0); + signal_handler_installed_ = false; + } + } + } + + // 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(); + // 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(int tid) { + if (!signal_handler_installed_) return; // Glibc doesn't provide a wrapper for tgkill(2). - syscall(SYS_tgkill, vm_tgid_, vm_tid_, SIGPROF); +#if defined(ANDROID) + syscall(__NR_tgkill, vm_tgid_, tid, SIGPROF); +#else + syscall(SYS_tgkill, vm_tgid_, tid, SIGPROF); +#endif } 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 if (result != 0 && errno != EINTR) { fprintf(stderr, @@ -891,91 +1064,57 @@ } #endif 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 vm_tgid_; + 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_; + + DISALLOW_COPY_AND_ASSIGN(SignalSender); }; -static void* SenderEntry(void* arg) { - Sampler::PlatformData* data = - reinterpret_cast<Sampler::PlatformData*>(arg); - data->SignalSender(); - return 0; -} +Mutex* SignalSender::mutex_ = OS::CreateMutex(); +SignalSender* SignalSender::instance_ = NULL; +struct sigaction SignalSender::old_signal_handler_; +bool SignalSender::signal_handler_installed_ = false; -Sampler::Sampler(int interval) - : interval_(interval), +Sampler::Sampler(Isolate* isolate, int interval) + : isolate_(isolate), + interval_(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; - if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; - data_->signal_handler_installed_ = true; - - // 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 } } // namespace v8::internal