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