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

- old
+ new

@@ -46,12 +46,14 @@ #include <mach/task.h> #include <mach/vm_statistics.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/types.h> +#include <sys/sysctl.h> #include <stdarg.h> #include <stdlib.h> +#include <string.h> #include <errno.h> #undef MAP_TYPE #include "v8.h" @@ -86,18 +88,22 @@ 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 will 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(); } // We keep the lowest and highest addresses mapped as a quick way of // determining that pointers are outside the heap (used mostly in assertions @@ -107,10 +113,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)); } @@ -141,11 +150,11 @@ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, kMmapFd, kMmapFdOffset); if (mbase == MAP_FAILED) { - LOG(StringEvent("OS::Allocate", "mmap failed")); + LOG(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed")); return NULL; } *allocated = msize; UpdateAllocatedSpaceLimits(mbase, msize); return mbase; @@ -256,11 +265,12 @@ char* code_ptr = getsectdatafromheader(header, SEG_TEXT, SECT_TEXT, &size); #endif if (code_ptr == NULL) continue; const uintptr_t slide = _dyld_get_image_vmaddr_slide(i); const uintptr_t start = reinterpret_cast<uintptr_t>(code_ptr) + slide; - LOG(SharedLibraryEvent(_dyld_get_image_name(i), start, start + size)); + LOG(Isolate::Current(), + SharedLibraryEvent(_dyld_get_image_name(i), start, start + size)); } #endif // ENABLE_LOGGING_AND_PROFILING } @@ -422,25 +432,30 @@ 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() { } - static void SetThreadName(const char* name) { // pthread_setname_np is only available in 10.6 or later, so test // for it at runtime. int (*dynamic_pthread_setname_np)(const char*); *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = @@ -462,10 +477,11 @@ // don't know which thread will run first (the original thread or the new // one) so we initialize it here too. thread->thread_handle_data()->thread_ = pthread_self(); SetThreadName(thread->name()); ASSERT(thread->IsValid()); + Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate()); thread->Run(); return NULL; } @@ -474,25 +490,100 @@ 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() { pthread_join(thread_handle_data()->thread_, NULL); } +#ifdef V8_FAST_TLS_SUPPORTED + +static Atomic32 tls_base_offset_initialized = 0; +intptr_t kMacTlsBaseOffset = 0; + +// It's safe to do the initialization more that once, but it has to be +// done at least once. +static void InitializeTlsBaseOffset() { + const size_t kBufferSize = 128; + char buffer[kBufferSize]; + size_t buffer_size = kBufferSize; + int ctl_name[] = { CTL_KERN , KERN_OSRELEASE }; + if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) { + V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version"); + } + // The buffer now contains a string of the form XX.YY.ZZ, where + // XX is the major kernel version component. + // Make sure the buffer is 0-terminated. + buffer[kBufferSize - 1] = '\0'; + char* period_pos = strchr(buffer, '.'); + *period_pos = '\0'; + int kernel_version_major = + static_cast<int>(strtol(buffer, NULL, 10)); // NOLINT + // The constants below are taken from pthreads.s from the XNU kernel + // sources archive at www.opensource.apple.com. + if (kernel_version_major < 11) { + // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the + // same offsets. +#if defined(V8_HOST_ARCH_IA32) + kMacTlsBaseOffset = 0x48; +#else + kMacTlsBaseOffset = 0x60; +#endif + } else { + // 11.x.x (Lion) changed the offset. + kMacTlsBaseOffset = 0; + } + + Release_Store(&tls_base_offset_initialized, 1); +} + +static void CheckFastTls(Thread::LocalStorageKey key) { + void* expected = reinterpret_cast<void*>(0x1234CAFE); + Thread::SetThreadLocal(key, expected); + void* actual = Thread::GetExistingThreadLocal(key); + if (expected != actual) { + V8_Fatal(__FILE__, __LINE__, + "V8 failed to initialize fast TLS on current kernel"); + } + Thread::SetThreadLocal(key, NULL); +} + +#endif // V8_FAST_TLS_SUPPORTED + + Thread::LocalStorageKey Thread::CreateThreadLocalKey() { +#ifdef V8_FAST_TLS_SUPPORTED + bool check_fast_tls = false; + if (tls_base_offset_initialized == 0) { + check_fast_tls = true; + InitializeTlsBaseOffset(); + } +#endif pthread_key_t key; int result = pthread_key_create(&key, NULL); USE(result); ASSERT(result == 0); - return static_cast<LocalStorageKey>(key); + LocalStorageKey typed_key = static_cast<LocalStorageKey>(key); +#ifdef V8_FAST_TLS_SUPPORTED + // If we just initialized fast TLS support, make sure it works. + if (check_fast_tls) CheckFastTls(typed_key); +#endif + return typed_key; } void Thread::DeleteThreadLocalKey(LocalStorageKey key) { pthread_key_t pthread_key = static_cast<pthread_key_t>(key); @@ -593,139 +684,181 @@ #ifdef ENABLE_LOGGING_AND_PROFILING class Sampler::PlatformData : public Malloced { public: - explicit PlatformData(Sampler* sampler) - : sampler_(sampler), - task_self_(mach_task_self()), - profiled_thread_(0), - sampler_thread_(0) { + PlatformData() : profiled_thread_(mach_thread_self()) {} + + ~PlatformData() { + // Deallocate Mach port for thread. + mach_port_deallocate(mach_task_self(), profiled_thread_); } - Sampler* sampler_; + thread_act_t profiled_thread() { return profiled_thread_; } + + 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. - mach_port_t task_self_; thread_act_t profiled_thread_; - pthread_t sampler_thread_; - RuntimeProfilerRateLimiter rate_limiter_; +}; - // Sampler thread handler. - void Runner() { - while (sampler_->IsActive()) { - if (rate_limiter_.SuspendIfNecessary()) continue; - Sample(); - OS::Sleep(sampler_->interval_); +class SamplerThread : public Thread { + public: + explicit SamplerThread(int interval) + : Thread(NULL, "SamplerThread"), + interval_(interval) {} + + static void AddActiveSampler(Sampler* sampler) { + ScopedLock lock(mutex_); + SamplerRegistry::AddActiveSampler(sampler); + if (instance_ == NULL) { + instance_ = new SamplerThread(sampler->interval()); + instance_->Start(); + } else { + ASSERT(instance_->interval_ == sampler->interval()); } } - void Sample() { - if (sampler_->IsProfiling()) { - TickSample sample_obj; - TickSample* sample = CpuProfiler::TickSampleEvent(); - if (sample == NULL) sample = &sample_obj; + 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; + } + } - if (KERN_SUCCESS != thread_suspend(profiled_thread_)) return; + // 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) { + if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) { + return; + } + } + if (runtime_profiler_enabled) { + if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) { + return; + } + } + OS::Sleep(interval_); + } + } + static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) { + if (!sampler->isolate()->IsInitialized()) return; + if (!sampler->IsProfiling()) return; + SamplerThread* sampler_thread = + reinterpret_cast<SamplerThread*>(raw_sampler_thread); + sampler_thread->SampleContext(sampler); + } + + static void DoRuntimeProfile(Sampler* sampler, void* ignored) { + if (!sampler->isolate()->IsInitialized()) return; + sampler->isolate()->runtime_profiler()->NotifyTick(); + } + + void SampleContext(Sampler* sampler) { + thread_act_t profiled_thread = sampler->platform_data()->profiled_thread(); + TickSample sample_obj; + TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate()); + 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; + 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; + 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 = Top::current_vm_state(); - 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)); - sampler_->SampleStack(sample); - sampler_->Tick(sample); - } - thread_resume(profiled_thread_); + if (thread_get_state(profiled_thread, + flavor, + reinterpret_cast<natural_t*>(&state), + &count) == KERN_SUCCESS) { + sample->state = sampler->isolate()->current_vm_state(); + 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)); + sampler->SampleStack(sample); + sampler->Tick(sample); } - if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick(); + thread_resume(profiled_thread); } + + const int interval_; + RuntimeProfilerRateLimiter rate_limiter_; + + // Protects the process wide state below. + static Mutex* mutex_; + static SamplerThread* instance_; + + DISALLOW_COPY_AND_ASSIGN(SamplerThread); }; #undef REGISTER_FIELD -// Entry point for sampler thread. -static void* SamplerEntry(void* arg) { - Sampler::PlatformData* data = - reinterpret_cast<Sampler::PlatformData*>(arg); - data->Runner(); - return 0; -} +Mutex* SamplerThread::mutex_ = OS::CreateMutex(); +SamplerThread* SamplerThread::instance_ = NULL; -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(!IsActive()); delete data_; } void Sampler::Start() { - // Do not start multiple threads for the same sampler. ASSERT(!IsActive()); - 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); - SetActive(true); - pthread_create(&data_->sampler_thread_, &sched_attr, SamplerEntry, data_); + SamplerThread::AddActiveSampler(this); } void Sampler::Stop() { - // Seting active to false triggers termination of the sampler - // thread. + ASSERT(IsActive()); + SamplerThread::RemoveActiveSampler(this); SetActive(false); - - // Wait for sampler thread to terminate. - Top::WakeUpRuntimeProfilerThreadBeforeShutdown(); - pthread_join(data_->sampler_thread_, NULL); - - // Deallocate Mach port for thread. - mach_port_deallocate(data_->task_self_, data_->profiled_thread_); } #endif // ENABLE_LOGGING_AND_PROFILING } } // namespace v8::internal