Updated V8 from git://github.com/v8/v8.git to 3e6ec7e018bbf2c63ef04b85ff688198ea204c04
[profile/ivi/qtjsbackend.git] / src / 3rdparty / v8 / src / platform-openbsd.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 OpenBSD and NetBSD goes here. For the POSIX
29 // comaptible parts the implementation is in platform-posix.cc.
30
31 #include <pthread.h>
32 #include <semaphore.h>
33 #include <signal.h>
34 #include <sys/time.h>
35 #include <sys/resource.h>
36 #include <sys/syscall.h>
37 #include <sys/types.h>
38 #include <stdlib.h>
39
40 #include <sys/types.h>  // mmap & munmap
41 #include <sys/mman.h>   // mmap & munmap
42 #include <sys/stat.h>   // open
43 #include <fcntl.h>      // open
44 #include <unistd.h>     // sysconf
45 #include <execinfo.h>   // backtrace, backtrace_symbols
46 #include <strings.h>    // index
47 #include <errno.h>
48 #include <stdarg.h>
49
50 #undef MAP_TYPE
51
52 #include "v8.h"
53
54 #include "platform-posix.h"
55 #include "platform.h"
56 #include "v8threads.h"
57 #include "vm-state-inl.h"
58
59
60 namespace v8 {
61 namespace internal {
62
63 // 0 is never a valid thread id on Linux and OpenBSD since tids and pids share a
64 // name space and pid 0 is reserved (see man 2 kill).
65 static const pthread_t kNoThread = (pthread_t) 0;
66
67
68 double ceiling(double x) {
69   return ceil(x);
70 }
71
72
73 static Mutex* limit_mutex = NULL;
74
75
76 static void* GetRandomMmapAddr() {
77   Isolate* isolate = Isolate::UncheckedCurrent();
78   // Note that the current isolate isn't set up in a call path via
79   // CpuFeatures::Probe. We don't care about randomization in this case because
80   // the code page is immediately freed.
81   if (isolate != NULL) {
82 #ifdef V8_TARGET_ARCH_X64
83     uint64_t rnd1 = V8::RandomPrivate(isolate);
84     uint64_t rnd2 = V8::RandomPrivate(isolate);
85     uint64_t raw_addr = (rnd1 << 32) ^ rnd2;
86     // Currently available CPUs have 48 bits of virtual addressing.  Truncate
87     // the hint address to 46 bits to give the kernel a fighting chance of
88     // fulfilling our placement request.
89     raw_addr &= V8_UINT64_C(0x3ffffffff000);
90 #else
91     uint32_t raw_addr = V8::RandomPrivate(isolate);
92     // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
93     // variety of ASLR modes (PAE kernel, NX compat mode, etc).
94     raw_addr &= 0x3ffff000;
95     raw_addr += 0x20000000;
96 #endif
97     return reinterpret_cast<void*>(raw_addr);
98   }
99   return NULL;
100 }
101
102
103 void OS::PostSetUp() {
104   POSIXPostSetUp();
105 }
106
107
108 uint64_t OS::CpuFeaturesImpliedByPlatform() {
109   return 0;
110 }
111
112
113 int OS::ActivationFrameAlignment() {
114   // With gcc 4.4 the tree vectorization optimizer can generate code
115   // that requires 16 byte alignment such as movdqa on x86.
116   return 16;
117 }
118
119
120 void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
121   __asm__ __volatile__("" : : : "memory");
122   // An x86 store acts as a release barrier.
123   *ptr = value;
124 }
125
126
127 const char* OS::LocalTimezone(double time) {
128   if (isnan(time)) return "";
129   time_t tv = static_cast<time_t>(floor(time/msPerSecond));
130   struct tm* t = localtime(&tv);
131   if (NULL == t) return "";
132   return t->tm_zone;
133 }
134
135
136 double OS::LocalTimeOffset() {
137   time_t tv = time(NULL);
138   struct tm* t = localtime(&tv);
139   // tm_gmtoff includes any daylight savings offset, so subtract it.
140   return static_cast<double>(t->tm_gmtoff * msPerSecond -
141                              (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
142 }
143
144
145 // We keep the lowest and highest addresses mapped as a quick way of
146 // determining that pointers are outside the heap (used mostly in assertions
147 // and verification).  The estimate is conservative, i.e., not all addresses in
148 // 'allocated' space are actually allocated to our heap.  The range is
149 // [lowest, highest), inclusive on the low and and exclusive on the high end.
150 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
151 static void* highest_ever_allocated = reinterpret_cast<void*>(0);
152
153
154 static void UpdateAllocatedSpaceLimits(void* address, int size) {
155   ASSERT(limit_mutex != NULL);
156   ScopedLock lock(limit_mutex);
157
158   lowest_ever_allocated = Min(lowest_ever_allocated, address);
159   highest_ever_allocated =
160       Max(highest_ever_allocated,
161           reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
162 }
163
164
165 bool OS::IsOutsideAllocatedSpace(void* address) {
166   return address < lowest_ever_allocated || address >= highest_ever_allocated;
167 }
168
169
170 size_t OS::AllocateAlignment() {
171   return sysconf(_SC_PAGESIZE);
172 }
173
174
175 void* OS::Allocate(const size_t requested,
176                    size_t* allocated,
177                    bool is_executable) {
178   const size_t msize = RoundUp(requested, AllocateAlignment());
179   int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
180   void* addr = GetRandomMmapAddr();
181   void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
182   if (mbase == MAP_FAILED) {
183     LOG(i::Isolate::Current(),
184         StringEvent("OS::Allocate", "mmap failed"));
185     return NULL;
186   }
187   *allocated = msize;
188   UpdateAllocatedSpaceLimits(mbase, msize);
189   return mbase;
190 }
191
192
193 void OS::Free(void* address, const size_t size) {
194   // TODO(1240712): munmap has a return value which is ignored here.
195   int result = munmap(address, size);
196   USE(result);
197   ASSERT(result == 0);
198 }
199
200
201 void OS::Sleep(int milliseconds) {
202   unsigned int ms = static_cast<unsigned int>(milliseconds);
203   usleep(1000 * ms);
204 }
205
206
207 void OS::Abort() {
208   // Redirect to std abort to signal abnormal program termination.
209   abort();
210 }
211
212
213 void OS::DebugBreak() {
214   asm("int $3");
215 }
216
217
218 class PosixMemoryMappedFile : public OS::MemoryMappedFile {
219  public:
220   PosixMemoryMappedFile(FILE* file, void* memory, int size)
221     : file_(file), memory_(memory), size_(size) { }
222   virtual ~PosixMemoryMappedFile();
223   virtual void* memory() { return memory_; }
224   virtual int size() { return size_; }
225  private:
226   FILE* file_;
227   void* memory_;
228   int size_;
229 };
230
231
232 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
233   FILE* file = fopen(name, "r+");
234   if (file == NULL) return NULL;
235
236   fseek(file, 0, SEEK_END);
237   int size = ftell(file);
238
239   void* memory =
240       mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
241   return new PosixMemoryMappedFile(file, memory, size);
242 }
243
244
245 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
246     void* initial) {
247   FILE* file = fopen(name, "w+");
248   if (file == NULL) return NULL;
249   int result = fwrite(initial, size, 1, file);
250   if (result < 1) {
251     fclose(file);
252     return NULL;
253   }
254   void* memory =
255       mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
256   return new PosixMemoryMappedFile(file, memory, size);
257 }
258
259
260 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
261   if (memory_) OS::Free(memory_, size_);
262   fclose(file_);
263 }
264
265
266 void OS::LogSharedLibraryAddresses() {
267   // This function assumes that the layout of the file is as follows:
268   // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
269   // If we encounter an unexpected situation we abort scanning further entries.
270   FILE* fp = fopen("/proc/self/maps", "r");
271   if (fp == NULL) return;
272
273   // Allocate enough room to be able to store a full file name.
274   const int kLibNameLen = FILENAME_MAX + 1;
275   char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
276
277   i::Isolate* isolate = ISOLATE;
278   // This loop will terminate once the scanning hits an EOF.
279   while (true) {
280     uintptr_t start, end;
281     char attr_r, attr_w, attr_x, attr_p;
282     // Parse the addresses and permission bits at the beginning of the line.
283     if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
284     if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
285
286     int c;
287     if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
288       // Found a read-only executable entry. Skip characters until we reach
289       // the beginning of the filename or the end of the line.
290       do {
291         c = getc(fp);
292       } while ((c != EOF) && (c != '\n') && (c != '/'));
293       if (c == EOF) break;  // EOF: Was unexpected, just exit.
294
295       // Process the filename if found.
296       if (c == '/') {
297         ungetc(c, fp);  // Push the '/' back into the stream to be read below.
298
299         // Read to the end of the line. Exit if the read fails.
300         if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
301
302         // Drop the newline character read by fgets. We do not need to check
303         // for a zero-length string because we know that we at least read the
304         // '/' character.
305         lib_name[strlen(lib_name) - 1] = '\0';
306       } else {
307         // No library name found, just record the raw address range.
308         snprintf(lib_name, kLibNameLen,
309                  "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
310       }
311       LOG(isolate, SharedLibraryEvent(lib_name, start, end));
312     } else {
313       // Entry not describing executable data. Skip to end of line to set up
314       // reading the next entry.
315       do {
316         c = getc(fp);
317       } while ((c != EOF) && (c != '\n'));
318       if (c == EOF) break;
319     }
320   }
321   free(lib_name);
322   fclose(fp);
323 }
324
325
326 static const char kGCFakeMmap[] = "/tmp/__v8_gc__";
327
328
329 void OS::SignalCodeMovingGC() {
330   // Support for ll_prof.py.
331   //
332   // The Linux profiler built into the kernel logs all mmap's with
333   // PROT_EXEC so that analysis tools can properly attribute ticks. We
334   // do a mmap with a name known by ll_prof.py and immediately munmap
335   // it. This injects a GC marker into the stream of events generated
336   // by the kernel and allows us to synchronize V8 code log and the
337   // kernel log.
338   int size = sysconf(_SC_PAGESIZE);
339   FILE* f = fopen(kGCFakeMmap, "w+");
340   void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE,
341                     fileno(f), 0);
342   ASSERT(addr != MAP_FAILED);
343   OS::Free(addr, size);
344   fclose(f);
345 }
346
347
348 int OS::StackWalk(Vector<OS::StackFrame> frames) {
349   // backtrace is a glibc extension.
350   int frames_size = frames.length();
351   ScopedVector<void*> addresses(frames_size);
352
353   int frames_count = backtrace(addresses.start(), frames_size);
354
355   char** symbols = backtrace_symbols(addresses.start(), frames_count);
356   if (symbols == NULL) {
357     return kStackWalkError;
358   }
359
360   for (int i = 0; i < frames_count; i++) {
361     frames[i].address = addresses[i];
362     // Format a text representation of the frame based on the information
363     // available.
364     SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
365              "%s",
366              symbols[i]);
367     // Make sure line termination is in place.
368     frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
369   }
370
371   free(symbols);
372
373   return frames_count;
374 }
375
376
377 // Constants used for mmap.
378 static const int kMmapFd = -1;
379 static const int kMmapFdOffset = 0;
380
381 VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
382
383 VirtualMemory::VirtualMemory(size_t size) {
384   address_ = ReserveRegion(size);
385   size_ = size;
386 }
387
388
389 VirtualMemory::VirtualMemory(size_t size, size_t alignment)
390     : address_(NULL), size_(0) {
391   ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
392   size_t request_size = RoundUp(size + alignment,
393                                 static_cast<intptr_t>(OS::AllocateAlignment()));
394   void* reservation = mmap(GetRandomMmapAddr(),
395                            request_size,
396                            PROT_NONE,
397                            MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
398                            kMmapFd,
399                            kMmapFdOffset);
400   if (reservation == MAP_FAILED) return;
401
402   Address base = static_cast<Address>(reservation);
403   Address aligned_base = RoundUp(base, alignment);
404   ASSERT_LE(base, aligned_base);
405
406   // Unmap extra memory reserved before and after the desired block.
407   if (aligned_base != base) {
408     size_t prefix_size = static_cast<size_t>(aligned_base - base);
409     OS::Free(base, prefix_size);
410     request_size -= prefix_size;
411   }
412
413   size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
414   ASSERT_LE(aligned_size, request_size);
415
416   if (aligned_size != request_size) {
417     size_t suffix_size = request_size - aligned_size;
418     OS::Free(aligned_base + aligned_size, suffix_size);
419     request_size -= suffix_size;
420   }
421
422   ASSERT(aligned_size == request_size);
423
424   address_ = static_cast<void*>(aligned_base);
425   size_ = aligned_size;
426 }
427
428
429 VirtualMemory::~VirtualMemory() {
430   if (IsReserved()) {
431     bool result = ReleaseRegion(address(), size());
432     ASSERT(result);
433     USE(result);
434   }
435 }
436
437
438 bool VirtualMemory::IsReserved() {
439   return address_ != NULL;
440 }
441
442
443 void VirtualMemory::Reset() {
444   address_ = NULL;
445   size_ = 0;
446 }
447
448
449 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
450   return CommitRegion(address, size, is_executable);
451 }
452
453
454 bool VirtualMemory::Uncommit(void* address, size_t size) {
455   return UncommitRegion(address, size);
456 }
457
458
459 bool VirtualMemory::Guard(void* address) {
460   OS::Guard(address, OS::CommitPageSize());
461   return true;
462 }
463
464
465 void* VirtualMemory::ReserveRegion(size_t size) {
466   void* result = mmap(GetRandomMmapAddr(),
467                       size,
468                       PROT_NONE,
469                       MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
470                       kMmapFd,
471                       kMmapFdOffset);
472
473   if (result == MAP_FAILED) return NULL;
474
475   return result;
476 }
477
478
479 bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
480   int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
481   if (MAP_FAILED == mmap(base,
482                          size,
483                          prot,
484                          MAP_PRIVATE | MAP_ANON | MAP_FIXED,
485                          kMmapFd,
486                          kMmapFdOffset)) {
487     return false;
488   }
489
490   UpdateAllocatedSpaceLimits(base, size);
491   return true;
492 }
493
494
495 bool VirtualMemory::UncommitRegion(void* base, size_t size) {
496   return mmap(base,
497               size,
498               PROT_NONE,
499               MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
500               kMmapFd,
501               kMmapFdOffset) != MAP_FAILED;
502 }
503
504
505 bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
506   return munmap(base, size) == 0;
507 }
508
509
510 class Thread::PlatformData : public Malloced {
511  public:
512   PlatformData() : thread_(kNoThread) {}
513
514   pthread_t thread_;  // Thread handle for pthread.
515 };
516
517 Thread::Thread(const Options& options)
518     : data_(new PlatformData()),
519       stack_size_(options.stack_size()) {
520   set_name(options.name());
521 }
522
523
524 Thread::~Thread() {
525   delete data_;
526 }
527
528
529 static void* ThreadEntry(void* arg) {
530   Thread* thread = reinterpret_cast<Thread*>(arg);
531   // This is also initialized by the first argument to pthread_create() but we
532   // don't know which thread will run first (the original thread or the new
533   // one) so we initialize it here too.
534 #ifdef PR_SET_NAME
535   prctl(PR_SET_NAME,
536         reinterpret_cast<unsigned long>(thread->name()),  // NOLINT
537         0, 0, 0);
538 #endif
539   thread->data()->thread_ = pthread_self();
540   ASSERT(thread->data()->thread_ != kNoThread);
541   thread->Run();
542   return NULL;
543 }
544
545
546 void Thread::set_name(const char* name) {
547   strncpy(name_, name, sizeof(name_));
548   name_[sizeof(name_) - 1] = '\0';
549 }
550
551
552 void Thread::Start() {
553   pthread_attr_t* attr_ptr = NULL;
554   pthread_attr_t attr;
555   if (stack_size_ > 0) {
556     pthread_attr_init(&attr);
557     pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
558     attr_ptr = &attr;
559   }
560   pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
561   ASSERT(data_->thread_ != kNoThread);
562 }
563
564
565 void Thread::Join() {
566   pthread_join(data_->thread_, NULL);
567 }
568
569
570 Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
571   pthread_key_t key;
572   int result = pthread_key_create(&key, NULL);
573   USE(result);
574   ASSERT(result == 0);
575   return static_cast<LocalStorageKey>(key);
576 }
577
578
579 void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
580   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
581   int result = pthread_key_delete(pthread_key);
582   USE(result);
583   ASSERT(result == 0);
584 }
585
586
587 void* Thread::GetThreadLocal(LocalStorageKey key) {
588   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
589   return pthread_getspecific(pthread_key);
590 }
591
592
593 void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
594   pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
595   pthread_setspecific(pthread_key, value);
596 }
597
598
599 void Thread::YieldCPU() {
600   sched_yield();
601 }
602
603
604 class OpenBSDMutex : public Mutex {
605  public:
606   OpenBSDMutex() {
607     pthread_mutexattr_t attrs;
608     int result = pthread_mutexattr_init(&attrs);
609     ASSERT(result == 0);
610     result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
611     ASSERT(result == 0);
612     result = pthread_mutex_init(&mutex_, &attrs);
613     ASSERT(result == 0);
614     USE(result);
615   }
616
617   virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); }
618
619   virtual int Lock() {
620     int result = pthread_mutex_lock(&mutex_);
621     return result;
622   }
623
624   virtual int Unlock() {
625     int result = pthread_mutex_unlock(&mutex_);
626     return result;
627   }
628
629   virtual bool TryLock() {
630     int result = pthread_mutex_trylock(&mutex_);
631     // Return false if the lock is busy and locking failed.
632     if (result == EBUSY) {
633       return false;
634     }
635     ASSERT(result == 0);  // Verify no other errors.
636     return true;
637   }
638
639  private:
640   pthread_mutex_t mutex_;   // Pthread mutex for POSIX platforms.
641 };
642
643
644 Mutex* OS::CreateMutex() {
645   return new OpenBSDMutex();
646 }
647
648
649 class OpenBSDSemaphore : public Semaphore {
650  public:
651   explicit OpenBSDSemaphore(int count) {  sem_init(&sem_, 0, count); }
652   virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
653
654   virtual void Wait();
655   virtual bool Wait(int timeout);
656   virtual void Signal() { sem_post(&sem_); }
657  private:
658   sem_t sem_;
659 };
660
661
662 void OpenBSDSemaphore::Wait() {
663   while (true) {
664     int result = sem_wait(&sem_);
665     if (result == 0) return;  // Successfully got semaphore.
666     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
667   }
668 }
669
670
671 #ifndef TIMEVAL_TO_TIMESPEC
672 #define TIMEVAL_TO_TIMESPEC(tv, ts) do {                            \
673     (ts)->tv_sec = (tv)->tv_sec;                                    \
674     (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
675 } while (false)
676 #endif
677
678
679 bool OpenBSDSemaphore::Wait(int timeout) {
680   const long kOneSecondMicros = 1000000;  // NOLINT
681
682   // Split timeout into second and nanosecond parts.
683   struct timeval delta;
684   delta.tv_usec = timeout % kOneSecondMicros;
685   delta.tv_sec = timeout / kOneSecondMicros;
686
687   struct timeval current_time;
688   // Get the current time.
689   if (gettimeofday(&current_time, NULL) == -1) {
690     return false;
691   }
692
693   // Calculate time for end of timeout.
694   struct timeval end_time;
695   timeradd(&current_time, &delta, &end_time);
696
697   struct timespec ts;
698   TIMEVAL_TO_TIMESPEC(&end_time, &ts);
699
700   int to = ts.tv_sec;
701
702   while (true) {
703     int result = sem_trywait(&sem_);
704     if (result == 0) return true;  // Successfully got semaphore.
705     if (!to) return false;  // Timeout.
706     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
707     usleep(ts.tv_nsec / 1000);
708     to--;
709   }
710 }
711
712 Semaphore* OS::CreateSemaphore(int count) {
713   return new OpenBSDSemaphore(count);
714 }
715
716
717 static pthread_t GetThreadID() {
718   return pthread_self();
719 }
720
721 static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
722   USE(info);
723   if (signal != SIGPROF) return;
724   Isolate* isolate = Isolate::UncheckedCurrent();
725   if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
726     // We require a fully initialized and entered isolate.
727     return;
728   }
729   if (v8::Locker::IsActive() &&
730       !isolate->thread_manager()->IsLockedByCurrentThread()) {
731     return;
732   }
733
734   Sampler* sampler = isolate->logger()->sampler();
735   if (sampler == NULL || !sampler->IsActive()) return;
736
737   TickSample sample_obj;
738   TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
739   if (sample == NULL) sample = &sample_obj;
740
741   // Extracting the sample from the context is extremely machine dependent.
742   sample->state = isolate->current_vm_state();
743   ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
744 #ifdef __NetBSD__
745   mcontext_t& mcontext = ucontext->uc_mcontext;
746 #if V8_HOST_ARCH_IA32
747   sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_EIP]);
748   sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_ESP]);
749   sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_EBP]);
750 #elif V8_HOST_ARCH_X64
751   sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_RIP]);
752   sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RSP]);
753   sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RBP]);
754 #endif  // V8_HOST_ARCH
755 #else  // OpenBSD
756 #if V8_HOST_ARCH_IA32
757   sample->pc = reinterpret_cast<Address>(ucontext->sc_eip);
758   sample->sp = reinterpret_cast<Address>(ucontext->sc_esp);
759   sample->fp = reinterpret_cast<Address>(ucontext->sc_ebp);
760 #elif V8_HOST_ARCH_X64
761   sample->pc = reinterpret_cast<Address>(ucontext->sc_rip);
762   sample->sp = reinterpret_cast<Address>(ucontext->sc_rsp);
763   sample->fp = reinterpret_cast<Address>(ucontext->sc_rbp);
764 #endif  // V8_HOST_ARCH
765 #endif  // __NetBSD__
766   sampler->SampleStack(sample);
767   sampler->Tick(sample);
768 }
769
770
771 class Sampler::PlatformData : public Malloced {
772  public:
773   PlatformData() : vm_tid_(GetThreadID()) {}
774
775   pthread_t vm_tid() const { return vm_tid_; }
776
777  private:
778   pthread_t vm_tid_;
779 };
780
781
782 class SignalSender : public Thread {
783  public:
784   enum SleepInterval {
785     HALF_INTERVAL,
786     FULL_INTERVAL
787   };
788
789   static const int kSignalSenderStackSize = 64 * KB;
790
791   explicit SignalSender(int interval)
792       : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
793         vm_tgid_(getpid()),
794         interval_(interval) {}
795
796   static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
797   static void TearDown() { delete mutex_; }
798
799   static void InstallSignalHandler() {
800     struct sigaction sa;
801     sa.sa_sigaction = ProfilerSignalHandler;
802     sigemptyset(&sa.sa_mask);
803     sa.sa_flags = SA_RESTART | SA_SIGINFO;
804     signal_handler_installed_ =
805         (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
806   }
807
808   static void RestoreSignalHandler() {
809     if (signal_handler_installed_) {
810       sigaction(SIGPROF, &old_signal_handler_, 0);
811       signal_handler_installed_ = false;
812     }
813   }
814
815   static void AddActiveSampler(Sampler* sampler) {
816     ScopedLock lock(mutex_);
817     SamplerRegistry::AddActiveSampler(sampler);
818     if (instance_ == NULL) {
819       // Start a thread that will send SIGPROF signal to VM threads,
820       // when CPU profiling will be enabled.
821       instance_ = new SignalSender(sampler->interval());
822       instance_->Start();
823     } else {
824       ASSERT(instance_->interval_ == sampler->interval());
825     }
826   }
827
828   static void RemoveActiveSampler(Sampler* sampler) {
829     ScopedLock lock(mutex_);
830     SamplerRegistry::RemoveActiveSampler(sampler);
831     if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
832       RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
833       delete instance_;
834       instance_ = NULL;
835       RestoreSignalHandler();
836     }
837   }
838
839   // Implement Thread::Run().
840   virtual void Run() {
841     SamplerRegistry::State state;
842     while ((state = SamplerRegistry::GetState()) !=
843            SamplerRegistry::HAS_NO_SAMPLERS) {
844       bool cpu_profiling_enabled =
845           (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
846       bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
847       if (cpu_profiling_enabled && !signal_handler_installed_) {
848         InstallSignalHandler();
849       } else if (!cpu_profiling_enabled && signal_handler_installed_) {
850         RestoreSignalHandler();
851       }
852       // When CPU profiling is enabled both JavaScript and C++ code is
853       // profiled. We must not suspend.
854       if (!cpu_profiling_enabled) {
855         if (rate_limiter_.SuspendIfNecessary()) continue;
856       }
857       if (cpu_profiling_enabled && runtime_profiler_enabled) {
858         if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
859           return;
860         }
861         Sleep(HALF_INTERVAL);
862         if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
863           return;
864         }
865         Sleep(HALF_INTERVAL);
866       } else {
867         if (cpu_profiling_enabled) {
868           if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
869                                                       this)) {
870             return;
871           }
872         }
873         if (runtime_profiler_enabled) {
874           if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
875                                                       NULL)) {
876             return;
877           }
878         }
879         Sleep(FULL_INTERVAL);
880       }
881     }
882   }
883
884   static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
885     if (!sampler->IsProfiling()) return;
886     SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
887     sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
888   }
889
890   static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
891     if (!sampler->isolate()->IsInitialized()) return;
892     sampler->isolate()->runtime_profiler()->NotifyTick();
893   }
894
895   void SendProfilingSignal(pthread_t tid) {
896     if (!signal_handler_installed_) return;
897     pthread_kill(tid, SIGPROF);
898   }
899
900   void Sleep(SleepInterval full_or_half) {
901     // Convert ms to us and subtract 100 us to compensate delays
902     // occuring during signal delivery.
903     useconds_t interval = interval_ * 1000 - 100;
904     if (full_or_half == HALF_INTERVAL) interval /= 2;
905     int result = usleep(interval);
906 #ifdef DEBUG
907     if (result != 0 && errno != EINTR) {
908       fprintf(stderr,
909               "SignalSender usleep error; interval = %u, errno = %d\n",
910               interval,
911               errno);
912       ASSERT(result == 0 || errno == EINTR);
913     }
914 #endif
915     USE(result);
916   }
917
918   const int vm_tgid_;
919   const int interval_;
920   RuntimeProfilerRateLimiter rate_limiter_;
921
922   // Protects the process wide state below.
923   static Mutex* mutex_;
924   static SignalSender* instance_;
925   static bool signal_handler_installed_;
926   static struct sigaction old_signal_handler_;
927
928  private:
929   DISALLOW_COPY_AND_ASSIGN(SignalSender);
930 };
931
932
933 Mutex* SignalSender::mutex_ = NULL;
934 SignalSender* SignalSender::instance_ = NULL;
935 struct sigaction SignalSender::old_signal_handler_;
936 bool SignalSender::signal_handler_installed_ = false;
937
938
939 void OS::SetUp() {
940   // Seed the random number generator. We preserve microsecond resolution.
941   uint64_t seed = Ticks() ^ (getpid() << 16);
942   srandom(static_cast<unsigned int>(seed));
943   limit_mutex = CreateMutex();
944   SignalSender::SetUp();
945 }
946
947
948 void OS::TearDown() {
949   SignalSender::TearDown();
950   delete limit_mutex;
951 }
952
953
954 Sampler::Sampler(Isolate* isolate, int interval)
955     : isolate_(isolate),
956       interval_(interval),
957       profiling_(false),
958       active_(false),
959       samples_taken_(0) {
960   data_ = new PlatformData;
961 }
962
963
964 Sampler::~Sampler() {
965   ASSERT(!IsActive());
966   delete data_;
967 }
968
969
970 void Sampler::Start() {
971   ASSERT(!IsActive());
972   SetActive(true);
973   SignalSender::AddActiveSampler(this);
974 }
975
976
977 void Sampler::Stop() {
978   ASSERT(IsActive());
979   SignalSender::RemoveActiveSampler(this);
980   SetActive(false);
981 }
982
983
984 } }  // namespace v8::internal