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
50namespace v8 {
51namespace internal {
52
53// 0 is never a valid thread id
54static const pthread_t kNoThread = (pthread_t) 0;
55
56
57double ceiling(double x) {
58  return ceil(x);
59}
60
61
62static Mutex* limit_mutex = NULL;
63
64
65void 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
77void OS::PostSetUp() {
78  // Math functions depend on CPU features therefore they are initialized after
79  // CPU.
80  MathSetup();
81}
82
83uint64_t OS::CpuFeaturesImpliedByPlatform() {
84  return 0;  // Nothing special about Cygwin.
85}
86
87
88int 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
95void 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
101const 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
110double 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.
127static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
128static void* highest_ever_allocated = reinterpret_cast<void*>(0);
129
130
131static 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
142bool OS::IsOutsideAllocatedSpace(void* address) {
143  return address < lowest_ever_allocated || address >= highest_ever_allocated;
144}
145
146
147size_t OS::AllocateAlignment() {
148  return sysconf(_SC_PAGESIZE);
149}
150
151
152void* 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
168void 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
176void OS::ProtectCode(void* address, const size_t size) {
177  DWORD old_protect;
178  VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
179}
180
181
182void OS::Guard(void* address, const size_t size) {
183  DWORD oldprotect;
184  VirtualProtect(address, size, PAGE_READONLY | PAGE_GUARD, &oldprotect);
185}
186
187
188void OS::Sleep(int milliseconds) {
189  unsigned int ms = static_cast<unsigned int>(milliseconds);
190  usleep(1000 * ms);
191}
192
193
194void OS::Abort() {
195  // Redirect to std abort to signal abnormal program termination.
196  abort();
197}
198
199
200void OS::DebugBreak() {
201  asm("int $3");
202}
203
204
205class 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
219OS::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
232OS::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
247PosixMemoryMappedFile::~PosixMemoryMappedFile() {
248  if (memory_) munmap(memory_, size_);
249  fclose(file_);
250}
251
252
253void 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
313void OS::SignalCodeMovingGC() {
314  // Nothing to do on Cygwin.
315}
316
317
318int 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
330bool VirtualMemory::IsReserved() {
331  return address_ != NULL;
332}
333
334
335VirtualMemory::VirtualMemory(size_t size) {
336  address_ = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
337  size_ = size;
338}
339
340
341VirtualMemory::~VirtualMemory() {
342  if (IsReserved()) {
343    if (0 == VirtualFree(address(), 0, MEM_RELEASE)) address_ = NULL;
344  }
345}
346
347
348bool 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
359bool VirtualMemory::Uncommit(void* address, size_t size) {
360  ASSERT(IsReserved());
361  return VirtualFree(address, size, MEM_DECOMMIT) != false;
362}
363
364
365bool 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
376class Thread::PlatformData : public Malloced {
377 public:
378  PlatformData() : thread_(kNoThread) {}
379  pthread_t thread_;  // Thread handle for pthread.
380};
381
382
383
384
385Thread::Thread(const Options& options)
386    : data_(new PlatformData()),
387      stack_size_(options.stack_size()) {
388  set_name(options.name());
389}
390
391
392Thread::~Thread() {
393  delete data_;
394}
395
396
397static 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
409void Thread::set_name(const char* name) {
410  strncpy(name_, name, sizeof(name_));
411  name_[sizeof(name_) - 1] = '\0';
412}
413
414
415void 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
428void Thread::Join() {
429  pthread_join(data_->thread_, NULL);
430}
431
432
433static 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
444static 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
452Thread::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
461void 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
469void* Thread::GetThreadLocal(LocalStorageKey key) {
470  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
471  return pthread_getspecific(pthread_key);
472}
473
474
475void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
476  pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
477  pthread_setspecific(pthread_key, value);
478}
479
480
481void Thread::YieldCPU() {
482  sched_yield();
483}
484
485
486class 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
527Mutex* OS::CreateMutex() {
528  return new CygwinMutex();
529}
530
531
532class 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
545void 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
562bool 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
592Semaphore* 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
602class 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
629class 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
743LazyMutex SamplerThread::mutex_ = LAZY_MUTEX_INITIALIZER;
744SamplerThread* SamplerThread::instance_ = NULL;
745
746
747Sampler::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
757Sampler::~Sampler() {
758  ASSERT(!IsActive());
759  delete data_;
760}
761
762
763void Sampler::Start() {
764  ASSERT(!IsActive());
765  SetActive(true);
766  SamplerThread::AddActiveSampler(this);
767}
768
769
770void Sampler::Stop() {
771  ASSERT(IsActive());
772  SamplerThread::RemoveActiveSampler(this);
773  SetActive(false);
774}
775
776
777} }  // namespace v8::internal
778