Home | History | Annotate | Download | only in src
      1 // Copyright 2012 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 // Platform specific code for Cygwin goes here. For the POSIX comaptible parts
     29 // the implementation is in platform-posix.cc.
     30 
     31 #include <errno.h>
     32 #include <pthread.h>
     33 #include <semaphore.h>
     34 #include <stdarg.h>
     35 #include <strings.h>    // index
     36 #include <sys/time.h>
     37 #include <sys/mman.h>   // mmap & munmap
     38 #include <unistd.h>     // sysconf
     39 
     40 #undef MAP_TYPE
     41 
     42 #include "v8.h"
     43 
     44 #include "platform-posix.h"
     45 #include "platform.h"
     46 #include "v8threads.h"
     47 #include "vm-state-inl.h"
     48 #include "win32-headers.h"
     49 
     50 namespace v8 {
     51 namespace internal {
     52 
     53 // 0 is never a valid thread id
     54 static const pthread_t kNoThread = (pthread_t) 0;
     55 
     56 
     57 double ceiling(double x) {
     58   return ceil(x);
     59 }
     60 
     61 
     62 static Mutex* limit_mutex = NULL;
     63 
     64 
     65 void OS::SetUp() {
     66   // Seed the random number generator.
     67   // Convert the current time to a 64-bit integer first, before converting it
     68   // to an unsigned. Going directly can cause an overflow and the seed to be
     69   // set to all ones. The seed will be identical for different instances that
     70   // call this setup code within the same millisecond.
     71   uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
     72   srandom(static_cast<unsigned int>(seed));
     73   limit_mutex = CreateMutex();
     74 }
     75 
     76 
     77 void OS::PostSetUp() {
     78   // Math functions depend on CPU features therefore they are initialized after
     79   // CPU.
     80   MathSetup();
     81 }
     82 
     83 uint64_t OS::CpuFeaturesImpliedByPlatform() {
     84   return 0;  // Nothing special about Cygwin.
     85 }
     86 
     87 
     88 int OS::ActivationFrameAlignment() {
     89   // With gcc 4.4 the tree vectorization optimizer can generate code
     90   // that requires 16 byte alignment such as movdqa on x86.
     91   return 16;
     92 }
     93 
     94 
     95 void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
     96   __asm__ __volatile__("" : : : "memory");
     97   // An x86 store acts as a release barrier.
     98   *ptr = value;
     99 }
    100 
    101 const char* OS::LocalTimezone(double time) {
    102   if (isnan(time)) return "";
    103   time_t tv = static_cast<time_t>(floor(time/msPerSecond));
    104   struct tm* t = localtime(&tv);
    105   if (NULL == t) return "";
    106   return tzname[0];  // The location of the timezone string on Cygwin.
    107 }
    108 
    109 
    110 double OS::LocalTimeOffset() {
    111   // On Cygwin, struct tm does not contain a tm_gmtoff field.
    112   time_t utc = time(NULL);
    113   ASSERT(utc != -1);
    114   struct tm* loc = localtime(&utc);
    115   ASSERT(loc != NULL);
    116   // time - localtime includes any daylight savings offset, so subtract it.
    117   return static_cast<double>((mktime(loc) - utc) * msPerSecond -
    118                              (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0));
    119 }
    120 
    121 
    122 // We keep the lowest and highest addresses mapped as a quick way of
    123 // determining that pointers are outside the heap (used mostly in assertions
    124 // and verification).  The estimate is conservative, i.e., not all addresses in
    125 // 'allocated' space are actually allocated to our heap.  The range is
    126 // [lowest, highest), inclusive on the low and and exclusive on the high end.
    127 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
    128 static void* highest_ever_allocated = reinterpret_cast<void*>(0);
    129 
    130 
    131 static void UpdateAllocatedSpaceLimits(void* address, int size) {
    132   ASSERT(limit_mutex != NULL);
    133   ScopedLock lock(limit_mutex);
    134 
    135   lowest_ever_allocated = Min(lowest_ever_allocated, address);
    136   highest_ever_allocated =
    137       Max(highest_ever_allocated,
    138           reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
    139 }
    140 
    141 
    142 bool OS::IsOutsideAllocatedSpace(void* address) {
    143   return address < lowest_ever_allocated || address >= highest_ever_allocated;
    144 }
    145 
    146 
    147 size_t OS::AllocateAlignment() {
    148   return sysconf(_SC_PAGESIZE);
    149 }
    150 
    151 
    152 void* OS::Allocate(const size_t requested,
    153                    size_t* allocated,
    154                    bool is_executable) {
    155   const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
    156   int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
    157   void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    158   if (mbase == MAP_FAILED) {
    159     LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
    160     return NULL;
    161   }
    162   *allocated = msize;
    163   UpdateAllocatedSpaceLimits(mbase, msize);
    164   return mbase;
    165 }
    166 
    167 
    168 void OS::Free(void* address, const size_t size) {
    169   // TODO(1240712): munmap has a return value which is ignored here.
    170   int result = munmap(address, size);
    171   USE(result);
    172   ASSERT(result == 0);
    173 }
    174 
    175 
    176 void OS::ProtectCode(void* address, const size_t size) {
    177   DWORD old_protect;
    178   VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
    179 }
    180 
    181 
    182 void OS::Guard(void* address, const size_t size) {
    183   DWORD oldprotect;
    184   VirtualProtect(address, size, PAGE_READONLY | PAGE_GUARD, &oldprotect);
    185 }
    186 
    187 
    188 void OS::Sleep(int milliseconds) {
    189   unsigned int ms = static_cast<unsigned int>(milliseconds);
    190   usleep(1000 * ms);
    191 }
    192 
    193 
    194 void OS::Abort() {
    195   // Redirect to std abort to signal abnormal program termination.
    196   abort();
    197 }
    198 
    199 
    200 void OS::DebugBreak() {
    201   asm("int $3");
    202 }
    203 
    204 
    205 class PosixMemoryMappedFile : public OS::MemoryMappedFile {
    206  public:
    207   PosixMemoryMappedFile(FILE* file, void* memory, int size)
    208     : file_(file), memory_(memory), size_(size) { }
    209   virtual ~PosixMemoryMappedFile();
    210   virtual void* memory() { return memory_; }
    211   virtual int size() { return size_; }
    212  private:
    213   FILE* file_;
    214   void* memory_;
    215   int size_;
    216 };
    217 
    218 
    219 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
    220   FILE* file = fopen(name, "r+");
    221   if (file == NULL) return NULL;
    222 
    223   fseek(file, 0, SEEK_END);
    224   int size = ftell(file);
    225 
    226   void* memory =
    227       mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
    228   return new PosixMemoryMappedFile(file, memory, size);
    229 }
    230 
    231 
    232 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
    233     void* initial) {
    234   FILE* file = fopen(name, "w+");
    235   if (file == NULL) return NULL;
    236   int result = fwrite(initial, size, 1, file);
    237   if (result < 1) {
    238     fclose(file);
    239     return NULL;
    240   }
    241   void* memory =
    242       mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
    243   return new PosixMemoryMappedFile(file, memory, size);
    244 }
    245 
    246 
    247 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
    248   if (memory_) munmap(memory_, size_);
    249   fclose(file_);
    250 }
    251 
    252 
    253 void OS::LogSharedLibraryAddresses() {
    254   // This function assumes that the layout of the file is as follows:
    255   // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
    256   // If we encounter an unexpected situation we abort scanning further entries.
    257   FILE* fp = fopen("/proc/self/maps", "r");
    258   if (fp == NULL) return;
    259 
    260   // Allocate enough room to be able to store a full file name.
    261   const int kLibNameLen = FILENAME_MAX + 1;
    262   char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
    263 
    264   i::Isolate* isolate = ISOLATE;
    265   // This loop will terminate once the scanning hits an EOF.
    266   while (true) {
    267     uintptr_t start, end;
    268     char attr_r, attr_w, attr_x, attr_p;
    269     // Parse the addresses and permission bits at the beginning of the line.
    270     if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
    271     if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
    272 
    273     int c;
    274     if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
    275       // Found a read-only executable entry. Skip characters until we reach
    276       // the beginning of the filename or the end of the line.
    277       do {
    278         c = getc(fp);
    279       } while ((c != EOF) && (c != '\n') && (c != '/'));
    280       if (c == EOF) break;  // EOF: Was unexpected, just exit.
    281 
    282       // Process the filename if found.
    283       if (c == '/') {
    284         ungetc(c, fp);  // Push the '/' back into the stream to be read below.
    285 
    286         // Read to the end of the line. Exit if the read fails.
    287         if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
    288 
    289         // Drop the newline character read by fgets. We do not need to check
    290         // for a zero-length string because we know that we at least read the
    291         // '/' character.
    292         lib_name[strlen(lib_name) - 1] = '\0';
    293       } else {
    294         // No library name found, just record the raw address range.
    295         snprintf(lib_name, kLibNameLen,
    296                  "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
    297       }
    298       LOG(isolate, SharedLibraryEvent(lib_name, start, end));
    299     } else {
    300       // Entry not describing executable data. Skip to end of line to set up
    301       // reading the next entry.
    302       do {
    303         c = getc(fp);
    304       } while ((c != EOF) && (c != '\n'));
    305       if (c == EOF) break;
    306     }
    307   }
    308   free(lib_name);
    309   fclose(fp);
    310 }
    311 
    312 
    313 void OS::SignalCodeMovingGC() {
    314   // Nothing to do on Cygwin.
    315 }
    316 
    317 
    318 int OS::StackWalk(Vector<OS::StackFrame> frames) {
    319   // Not supported on Cygwin.
    320   return 0;
    321 }
    322 
    323 
    324 // The VirtualMemory implementation is taken from platform-win32.cc.
    325 // The mmap-based virtual memory implementation as it is used on most posix
    326 // platforms does not work well because Cygwin does not support MAP_FIXED.
    327 // This causes VirtualMemory::Commit to not always commit the memory region
    328 // specified.
    329 
    330 bool VirtualMemory::IsReserved() {
    331   return address_ != NULL;
    332 }
    333 
    334 
    335 VirtualMemory::VirtualMemory(size_t size) {
    336   address_ = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
    337   size_ = size;
    338 }
    339 
    340 
    341 VirtualMemory::~VirtualMemory() {
    342   if (IsReserved()) {
    343     if (0 == VirtualFree(address(), 0, MEM_RELEASE)) address_ = NULL;
    344   }
    345 }
    346 
    347 
    348 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
    349   int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
    350   if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) {
    351     return false;
    352   }
    353 
    354   UpdateAllocatedSpaceLimits(address, static_cast<int>(size));
    355   return true;
    356 }
    357 
    358 
    359 bool VirtualMemory::Uncommit(void* address, size_t size) {
    360   ASSERT(IsReserved());
    361   return VirtualFree(address, size, MEM_DECOMMIT) != false;
    362 }
    363 
    364 
    365 bool VirtualMemory::Guard(void* address) {
    366   if (NULL == VirtualAlloc(address,
    367                            OS::CommitPageSize(),
    368                            MEM_COMMIT,
    369                            PAGE_READONLY | PAGE_GUARD)) {
    370     return false;
    371   }
    372   return true;
    373 }
    374 
    375 
    376 class Thread::PlatformData : public Malloced {
    377  public:
    378   PlatformData() : thread_(kNoThread) {}
    379   pthread_t thread_;  // Thread handle for pthread.
    380 };
    381 
    382 
    383 
    384 
    385 Thread::Thread(const Options& options)
    386     : data_(new PlatformData()),
    387       stack_size_(options.stack_size()) {
    388   set_name(options.name());
    389 }
    390 
    391 
    392 Thread::~Thread() {
    393   delete data_;
    394 }
    395 
    396 
    397 static void* ThreadEntry(void* arg) {
    398   Thread* thread = reinterpret_cast<Thread*>(arg);
    399   // This is also initialized by the first argument to pthread_create() but we
    400   // don't know which thread will run first (the original thread or the new
    401   // one) so we initialize it here too.
    402   thread->data()->thread_ = pthread_self();
    403   ASSERT(thread->data()->thread_ != kNoThread);
    404   thread->Run();
    405   return NULL;
    406 }
    407 
    408 
    409 void Thread::set_name(const char* name) {
    410   strncpy(name_, name, sizeof(name_));
    411   name_[sizeof(name_) - 1] = '\0';
    412 }
    413 
    414 
    415 void Thread::Start() {
    416   pthread_attr_t* attr_ptr = NULL;
    417   pthread_attr_t attr;
    418   if (stack_size_ > 0) {
    419     pthread_attr_init(&attr);
    420     pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
    421     attr_ptr = &attr;
    422   }
    423   pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
    424   ASSERT(data_->thread_ != kNoThread);
    425 }
    426 
    427 
    428 void Thread::Join() {
    429   pthread_join(data_->thread_, NULL);
    430 }
    431 
    432 
    433 static inline Thread::LocalStorageKey PthreadKeyToLocalKey(
    434     pthread_key_t pthread_key) {
    435   // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
    436   // because pthread_key_t is a pointer type on Cygwin. This will probably not
    437   // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
    438   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
    439   intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
    440   return static_cast<Thread::LocalStorageKey>(ptr_key);
    441 }
    442 
    443 
    444 static inline pthread_key_t LocalKeyToPthreadKey(
    445     Thread::LocalStorageKey local_key) {
    446   STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
    447   intptr_t ptr_key = static_cast<intptr_t>(local_key);
    448   return reinterpret_cast<pthread_key_t>(ptr_key);
    449 }
    450 
    451 
    452 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
    453   pthread_key_t key;
    454   int result = pthread_key_create(&key, NULL);
    455   USE(result);
    456   ASSERT(result == 0);
    457   return PthreadKeyToLocalKey(key);
    458 }
    459 
    460 
    461 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
    462   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
    463   int result = pthread_key_delete(pthread_key);
    464   USE(result);
    465   ASSERT(result == 0);
    466 }
    467 
    468 
    469 void* Thread::GetThreadLocal(LocalStorageKey key) {
    470   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
    471   return pthread_getspecific(pthread_key);
    472 }
    473 
    474 
    475 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
    476   pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
    477   pthread_setspecific(pthread_key, value);
    478 }
    479 
    480 
    481 void Thread::YieldCPU() {
    482   sched_yield();
    483 }
    484 
    485 
    486 class CygwinMutex : public Mutex {
    487  public:
    488   CygwinMutex() {
    489     pthread_mutexattr_t attrs;
    490     memset(&attrs, 0, sizeof(attrs));
    491 
    492     int result = pthread_mutexattr_init(&attrs);
    493     ASSERT(result == 0);
    494     result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
    495     ASSERT(result == 0);
    496     result = pthread_mutex_init(&mutex_, &attrs);
    497     ASSERT(result == 0);
    498   }
    499 
    500   virtual ~CygwinMutex() { pthread_mutex_destroy(&mutex_); }
    501 
    502   virtual int Lock() {
    503     int result = pthread_mutex_lock(&mutex_);
    504     return result;
    505   }
    506 
    507   virtual int Unlock() {
    508     int result = pthread_mutex_unlock(&mutex_);
    509     return result;
    510   }
    511 
    512   virtual bool TryLock() {
    513     int result = pthread_mutex_trylock(&mutex_);
    514     // Return false if the lock is busy and locking failed.
    515     if (result == EBUSY) {
    516       return false;
    517     }
    518     ASSERT(result == 0);  // Verify no other errors.
    519     return true;
    520   }
    521 
    522  private:
    523   pthread_mutex_t mutex_;   // Pthread mutex for POSIX platforms.
    524 };
    525 
    526 
    527 Mutex* OS::CreateMutex() {
    528   return new CygwinMutex();
    529 }
    530 
    531 
    532 class CygwinSemaphore : public Semaphore {
    533  public:
    534   explicit CygwinSemaphore(int count) {  sem_init(&sem_, 0, count); }
    535   virtual ~CygwinSemaphore() { sem_destroy(&sem_); }
    536 
    537   virtual void Wait();
    538   virtual bool Wait(int timeout);
    539   virtual void Signal() { sem_post(&sem_); }
    540  private:
    541   sem_t sem_;
    542 };
    543 
    544 
    545 void CygwinSemaphore::Wait() {
    546   while (true) {
    547     int result = sem_wait(&sem_);
    548     if (result == 0) return;  // Successfully got semaphore.
    549     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
    550   }
    551 }
    552 
    553 
    554 #ifndef TIMEVAL_TO_TIMESPEC
    555 #define TIMEVAL_TO_TIMESPEC(tv, ts) do {                            \
    556     (ts)->tv_sec = (tv)->tv_sec;                                    \
    557     (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
    558 } while (false)
    559 #endif
    560 
    561 
    562 bool CygwinSemaphore::Wait(int timeout) {
    563   const long kOneSecondMicros = 1000000;  // NOLINT
    564 
    565   // Split timeout into second and nanosecond parts.
    566   struct timeval delta;
    567   delta.tv_usec = timeout % kOneSecondMicros;
    568   delta.tv_sec = timeout / kOneSecondMicros;
    569 
    570   struct timeval current_time;
    571   // Get the current time.
    572   if (gettimeofday(&current_time, NULL) == -1) {
    573     return false;
    574   }
    575 
    576   // Calculate time for end of timeout.
    577   struct timeval end_time;
    578   timeradd(&current_time, &delta, &end_time);
    579 
    580   struct timespec ts;
    581   TIMEVAL_TO_TIMESPEC(&end_time, &ts);
    582   // Wait for semaphore signalled or timeout.
    583   while (true) {
    584     int result = sem_timedwait(&sem_, &ts);
    585     if (result == 0) return true;  // Successfully got semaphore.
    586     if (result == -1 && errno == ETIMEDOUT) return false;  // Timeout.
    587     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
    588   }
    589 }
    590 
    591 
    592 Semaphore* OS::CreateSemaphore(int count) {
    593   return new CygwinSemaphore(count);
    594 }
    595 
    596 
    597 // ----------------------------------------------------------------------------
    598 // Cygwin profiler support.
    599 //
    600 // On Cygwin we use the same sampler implementation as on win32.
    601 
    602 class Sampler::PlatformData : public Malloced {
    603  public:
    604   // Get a handle to the calling thread. This is the thread that we are
    605   // going to profile. We need to make a copy of the handle because we are
    606   // going to use it in the sampler thread. Using GetThreadHandle() will
    607   // not work in this case. We're using OpenThread because DuplicateHandle
    608   // for some reason doesn't work in Chrome's sandbox.
    609   PlatformData() : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
    610                                                THREAD_SUSPEND_RESUME |
    611                                                THREAD_QUERY_INFORMATION,
    612                                                false,
    613                                                GetCurrentThreadId())) {}
    614 
    615   ~PlatformData() {
    616     if (profiled_thread_ != NULL) {
    617       CloseHandle(profiled_thread_);
    618       profiled_thread_ = NULL;
    619     }
    620   }
    621 
    622   HANDLE profiled_thread() { return profiled_thread_; }
    623 
    624  private:
    625   HANDLE profiled_thread_;
    626 };
    627 
    628 
    629 class SamplerThread : public Thread {
    630  public:
    631   static const int kSamplerThreadStackSize = 64 * KB;
    632 
    633   explicit SamplerThread(int interval)
    634       : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)),
    635         interval_(interval) {}
    636 
    637   static void AddActiveSampler(Sampler* sampler) {
    638     ScopedLock lock(mutex_.Pointer());
    639     SamplerRegistry::AddActiveSampler(sampler);
    640     if (instance_ == NULL) {
    641       instance_ = new SamplerThread(sampler->interval());
    642       instance_->Start();
    643     } else {
    644       ASSERT(instance_->interval_ == sampler->interval());
    645     }
    646   }
    647 
    648   static void RemoveActiveSampler(Sampler* sampler) {
    649     ScopedLock lock(mutex_.Pointer());
    650     SamplerRegistry::RemoveActiveSampler(sampler);
    651     if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
    652       RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
    653       delete instance_;
    654       instance_ = NULL;
    655     }
    656   }
    657 
    658   // Implement Thread::Run().
    659   virtual void Run() {
    660     SamplerRegistry::State state;
    661     while ((state = SamplerRegistry::GetState()) !=
    662            SamplerRegistry::HAS_NO_SAMPLERS) {
    663       bool cpu_profiling_enabled =
    664           (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
    665       bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
    666       // When CPU profiling is enabled both JavaScript and C++ code is
    667       // profiled. We must not suspend.
    668       if (!cpu_profiling_enabled) {
    669         if (rate_limiter_.SuspendIfNecessary()) continue;
    670       }
    671       if (cpu_profiling_enabled) {
    672         if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
    673           return;
    674         }
    675       }
    676       if (runtime_profiler_enabled) {
    677         if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
    678           return;
    679         }
    680       }
    681       OS::Sleep(interval_);
    682     }
    683   }
    684 
    685   static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) {
    686     if (!sampler->isolate()->IsInitialized()) return;
    687     if (!sampler->IsProfiling()) return;
    688     SamplerThread* sampler_thread =
    689         reinterpret_cast<SamplerThread*>(raw_sampler_thread);
    690     sampler_thread->SampleContext(sampler);
    691   }
    692 
    693   static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
    694     if (!sampler->isolate()->IsInitialized()) return;
    695     sampler->isolate()->runtime_profiler()->NotifyTick();
    696   }
    697 
    698   void SampleContext(Sampler* sampler) {
    699     HANDLE profiled_thread = sampler->platform_data()->profiled_thread();
    700     if (profiled_thread == NULL) return;
    701 
    702     // Context used for sampling the register state of the profiled thread.
    703     CONTEXT context;
    704     memset(&context, 0, sizeof(context));
    705 
    706     TickSample sample_obj;
    707     TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate());
    708     if (sample == NULL) sample = &sample_obj;
    709 
    710     static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
    711     if (SuspendThread(profiled_thread) == kSuspendFailed) return;
    712     sample->state = sampler->isolate()->current_vm_state();
    713 
    714     context.ContextFlags = CONTEXT_FULL;
    715     if (GetThreadContext(profiled_thread, &context) != 0) {
    716 #if V8_HOST_ARCH_X64
    717       sample->pc = reinterpret_cast<Address>(context.Rip);
    718       sample->sp = reinterpret_cast<Address>(context.Rsp);
    719       sample->fp = reinterpret_cast<Address>(context.Rbp);
    720 #else
    721       sample->pc = reinterpret_cast<Address>(context.Eip);
    722       sample->sp = reinterpret_cast<Address>(context.Esp);
    723       sample->fp = reinterpret_cast<Address>(context.Ebp);
    724 #endif
    725       sampler->SampleStack(sample);
    726       sampler->Tick(sample);
    727     }
    728     ResumeThread(profiled_thread);
    729   }
    730 
    731   const int interval_;
    732   RuntimeProfilerRateLimiter rate_limiter_;
    733 
    734   // Protects the process wide state below.
    735   static LazyMutex mutex_;
    736   static SamplerThread* instance_;
    737 
    738  private:
    739   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
    740 };
    741 
    742 
    743 LazyMutex SamplerThread::mutex_ = LAZY_MUTEX_INITIALIZER;
    744 SamplerThread* SamplerThread::instance_ = NULL;
    745 
    746 
    747 Sampler::Sampler(Isolate* isolate, int interval)
    748     : isolate_(isolate),
    749       interval_(interval),
    750       profiling_(false),
    751       active_(false),
    752       samples_taken_(0) {
    753   data_ = new PlatformData;
    754 }
    755 
    756 
    757 Sampler::~Sampler() {
    758   ASSERT(!IsActive());
    759   delete data_;
    760 }
    761 
    762 
    763 void Sampler::Start() {
    764   ASSERT(!IsActive());
    765   SetActive(true);
    766   SamplerThread::AddActiveSampler(this);
    767 }
    768 
    769 
    770 void Sampler::Stop() {
    771   ASSERT(IsActive());
    772   SamplerThread::RemoveActiveSampler(this);
    773   SetActive(false);
    774 }
    775 
    776 
    777 } }  // namespace v8::internal
    778