[V8] Introduce a QML compilation mode
[profile/ivi/qtjsbackend.git] / src / 3rdparty / v8 / src / platform-macos.cc
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 MacOS goes here. For the POSIX comaptible parts
29 // the implementation is in platform-posix.cc.
30
31 #include <dlfcn.h>
32 #include <unistd.h>
33 #include <sys/mman.h>
34 #include <mach/mach_init.h>
35 #include <mach-o/dyld.h>
36 #include <mach-o/getsect.h>
37
38 #include <AvailabilityMacros.h>
39
40 #include <pthread.h>
41 #include <semaphore.h>
42 #include <signal.h>
43 #include <libkern/OSAtomic.h>
44 #include <mach/mach.h>
45 #include <mach/semaphore.h>
46 #include <mach/task.h>
47 #include <mach/vm_statistics.h>
48 #include <sys/time.h>
49 #include <sys/resource.h>
50 #include <sys/types.h>
51 #include <sys/sysctl.h>
52 #include <stdarg.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <errno.h>
56
57 #undef MAP_TYPE
58
59 #include "v8.h"
60
61 #include "platform-posix.h"
62 #include "platform.h"
63 #include "vm-state-inl.h"
64
65 // Manually define these here as weak imports, rather than including execinfo.h.
66 // This lets us launch on 10.4 which does not have these calls.
67 extern "C" {
68   extern int backtrace(void**, int) __attribute__((weak_import));
69   extern char** backtrace_symbols(void* const*, int)
70       __attribute__((weak_import));
71   extern void backtrace_symbols_fd(void* const*, int, int)
72       __attribute__((weak_import));
73 }
74
75
76 namespace v8 {
77 namespace internal {
78
79 // 0 is never a valid thread id on MacOSX since a pthread_t is
80 // a pointer.
81 static const pthread_t kNoThread = (pthread_t) 0;
82
83
84 double ceiling(double x) {
85   // Correct Mac OS X Leopard 'ceil' behavior.
86   if (-1.0 < x && x < 0.0) {
87     return -0.0;
88   } else {
89     return ceil(x);
90   }
91 }
92
93
94 static Mutex* limit_mutex = NULL;
95
96
97 void OS::PostSetUp() {
98   POSIXPostSetUp();
99 }
100
101
102 // We keep the lowest and highest addresses mapped as a quick way of
103 // determining that pointers are outside the heap (used mostly in assertions
104 // and verification).  The estimate is conservative, i.e., not all addresses in
105 // 'allocated' space are actually allocated to our heap.  The range is
106 // [lowest, highest), inclusive on the low and and exclusive on the high end.
107 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
108 static void* highest_ever_allocated = reinterpret_cast<void*>(0);
109
110
111 static void UpdateAllocatedSpaceLimits(void* address, int size) {
112   ASSERT(limit_mutex != NULL);
113   ScopedLock lock(limit_mutex);
114
115   lowest_ever_allocated = Min(lowest_ever_allocated, address);
116   highest_ever_allocated =
117       Max(highest_ever_allocated,
118           reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
119 }
120
121
122 bool OS::IsOutsideAllocatedSpace(void* address) {
123   return address < lowest_ever_allocated || address >= highest_ever_allocated;
124 }
125
126
127 size_t OS::AllocateAlignment() {
128   return getpagesize();
129 }
130
131
132 // Constants used for mmap.
133 // kMmapFd is used to pass vm_alloc flags to tag the region with the user
134 // defined tag 255 This helps identify V8-allocated regions in memory analysis
135 // tools like vmmap(1).
136 static const int kMmapFd = VM_MAKE_TAG(255);
137 static const off_t kMmapFdOffset = 0;
138
139
140 void* OS::Allocate(const size_t requested,
141                    size_t* allocated,
142                    bool is_executable) {
143   const size_t msize = RoundUp(requested, getpagesize());
144   int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
145   void* mbase = mmap(OS::GetRandomMmapAddr(),
146                      msize,
147                      prot,
148                      MAP_PRIVATE | MAP_ANON,
149                      kMmapFd,
150                      kMmapFdOffset);
151   if (mbase == MAP_FAILED) {
152     LOG(Isolate::Current(), StringEvent("OS::Allocate", "mmap failed"));
153     return NULL;
154   }
155   *allocated = msize;
156   UpdateAllocatedSpaceLimits(mbase, msize);
157   return mbase;
158 }
159
160
161 void OS::Free(void* address, const size_t size) {
162   // TODO(1240712): munmap has a return value which is ignored here.
163   int result = munmap(address, size);
164   USE(result);
165   ASSERT(result == 0);
166 }
167
168
169 void OS::Sleep(int milliseconds) {
170   usleep(1000 * milliseconds);
171 }
172
173
174 void OS::Abort() {
175   // Redirect to std abort to signal abnormal program termination
176   abort();
177 }
178
179
180 void OS::DebugBreak() {
181   asm("int $3");
182 }
183
184
185 class PosixMemoryMappedFile : public OS::MemoryMappedFile {
186  public:
187   PosixMemoryMappedFile(FILE* file, void* memory, int size)
188     : file_(file), memory_(memory), size_(size) { }
189   virtual ~PosixMemoryMappedFile();
190   virtual void* memory() { return memory_; }
191   virtual int size() { return size_; }
192  private:
193   FILE* file_;
194   void* memory_;
195   int size_;
196 };
197
198
199 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
200   FILE* file = fopen(name, "r+");
201   if (file == NULL) return NULL;
202
203   fseek(file, 0, SEEK_END);
204   int size = ftell(file);
205
206   void* memory =
207       mmap(OS::GetRandomMmapAddr(),
208            size,
209            PROT_READ | PROT_WRITE,
210            MAP_SHARED,
211            fileno(file),
212            0);
213   return new PosixMemoryMappedFile(file, memory, size);
214 }
215
216
217 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
218     void* initial) {
219   FILE* file = fopen(name, "w+");
220   if (file == NULL) return NULL;
221   int result = fwrite(initial, size, 1, file);
222   if (result < 1) {
223     fclose(file);
224     return NULL;
225   }
226   void* memory =
227       mmap(OS::GetRandomMmapAddr(),
228           size,
229           PROT_READ | PROT_WRITE,
230           MAP_SHARED,
231           fileno(file),
232           0);
233   return new PosixMemoryMappedFile(file, memory, size);
234 }
235
236
237 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
238   if (memory_) OS::Free(memory_, size_);
239   fclose(file_);
240 }
241
242
243 void OS::LogSharedLibraryAddresses() {
244   unsigned int images_count = _dyld_image_count();
245   for (unsigned int i = 0; i < images_count; ++i) {
246     const mach_header* header = _dyld_get_image_header(i);
247     if (header == NULL) continue;
248 #if V8_HOST_ARCH_X64
249     uint64_t size;
250     char* code_ptr = getsectdatafromheader_64(
251         reinterpret_cast<const mach_header_64*>(header),
252         SEG_TEXT,
253         SECT_TEXT,
254         &size);
255 #else
256     unsigned int size;
257     char* code_ptr = getsectdatafromheader(header, SEG_TEXT, SECT_TEXT, &size);
258 #endif
259     if (code_ptr == NULL) continue;
260     const uintptr_t slide = _dyld_get_image_vmaddr_slide(i);
261     const uintptr_t start = reinterpret_cast<uintptr_t>(code_ptr) + slide;
262     LOG(Isolate::Current(),
263         SharedLibraryEvent(_dyld_get_image_name(i), start, start + size));
264   }
265 }
266
267
268 void OS::SignalCodeMovingGC() {
269 }
270
271
272 uint64_t OS::CpuFeaturesImpliedByPlatform() {
273   // MacOSX requires all these to install so we can assume they are present.
274   // These constants are defined by the CPUid instructions.
275   const uint64_t one = 1;
276   return (one << SSE2) | (one << CMOV) | (one << RDTSC) | (one << CPUID);
277 }
278
279
280 int OS::ActivationFrameAlignment() {
281   // OS X activation frames must be 16 byte-aligned; see "Mac OS X ABI
282   // Function Call Guide".
283   return 16;
284 }
285
286
287 void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
288   OSMemoryBarrier();
289   *ptr = value;
290 }
291
292
293 const char* OS::LocalTimezone(double time) {
294   if (isnan(time)) return "";
295   time_t tv = static_cast<time_t>(floor(time/msPerSecond));
296   struct tm* t = localtime(&tv);
297   if (NULL == t) return "";
298   return t->tm_zone;
299 }
300
301
302 double OS::LocalTimeOffset() {
303   time_t tv = time(NULL);
304   struct tm* t = localtime(&tv);
305   // tm_gmtoff includes any daylight savings offset, so subtract it.
306   return static_cast<double>(t->tm_gmtoff * msPerSecond -
307                              (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
308 }
309
310
311 int OS::StackWalk(Vector<StackFrame> frames) {
312   // If weak link to execinfo lib has failed, ie because we are on 10.4, abort.
313   if (backtrace == NULL)
314     return 0;
315
316   int frames_size = frames.length();
317   ScopedVector<void*> addresses(frames_size);
318
319   int frames_count = backtrace(addresses.start(), frames_size);
320
321   char** symbols = backtrace_symbols(addresses.start(), frames_count);
322   if (symbols == NULL) {
323     return kStackWalkError;
324   }
325
326   for (int i = 0; i < frames_count; i++) {
327     frames[i].address = addresses[i];
328     // Format a text representation of the frame based on the information
329     // available.
330     SNPrintF(MutableCStrVector(frames[i].text,
331                                kStackWalkMaxTextLen),
332              "%s",
333              symbols[i]);
334     // Make sure line termination is in place.
335     frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
336   }
337
338   free(symbols);
339
340   return frames_count;
341 }
342
343
344 VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
345
346
347 VirtualMemory::VirtualMemory(size_t size)
348     : address_(ReserveRegion(size)), size_(size) { }
349
350
351 VirtualMemory::VirtualMemory(size_t size, size_t alignment)
352     : address_(NULL), size_(0) {
353   ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
354   size_t request_size = RoundUp(size + alignment,
355                                 static_cast<intptr_t>(OS::AllocateAlignment()));
356   void* reservation = mmap(OS::GetRandomMmapAddr(),
357                            request_size,
358                            PROT_NONE,
359                            MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
360                            kMmapFd,
361                            kMmapFdOffset);
362   if (reservation == MAP_FAILED) return;
363
364   Address base = static_cast<Address>(reservation);
365   Address aligned_base = RoundUp(base, alignment);
366   ASSERT_LE(base, aligned_base);
367
368   // Unmap extra memory reserved before and after the desired block.
369   if (aligned_base != base) {
370     size_t prefix_size = static_cast<size_t>(aligned_base - base);
371     OS::Free(base, prefix_size);
372     request_size -= prefix_size;
373   }
374
375   size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
376   ASSERT_LE(aligned_size, request_size);
377
378   if (aligned_size != request_size) {
379     size_t suffix_size = request_size - aligned_size;
380     OS::Free(aligned_base + aligned_size, suffix_size);
381     request_size -= suffix_size;
382   }
383
384   ASSERT(aligned_size == request_size);
385
386   address_ = static_cast<void*>(aligned_base);
387   size_ = aligned_size;
388 }
389
390
391 VirtualMemory::~VirtualMemory() {
392   if (IsReserved()) {
393     bool result = ReleaseRegion(address(), size());
394     ASSERT(result);
395     USE(result);
396   }
397 }
398
399
400 void VirtualMemory::Reset() {
401   address_ = NULL;
402   size_ = 0;
403 }
404
405
406 void* VirtualMemory::ReserveRegion(size_t size) {
407   void* result = mmap(OS::GetRandomMmapAddr(),
408                       size,
409                       PROT_NONE,
410                       MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
411                       kMmapFd,
412                       kMmapFdOffset);
413
414   if (result == MAP_FAILED) return NULL;
415
416   return result;
417 }
418
419
420 bool VirtualMemory::IsReserved() {
421   return address_ != NULL;
422 }
423
424
425 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
426   return CommitRegion(address, size, is_executable);
427 }
428
429
430 bool VirtualMemory::Guard(void* address) {
431   OS::Guard(address, OS::CommitPageSize());
432   return true;
433 }
434
435
436 bool VirtualMemory::CommitRegion(void* address,
437                                  size_t size,
438                                  bool is_executable) {
439   int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
440   if (MAP_FAILED == mmap(address,
441                          size,
442                          prot,
443                          MAP_PRIVATE | MAP_ANON | MAP_FIXED,
444                          kMmapFd,
445                          kMmapFdOffset)) {
446     return false;
447   }
448
449   UpdateAllocatedSpaceLimits(address, size);
450   return true;
451 }
452
453
454 bool VirtualMemory::Uncommit(void* address, size_t size) {
455   return UncommitRegion(address, size);
456 }
457
458
459 bool VirtualMemory::UncommitRegion(void* address, size_t size) {
460   return mmap(address,
461               size,
462               PROT_NONE,
463               MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
464               kMmapFd,
465               kMmapFdOffset) != MAP_FAILED;
466 }
467
468
469 bool VirtualMemory::ReleaseRegion(void* address, size_t size) {
470   return munmap(address, size) == 0;
471 }
472
473
474 class Thread::PlatformData : public Malloced {
475  public:
476   PlatformData() : thread_(kNoThread) {}
477   pthread_t thread_;  // Thread handle for pthread.
478 };
479
480
481 Thread::Thread(const Options& options)
482     : data_(new PlatformData),
483       stack_size_(options.stack_size()) {
484   set_name(options.name());
485 }
486
487
488 Thread::~Thread() {
489   delete data_;
490 }
491
492
493 static void SetThreadName(const char* name) {
494   // pthread_setname_np is only available in 10.6 or later, so test
495   // for it at runtime.
496   int (*dynamic_pthread_setname_np)(const char*);
497   *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
498     dlsym(RTLD_DEFAULT, "pthread_setname_np");
499   if (!dynamic_pthread_setname_np)
500     return;
501
502   // Mac OS X does not expose the length limit of the name, so hardcode it.
503   static const int kMaxNameLength = 63;
504   USE(kMaxNameLength);
505   ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
506   dynamic_pthread_setname_np(name);
507 }
508
509
510 static void* ThreadEntry(void* arg) {
511   Thread* thread = reinterpret_cast<Thread*>(arg);
512   // This is also initialized by the first argument to pthread_create() but we
513   // don't know which thread will run first (the original thread or the new
514   // one) so we initialize it here too.
515   thread->data()->thread_ = pthread_self();
516   SetThreadName(thread->name());
517   ASSERT(thread->data()->thread_ != kNoThread);
518   thread->Run();
519   return NULL;
520 }
521
522
523 void Thread::set_name(const char* name) {
524   strncpy(name_, name, sizeof(name_));
525   name_[sizeof(name_) - 1] = '\0';
526 }
527
528
529 void Thread::Start() {
530   pthread_attr_t* attr_ptr = NULL;
531   pthread_attr_t attr;
532   if (stack_size_ > 0) {
533     pthread_attr_init(&attr);
534     pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
535     attr_ptr = &attr;
536   }
537   pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
538   ASSERT(data_->thread_ != kNoThread);
539 }
540
541
542 void Thread::Join() {
543   pthread_join(data_->thread_, NULL);
544 }
545
546
547 #ifdef V8_FAST_TLS_SUPPORTED
548
549 static Atomic32 tls_base_offset_initialized = 0;
550 intptr_t kMacTlsBaseOffset = 0;
551
552 // It's safe to do the initialization more that once, but it has to be
553 // done at least once.
554 static void InitializeTlsBaseOffset() {
555   const size_t kBufferSize = 128;
556   char buffer[kBufferSize];
557   size_t buffer_size = kBufferSize;
558   int ctl_name[] = { CTL_KERN , KERN_OSRELEASE };
559   if (sysctl(ctl_name, 2, buffer, &buffer_size, NULL, 0) != 0) {
560     V8_Fatal(__FILE__, __LINE__, "V8 failed to get kernel version");
561   }
562   // The buffer now contains a string of the form XX.YY.ZZ, where
563   // XX is the major kernel version component.
564   // Make sure the buffer is 0-terminated.
565   buffer[kBufferSize - 1] = '\0';
566   char* period_pos = strchr(buffer, '.');
567   *period_pos = '\0';
568   int kernel_version_major =
569       static_cast<int>(strtol(buffer, NULL, 10));  // NOLINT
570   // The constants below are taken from pthreads.s from the XNU kernel
571   // sources archive at www.opensource.apple.com.
572   if (kernel_version_major < 11) {
573     // 8.x.x (Tiger), 9.x.x (Leopard), 10.x.x (Snow Leopard) have the
574     // same offsets.
575 #if defined(V8_HOST_ARCH_IA32)
576     kMacTlsBaseOffset = 0x48;
577 #else
578     kMacTlsBaseOffset = 0x60;
579 #endif
580   } else {
581     // 11.x.x (Lion) changed the offset.
582     kMacTlsBaseOffset = 0;
583   }
584
585   Release_Store(&tls_base_offset_initialized, 1);
586 }
587
588 static void CheckFastTls(Thread::LocalStorageKey key) {
589   void* expected = reinterpret_cast<void*>(0x1234CAFE);
590   Thread::SetThreadLocal(key, expected);
591   void* actual = Thread::GetExistingThreadLocal(key);
592   if (expected != actual) {
593     V8_Fatal(__FILE__, __LINE__,
594              "V8 failed to initialize fast TLS on current kernel");
595   }
596   Thread::SetThreadLocal(key, NULL);
597 }
598
599 #endif  // V8_FAST_TLS_SUPPORTED
600
601
602 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
603 #ifdef V8_FAST_TLS_SUPPORTED
604   bool check_fast_tls = false;
605   if (tls_base_offset_initialized == 0) {
606     check_fast_tls = true;
607     InitializeTlsBaseOffset();
608   }
609 #endif
610   pthread_key_t key;
611   int result = pthread_key_create(&key, NULL);
612   USE(result);
613   ASSERT(result == 0);
614   LocalStorageKey typed_key = static_cast<LocalStorageKey>(key);
615 #ifdef V8_FAST_TLS_SUPPORTED
616   // If we just initialized fast TLS support, make sure it works.
617   if (check_fast_tls) CheckFastTls(typed_key);
618 #endif
619   return typed_key;
620 }
621
622
623 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
624   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
625   int result = pthread_key_delete(pthread_key);
626   USE(result);
627   ASSERT(result == 0);
628 }
629
630
631 void* Thread::GetThreadLocal(LocalStorageKey key) {
632   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
633   return pthread_getspecific(pthread_key);
634 }
635
636
637 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
638   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
639   pthread_setspecific(pthread_key, value);
640 }
641
642
643 void Thread::YieldCPU() {
644   sched_yield();
645 }
646
647
648 class MacOSMutex : public Mutex {
649  public:
650   MacOSMutex() {
651     pthread_mutexattr_t attr;
652     pthread_mutexattr_init(&attr);
653     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
654     pthread_mutex_init(&mutex_, &attr);
655   }
656
657   virtual ~MacOSMutex() { pthread_mutex_destroy(&mutex_); }
658
659   virtual int Lock() { return pthread_mutex_lock(&mutex_); }
660   virtual int Unlock() { return pthread_mutex_unlock(&mutex_); }
661
662   virtual bool TryLock() {
663     int result = pthread_mutex_trylock(&mutex_);
664     // Return false if the lock is busy and locking failed.
665     if (result == EBUSY) {
666       return false;
667     }
668     ASSERT(result == 0);  // Verify no other errors.
669     return true;
670   }
671
672  private:
673   pthread_mutex_t mutex_;
674 };
675
676
677 Mutex* OS::CreateMutex() {
678   return new MacOSMutex();
679 }
680
681
682 class MacOSSemaphore : public Semaphore {
683  public:
684   explicit MacOSSemaphore(int count) {
685     semaphore_create(mach_task_self(), &semaphore_, SYNC_POLICY_FIFO, count);
686   }
687
688   ~MacOSSemaphore() {
689     semaphore_destroy(mach_task_self(), semaphore_);
690   }
691
692   // The MacOS mach semaphore documentation claims it does not have spurious
693   // wakeups, the way pthreads semaphores do.  So the code from the linux
694   // platform is not needed here.
695   void Wait() { semaphore_wait(semaphore_); }
696
697   bool Wait(int timeout);
698
699   void Signal() { semaphore_signal(semaphore_); }
700
701  private:
702   semaphore_t semaphore_;
703 };
704
705
706 bool MacOSSemaphore::Wait(int timeout) {
707   mach_timespec_t ts;
708   ts.tv_sec = timeout / 1000000;
709   ts.tv_nsec = (timeout % 1000000) * 1000;
710   return semaphore_timedwait(semaphore_, ts) != KERN_OPERATION_TIMED_OUT;
711 }
712
713
714 Semaphore* OS::CreateSemaphore(int count) {
715   return new MacOSSemaphore(count);
716 }
717
718
719 class Sampler::PlatformData : public Malloced {
720  public:
721   PlatformData() : profiled_thread_(mach_thread_self()) {}
722
723   ~PlatformData() {
724     // Deallocate Mach port for thread.
725     mach_port_deallocate(mach_task_self(), profiled_thread_);
726   }
727
728   thread_act_t profiled_thread() { return profiled_thread_; }
729
730  private:
731   // Note: for profiled_thread_ Mach primitives are used instead of PThread's
732   // because the latter doesn't provide thread manipulation primitives required.
733   // For details, consult "Mac OS X Internals" book, Section 7.3.
734   thread_act_t profiled_thread_;
735 };
736
737
738 class SamplerThread : public Thread {
739  public:
740   static const int kSamplerThreadStackSize = 64 * KB;
741
742   explicit SamplerThread(int interval)
743       : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)),
744         interval_(interval) {}
745
746   static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
747   static void TearDown() { delete mutex_; }
748
749   static void AddActiveSampler(Sampler* sampler) {
750     ScopedLock lock(mutex_);
751     SamplerRegistry::AddActiveSampler(sampler);
752     if (instance_ == NULL) {
753       instance_ = new SamplerThread(sampler->interval());
754       instance_->Start();
755     } else {
756       ASSERT(instance_->interval_ == sampler->interval());
757     }
758   }
759
760   static void RemoveActiveSampler(Sampler* sampler) {
761     ScopedLock lock(mutex_);
762     SamplerRegistry::RemoveActiveSampler(sampler);
763     if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
764       RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
765       delete instance_;
766       instance_ = NULL;
767     }
768   }
769
770   // Implement Thread::Run().
771   virtual void Run() {
772     SamplerRegistry::State state;
773     while ((state = SamplerRegistry::GetState()) !=
774            SamplerRegistry::HAS_NO_SAMPLERS) {
775       bool cpu_profiling_enabled =
776           (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
777       bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
778       // When CPU profiling is enabled both JavaScript and C++ code is
779       // profiled. We must not suspend.
780       if (!cpu_profiling_enabled) {
781         if (rate_limiter_.SuspendIfNecessary()) continue;
782       }
783       if (cpu_profiling_enabled) {
784         if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
785           return;
786         }
787       }
788       if (runtime_profiler_enabled) {
789         if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
790           return;
791         }
792       }
793       OS::Sleep(interval_);
794     }
795   }
796
797   static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) {
798     if (!sampler->isolate()->IsInitialized()) return;
799     if (!sampler->IsProfiling()) return;
800     SamplerThread* sampler_thread =
801         reinterpret_cast<SamplerThread*>(raw_sampler_thread);
802     sampler_thread->SampleContext(sampler);
803   }
804
805   static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
806     if (!sampler->isolate()->IsInitialized()) return;
807     sampler->isolate()->runtime_profiler()->NotifyTick();
808   }
809
810   void SampleContext(Sampler* sampler) {
811     thread_act_t profiled_thread = sampler->platform_data()->profiled_thread();
812     TickSample sample_obj;
813     TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate());
814     if (sample == NULL) sample = &sample_obj;
815
816     if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
817
818 #if V8_HOST_ARCH_X64
819     thread_state_flavor_t flavor = x86_THREAD_STATE64;
820     x86_thread_state64_t state;
821     mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
822 #if __DARWIN_UNIX03
823 #define REGISTER_FIELD(name) __r ## name
824 #else
825 #define REGISTER_FIELD(name) r ## name
826 #endif  // __DARWIN_UNIX03
827 #elif V8_HOST_ARCH_IA32
828     thread_state_flavor_t flavor = i386_THREAD_STATE;
829     i386_thread_state_t state;
830     mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
831 #if __DARWIN_UNIX03
832 #define REGISTER_FIELD(name) __e ## name
833 #else
834 #define REGISTER_FIELD(name) e ## name
835 #endif  // __DARWIN_UNIX03
836 #else
837 #error Unsupported Mac OS X host architecture.
838 #endif  // V8_HOST_ARCH
839
840     if (thread_get_state(profiled_thread,
841                          flavor,
842                          reinterpret_cast<natural_t*>(&state),
843                          &count) == KERN_SUCCESS) {
844       sample->state = sampler->isolate()->current_vm_state();
845       sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
846       sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
847       sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
848       sampler->SampleStack(sample);
849       sampler->Tick(sample);
850     }
851     thread_resume(profiled_thread);
852   }
853
854   const int interval_;
855   RuntimeProfilerRateLimiter rate_limiter_;
856
857   // Protects the process wide state below.
858   static Mutex* mutex_;
859   static SamplerThread* instance_;
860
861  private:
862   DISALLOW_COPY_AND_ASSIGN(SamplerThread);
863 };
864
865 #undef REGISTER_FIELD
866
867
868 Mutex* SamplerThread::mutex_ = NULL;
869 SamplerThread* SamplerThread::instance_ = NULL;
870
871
872 void OS::SetUp() {
873   // Seed the random number generator. We preserve microsecond resolution.
874   uint64_t seed = Ticks() ^ (getpid() << 16);
875   srandom(static_cast<unsigned int>(seed));
876   limit_mutex = CreateMutex();
877   SamplerThread::SetUp();
878 }
879
880
881 void OS::TearDown() {
882   SamplerThread::TearDown();
883   delete limit_mutex;
884 }
885
886
887 Sampler::Sampler(Isolate* isolate, int interval)
888     : isolate_(isolate),
889       interval_(interval),
890       profiling_(false),
891       active_(false),
892       samples_taken_(0) {
893   data_ = new PlatformData;
894 }
895
896
897 Sampler::~Sampler() {
898   ASSERT(!IsActive());
899   delete data_;
900 }
901
902
903 void Sampler::Start() {
904   ASSERT(!IsActive());
905   SetActive(true);
906   SamplerThread::AddActiveSampler(this);
907 }
908
909
910 void Sampler::Stop() {
911   ASSERT(IsActive());
912   SamplerThread::RemoveActiveSampler(this);
913   SetActive(false);
914 }
915
916
917 } }  // namespace v8::internal