diff options
Diffstat (limited to 'src/3rdparty/v8/src/platform-macos.cc')
-rw-r--r-- | src/3rdparty/v8/src/platform-macos.cc | 865 |
1 files changed, 865 insertions, 0 deletions
diff --git a/src/3rdparty/v8/src/platform-macos.cc b/src/3rdparty/v8/src/platform-macos.cc new file mode 100644 index 0000000..bfdf3b2 --- /dev/null +++ b/src/3rdparty/v8/src/platform-macos.cc @@ -0,0 +1,865 @@ +// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Platform specific code for MacOS goes here. For the POSIX comaptible parts +// the implementation is in platform-posix.cc. + +#include <dlfcn.h> +#include <unistd.h> +#include <sys/mman.h> +#include <mach/mach_init.h> +#include <mach-o/dyld.h> +#include <mach-o/getsect.h> + +#include <AvailabilityMacros.h> + +#include <pthread.h> +#include <semaphore.h> +#include <signal.h> +#include <libkern/OSAtomic.h> +#include <mach/mach.h> +#include <mach/semaphore.h> +#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" + +#include "platform.h" +#include "vm-state-inl.h" + +// Manually define these here as weak imports, rather than including execinfo.h. +// This lets us launch on 10.4 which does not have these calls. +extern "C" { + extern int backtrace(void**, int) __attribute__((weak_import)); + extern char** backtrace_symbols(void* const*, int) + __attribute__((weak_import)); + extern void backtrace_symbols_fd(void* const*, int, int) + __attribute__((weak_import)); +} + + +namespace v8 { +namespace internal { + +// 0 is never a valid thread id on MacOSX since a ptread_t is +// a pointer. +static const pthread_t kNoThread = (pthread_t) 0; + + +double ceiling(double x) { + // Correct Mac OS X Leopard 'ceil' behavior. + if (-1.0 < x && x < 0.0) { + return -0.0; + } else { + 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 +// and verification). The estimate is conservative, ie, not all addresses in +// 'allocated' space are actually allocated to our heap. The range is +// [lowest, highest), inclusive on the low and and exclusive on the high end. +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)); +} + + +bool OS::IsOutsideAllocatedSpace(void* address) { + return address < lowest_ever_allocated || address >= highest_ever_allocated; +} + + +size_t OS::AllocateAlignment() { + return getpagesize(); +} + + +// Constants used for mmap. +// kMmapFd is used to pass vm_alloc flags to tag the region with the user +// defined tag 255 This helps identify V8-allocated regions in memory analysis +// tools like vmmap(1). +static const int kMmapFd = VM_MAKE_TAG(255); +static const off_t kMmapFdOffset = 0; + + +void* OS::Allocate(const size_t requested, + size_t* allocated, + bool is_executable) { + const size_t msize = RoundUp(requested, getpagesize()); + 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(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed")); + return NULL; + } + *allocated = msize; + UpdateAllocatedSpaceLimits(mbase, msize); + return mbase; +} + + +void OS::Free(void* address, const size_t size) { + // TODO(1240712): munmap has a return value which is ignored here. + int result = munmap(address, size); + USE(result); + ASSERT(result == 0); +} + + +#ifdef ENABLE_HEAP_PROTECTION + +void OS::Protect(void* address, size_t size) { + UNIMPLEMENTED(); +} + + +void OS::Unprotect(void* address, size_t size, bool is_executable) { + UNIMPLEMENTED(); +} + +#endif + + +void OS::Sleep(int milliseconds) { + usleep(1000 * milliseconds); +} + + +void OS::Abort() { + // Redirect to std abort to signal abnormal program termination + abort(); +} + + +void OS::DebugBreak() { + asm("int $3"); +} + + +class PosixMemoryMappedFile : public OS::MemoryMappedFile { + public: + PosixMemoryMappedFile(FILE* file, void* memory, int size) + : file_(file), memory_(memory), size_(size) { } + virtual ~PosixMemoryMappedFile(); + virtual void* memory() { return memory_; } + virtual int size() { return size_; } + private: + FILE* file_; + void* memory_; + int size_; +}; + + +OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { + FILE* file = fopen(name, "r+"); + if (file == NULL) return NULL; + + fseek(file, 0, SEEK_END); + int size = ftell(file); + + void* memory = + mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); + return new PosixMemoryMappedFile(file, memory, size); +} + + +OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, + void* initial) { + FILE* file = fopen(name, "w+"); + if (file == NULL) return NULL; + int result = fwrite(initial, size, 1, file); + if (result < 1) { + fclose(file); + return NULL; + } + void* memory = + mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); + return new PosixMemoryMappedFile(file, memory, size); +} + + +PosixMemoryMappedFile::~PosixMemoryMappedFile() { + if (memory_) munmap(memory_, size_); + fclose(file_); +} + + +void OS::LogSharedLibraryAddresses() { +#ifdef ENABLE_LOGGING_AND_PROFILING + unsigned int images_count = _dyld_image_count(); + for (unsigned int i = 0; i < images_count; ++i) { + const mach_header* header = _dyld_get_image_header(i); + if (header == NULL) continue; +#if V8_HOST_ARCH_X64 + uint64_t size; + char* code_ptr = getsectdatafromheader_64( + reinterpret_cast<const mach_header_64*>(header), + SEG_TEXT, + SECT_TEXT, + &size); +#else + unsigned int size; + 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(Isolate::Current(), + SharedLibraryEvent(_dyld_get_image_name(i), start, start + size)); + } +#endif // ENABLE_LOGGING_AND_PROFILING +} + + +void OS::SignalCodeMovingGC() { +} + + +uint64_t OS::CpuFeaturesImpliedByPlatform() { + // MacOSX requires all these to install so we can assume they are present. + // These constants are defined by the CPUid instructions. + const uint64_t one = 1; + return (one << SSE2) | (one << CMOV) | (one << RDTSC) | (one << CPUID); +} + + +int OS::ActivationFrameAlignment() { + // OS X activation frames must be 16 byte-aligned; see "Mac OS X ABI + // Function Call Guide". + return 16; +} + + +void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { + OSMemoryBarrier(); + *ptr = value; +} + + +const char* OS::LocalTimezone(double time) { + if (isnan(time)) return ""; + time_t tv = static_cast<time_t>(floor(time/msPerSecond)); + struct tm* t = localtime(&tv); + if (NULL == t) return ""; + return t->tm_zone; +} + + +double OS::LocalTimeOffset() { + time_t tv = time(NULL); + struct tm* t = localtime(&tv); + // tm_gmtoff includes any daylight savings offset, so subtract it. + return static_cast<double>(t->tm_gmtoff * msPerSecond - + (t->tm_isdst > 0 ? 3600 * msPerSecond : 0)); +} + + +int OS::StackWalk(Vector<StackFrame> frames) { + // If weak link to execinfo lib has failed, ie because we are on 10.4, abort. + if (backtrace == NULL) + return 0; + + int frames_size = frames.length(); + ScopedVector<void*> addresses(frames_size); + + int frames_count = backtrace(addresses.start(), frames_size); + + char** symbols = backtrace_symbols(addresses.start(), frames_count); + if (symbols == NULL) { + return kStackWalkError; + } + + for (int i = 0; i < frames_count; i++) { + frames[i].address = addresses[i]; + // Format a text representation of the frame based on the information + // available. + SNPrintF(MutableCStrVector(frames[i].text, + kStackWalkMaxTextLen), + "%s", + symbols[i]); + // Make sure line termination is in place. + frames[i].text[kStackWalkMaxTextLen - 1] = '\0'; + } + + free(symbols); + + return frames_count; +} + + + + +VirtualMemory::VirtualMemory(size_t size) { + address_ = mmap(NULL, size, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, + kMmapFd, kMmapFdOffset); + size_ = size; +} + + +VirtualMemory::~VirtualMemory() { + if (IsReserved()) { + if (0 == munmap(address(), size())) address_ = MAP_FAILED; + } +} + + +bool VirtualMemory::IsReserved() { + return address_ != MAP_FAILED; +} + + +bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { + int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); + if (MAP_FAILED == mmap(address, size, prot, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + kMmapFd, kMmapFdOffset)) { + return false; + } + + UpdateAllocatedSpaceLimits(address, size); + return true; +} + + +bool VirtualMemory::Uncommit(void* address, size_t size) { + return mmap(address, size, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, + kMmapFd, kMmapFdOffset) != MAP_FAILED; +} + + +class ThreadHandle::PlatformData : public Malloced { + public: + explicit PlatformData(ThreadHandle::Kind kind) { + Initialize(kind); + } + + void Initialize(ThreadHandle::Kind kind) { + switch (kind) { + case ThreadHandle::SELF: thread_ = pthread_self(); break; + case ThreadHandle::INVALID: thread_ = kNoThread; break; + } + } + pthread_t thread_; // Thread handle for pthread. +}; + + + +ThreadHandle::ThreadHandle(Kind kind) { + data_ = new PlatformData(kind); +} + + +void ThreadHandle::Initialize(ThreadHandle::Kind kind) { + data_->Initialize(kind); +} + + +ThreadHandle::~ThreadHandle() { + delete data_; +} + + +bool ThreadHandle::IsSelf() const { + return pthread_equal(data_->thread_, pthread_self()); +} + + +bool ThreadHandle::IsValid() const { + return data_->thread_ != kNoThread; +} + + +Thread::Thread(Isolate* isolate, const Options& options) + : ThreadHandle(ThreadHandle::INVALID), + isolate_(isolate), + stack_size_(options.stack_size) { + set_name(options.name); +} + + +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) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + if (!dynamic_pthread_setname_np) + return; + + // Mac OS X does not expose the length limit of the name, so hardcode it. + static const int kMaxNameLength = 63; + USE(kMaxNameLength); + ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength); + dynamic_pthread_setname_np(name); +} + + +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. + thread->thread_handle_data()->thread_ = pthread_self(); + SetThreadName(thread->name()); + ASSERT(thread->IsValid()); + Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate()); + thread->Run(); + return NULL; +} + + +void Thread::set_name(const char* name) { + strncpy(name_, name, sizeof(name_)); + name_[sizeof(name_) - 1] = '\0'; +} + + +void Thread::Start() { + 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); + 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); + int result = pthread_key_delete(pthread_key); + USE(result); + ASSERT(result == 0); +} + + +void* Thread::GetThreadLocal(LocalStorageKey key) { + pthread_key_t pthread_key = static_cast<pthread_key_t>(key); + return pthread_getspecific(pthread_key); +} + + +void Thread::SetThreadLocal(LocalStorageKey key, void* value) { + pthread_key_t pthread_key = static_cast<pthread_key_t>(key); + pthread_setspecific(pthread_key, value); +} + + +void Thread::YieldCPU() { + sched_yield(); +} + + +class MacOSMutex : public Mutex { + public: + + MacOSMutex() { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex_, &attr); + } + + virtual ~MacOSMutex() { pthread_mutex_destroy(&mutex_); } + + virtual int Lock() { return pthread_mutex_lock(&mutex_); } + virtual int Unlock() { return pthread_mutex_unlock(&mutex_); } + + virtual bool TryLock() { + int result = pthread_mutex_trylock(&mutex_); + // Return false if the lock is busy and locking failed. + if (result == EBUSY) { + return false; + } + ASSERT(result == 0); // Verify no other errors. + return true; + } + + private: + pthread_mutex_t mutex_; +}; + + +Mutex* OS::CreateMutex() { + return new MacOSMutex(); +} + + +class MacOSSemaphore : public Semaphore { + public: + explicit MacOSSemaphore(int count) { + semaphore_create(mach_task_self(), &semaphore_, SYNC_POLICY_FIFO, count); + } + + ~MacOSSemaphore() { + semaphore_destroy(mach_task_self(), semaphore_); + } + + // The MacOS mach semaphore documentation claims it does not have spurious + // wakeups, the way pthreads semaphores do. So the code from the linux + // platform is not needed here. + void Wait() { semaphore_wait(semaphore_); } + + bool Wait(int timeout); + + void Signal() { semaphore_signal(semaphore_); } + + private: + semaphore_t semaphore_; +}; + + +bool MacOSSemaphore::Wait(int timeout) { + mach_timespec_t ts; + ts.tv_sec = timeout / 1000000; + ts.tv_nsec = (timeout % 1000000) * 1000; + return semaphore_timedwait(semaphore_, ts) != KERN_OPERATION_TIMED_OUT; +} + + +Semaphore* OS::CreateSemaphore(int count) { + return new MacOSSemaphore(count); +} + + +#ifdef ENABLE_LOGGING_AND_PROFILING + +class Sampler::PlatformData : public Malloced { + public: + PlatformData() : profiled_thread_(mach_thread_self()) {} + + ~PlatformData() { + // Deallocate Mach port for thread. + mach_port_deallocate(mach_task_self(), profiled_thread_); + } + + 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. + thread_act_t profiled_thread_; +}; + +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()); + } + } + + 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; + } + } + + // 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; +#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 = 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); + } + 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 + + +Mutex* SamplerThread::mutex_ = OS::CreateMutex(); +SamplerThread* SamplerThread::instance_ = NULL; + + +Sampler::Sampler(Isolate* isolate, int interval) + : isolate_(isolate), + interval_(interval), + profiling_(false), + active_(false), + samples_taken_(0) { + data_ = new PlatformData; +} + + +Sampler::~Sampler() { + ASSERT(!IsActive()); + delete data_; +} + + +void Sampler::Start() { + ASSERT(!IsActive()); + SetActive(true); + SamplerThread::AddActiveSampler(this); +} + + +void Sampler::Stop() { + ASSERT(IsActive()); + SamplerThread::RemoveActiveSampler(this); + SetActive(false); +} + +#endif // ENABLE_LOGGING_AND_PROFILING + +} } // namespace v8::internal |