platform-solaris.cc revision 1e0659c275bb392c045087af4f6b0d7565cb3d77
1// Copyright 2006-2009 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 Solaris 10 goes here. For the POSIX comaptible
29// parts the implementation is in platform-posix.cc.
30
31#ifdef __sparc
32# error "V8 does not support the SPARC CPU architecture."
33#endif
34
35#include <sys/stack.h>  // for stack alignment
36#include <unistd.h>  // getpagesize(), usleep()
37#include <sys/mman.h>  // mmap()
38#include <ucontext.h>  // walkstack(), getcontext()
39#include <dlfcn.h>     // dladdr
40#include <pthread.h>
41#include <sched.h>  // for sched_yield
42#include <semaphore.h>
43#include <time.h>
44#include <sys/time.h>  // gettimeofday(), timeradd()
45#include <errno.h>
46#include <ieeefp.h>  // finite()
47#include <signal.h>  // sigemptyset(), etc
48#include <sys/kdi_regs.h>
49
50
51#undef MAP_TYPE
52
53#include "v8.h"
54
55#include "platform.h"
56#include "vm-state-inl.h"
57
58
59// It seems there is a bug in some Solaris distributions (experienced in
60// SunOS 5.10 Generic_141445-09) which make it difficult or impossible to
61// access signbit() despite the availability of other C99 math functions.
62#ifndef signbit
63// Test sign - usually defined in math.h
64int signbit(double x) {
65  // We need to take care of the special case of both positive and negative
66  // versions of zero.
67  if (x == 0) {
68    return fpclass(x) & FP_NZERO;
69  } else {
70    // This won't detect negative NaN but that should be okay since we don't
71    // assume that behavior.
72    return x < 0;
73  }
74}
75#endif  // signbit
76
77namespace v8 {
78namespace internal {
79
80
81// 0 is never a valid thread id on Solaris since the main thread is 1 and
82// subsequent have their ids incremented from there
83static const pthread_t kNoThread = (pthread_t) 0;
84
85
86double ceiling(double x) {
87  return ceil(x);
88}
89
90
91void OS::Setup() {
92  // Seed the random number generator.
93  // Convert the current time to a 64-bit integer first, before converting it
94  // to an unsigned. Going directly will cause an overflow and the seed to be
95  // set to all ones. The seed will be identical for different instances that
96  // call this setup code within the same millisecond.
97  uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
98  srandom(static_cast<unsigned int>(seed));
99}
100
101
102uint64_t OS::CpuFeaturesImpliedByPlatform() {
103  return 0;  // Solaris runs on a lot of things.
104}
105
106
107int OS::ActivationFrameAlignment() {
108  return STACK_ALIGN;
109}
110
111
112void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
113  __asm__ __volatile__("" : : : "memory");
114  *ptr = value;
115}
116
117
118const char* OS::LocalTimezone(double time) {
119  if (isnan(time)) return "";
120  time_t tv = static_cast<time_t>(floor(time/msPerSecond));
121  struct tm* t = localtime(&tv);
122  if (NULL == t) return "";
123  return tzname[0];  // The location of the timezone string on Solaris.
124}
125
126
127double OS::LocalTimeOffset() {
128  // On Solaris, struct tm does not contain a tm_gmtoff field.
129  time_t utc = time(NULL);
130  ASSERT(utc != -1);
131  struct tm* loc = localtime(&utc);
132  ASSERT(loc != NULL);
133  return static_cast<double>((mktime(loc) - utc) * msPerSecond);
134}
135
136
137// We keep the lowest and highest addresses mapped as a quick way of
138// determining that pointers are outside the heap (used mostly in assertions
139// and verification).  The estimate is conservative, ie, not all addresses in
140// 'allocated' space are actually allocated to our heap.  The range is
141// [lowest, highest), inclusive on the low and and exclusive on the high end.
142static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
143static void* highest_ever_allocated = reinterpret_cast<void*>(0);
144
145
146static void UpdateAllocatedSpaceLimits(void* address, int size) {
147  lowest_ever_allocated = Min(lowest_ever_allocated, address);
148  highest_ever_allocated =
149      Max(highest_ever_allocated,
150          reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
151}
152
153
154bool OS::IsOutsideAllocatedSpace(void* address) {
155  return address < lowest_ever_allocated || address >= highest_ever_allocated;
156}
157
158
159size_t OS::AllocateAlignment() {
160  return static_cast<size_t>(getpagesize());
161}
162
163
164void* OS::Allocate(const size_t requested,
165                   size_t* allocated,
166                   bool is_executable) {
167  const size_t msize = RoundUp(requested, getpagesize());
168  int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
169  void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
170
171  if (mbase == MAP_FAILED) {
172    LOG(StringEvent("OS::Allocate", "mmap failed"));
173    return NULL;
174  }
175  *allocated = msize;
176  UpdateAllocatedSpaceLimits(mbase, msize);
177  return mbase;
178}
179
180
181void OS::Free(void* address, const size_t size) {
182  // TODO(1240712): munmap has a return value which is ignored here.
183  int result = munmap(address, size);
184  USE(result);
185  ASSERT(result == 0);
186}
187
188
189#ifdef ENABLE_HEAP_PROTECTION
190
191void OS::Protect(void* address, size_t size) {
192  // TODO(1240712): mprotect has a return value which is ignored here.
193  mprotect(address, size, PROT_READ);
194}
195
196
197void OS::Unprotect(void* address, size_t size, bool is_executable) {
198  // TODO(1240712): mprotect has a return value which is ignored here.
199  int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
200  mprotect(address, size, prot);
201}
202
203#endif
204
205
206void OS::Sleep(int milliseconds) {
207  useconds_t ms = static_cast<useconds_t>(milliseconds);
208  usleep(1000 * ms);
209}
210
211
212void OS::Abort() {
213  // Redirect to std abort to signal abnormal program termination.
214  abort();
215}
216
217
218void OS::DebugBreak() {
219  asm("int $3");
220}
221
222
223class PosixMemoryMappedFile : public OS::MemoryMappedFile {
224 public:
225  PosixMemoryMappedFile(FILE* file, void* memory, int size)
226    : file_(file), memory_(memory), size_(size) { }
227  virtual ~PosixMemoryMappedFile();
228  virtual void* memory() { return memory_; }
229  virtual int size() { return size_; }
230 private:
231  FILE* file_;
232  void* memory_;
233  int size_;
234};
235
236
237OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
238  FILE* file = fopen(name, "w+");
239  if (file == NULL) return NULL;
240
241  fseek(file, 0, SEEK_END);
242  int size = ftell(file);
243
244  void* memory =
245      mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
246  return new PosixMemoryMappedFile(file, memory, size);
247}
248
249
250OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
251    void* initial) {
252  FILE* file = fopen(name, "w+");
253  if (file == NULL) return NULL;
254  int result = fwrite(initial, size, 1, file);
255  if (result < 1) {
256    fclose(file);
257    return NULL;
258  }
259  void* memory =
260      mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
261  return new PosixMemoryMappedFile(file, memory, size);
262}
263
264
265PosixMemoryMappedFile::~PosixMemoryMappedFile() {
266  if (memory_) munmap(memory_, size_);
267  fclose(file_);
268}
269
270
271void OS::LogSharedLibraryAddresses() {
272}
273
274
275void OS::SignalCodeMovingGC() {
276}
277
278
279struct StackWalker {
280  Vector<OS::StackFrame>& frames;
281  int index;
282};
283
284
285static int StackWalkCallback(uintptr_t pc, int signo, void* data) {
286  struct StackWalker* walker = static_cast<struct StackWalker*>(data);
287  Dl_info info;
288
289  int i = walker->index;
290
291  walker->frames[i].address = reinterpret_cast<void*>(pc);
292
293  // Make sure line termination is in place.
294  walker->frames[i].text[OS::kStackWalkMaxTextLen - 1] = '\0';
295
296  Vector<char> text = MutableCStrVector(walker->frames[i].text,
297                                        OS::kStackWalkMaxTextLen);
298
299  if (dladdr(reinterpret_cast<void*>(pc), &info) == 0) {
300    OS::SNPrintF(text, "[0x%p]", pc);
301  } else if ((info.dli_fname != NULL && info.dli_sname != NULL)) {
302    // We have symbol info.
303    OS::SNPrintF(text, "%s'%s+0x%x", info.dli_fname, info.dli_sname, pc);
304  } else {
305    // No local symbol info.
306    OS::SNPrintF(text,
307                 "%s'0x%p [0x%p]",
308                 info.dli_fname,
309                 pc - reinterpret_cast<uintptr_t>(info.dli_fbase),
310                 pc);
311  }
312  walker->index++;
313  return 0;
314}
315
316
317int OS::StackWalk(Vector<OS::StackFrame> frames) {
318  ucontext_t ctx;
319  struct StackWalker walker = { frames, 0 };
320
321  if (getcontext(&ctx) < 0) return kStackWalkError;
322
323  if (!walkcontext(&ctx, StackWalkCallback, &walker)) {
324    return kStackWalkError;
325  }
326
327  return walker.index;
328}
329
330
331// Constants used for mmap.
332static const int kMmapFd = -1;
333static const int kMmapFdOffset = 0;
334
335
336VirtualMemory::VirtualMemory(size_t size) {
337  address_ = mmap(NULL, size, PROT_NONE,
338                  MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
339                  kMmapFd, kMmapFdOffset);
340  size_ = size;
341}
342
343
344VirtualMemory::~VirtualMemory() {
345  if (IsReserved()) {
346    if (0 == munmap(address(), size())) address_ = MAP_FAILED;
347  }
348}
349
350
351bool VirtualMemory::IsReserved() {
352  return address_ != MAP_FAILED;
353}
354
355
356bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
357  int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
358  if (MAP_FAILED == mmap(address, size, prot,
359                         MAP_PRIVATE | MAP_ANON | MAP_FIXED,
360                         kMmapFd, kMmapFdOffset)) {
361    return false;
362  }
363
364  UpdateAllocatedSpaceLimits(address, size);
365  return true;
366}
367
368
369bool VirtualMemory::Uncommit(void* address, size_t size) {
370  return mmap(address, size, PROT_NONE,
371              MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
372              kMmapFd, kMmapFdOffset) != MAP_FAILED;
373}
374
375
376class ThreadHandle::PlatformData : public Malloced {
377 public:
378  explicit PlatformData(ThreadHandle::Kind kind) {
379    Initialize(kind);
380  }
381
382  void Initialize(ThreadHandle::Kind kind) {
383    switch (kind) {
384      case ThreadHandle::SELF: thread_ = pthread_self(); break;
385      case ThreadHandle::INVALID: thread_ = kNoThread; break;
386    }
387  }
388
389  pthread_t thread_;  // Thread handle for pthread.
390};
391
392
393ThreadHandle::ThreadHandle(Kind kind) {
394  data_ = new PlatformData(kind);
395}
396
397
398void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
399  data_->Initialize(kind);
400}
401
402
403ThreadHandle::~ThreadHandle() {
404  delete data_;
405}
406
407
408bool ThreadHandle::IsSelf() const {
409  return pthread_equal(data_->thread_, pthread_self());
410}
411
412
413bool ThreadHandle::IsValid() const {
414  return data_->thread_ != kNoThread;
415}
416
417
418Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
419  set_name("v8:<unknown>");
420}
421
422
423Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
424  set_name(name);
425}
426
427
428Thread::~Thread() {
429}
430
431
432static void* ThreadEntry(void* arg) {
433  Thread* thread = reinterpret_cast<Thread*>(arg);
434  // This is also initialized by the first argument to pthread_create() but we
435  // don't know which thread will run first (the original thread or the new
436  // one) so we initialize it here too.
437  thread->thread_handle_data()->thread_ = pthread_self();
438  ASSERT(thread->IsValid());
439  thread->Run();
440  return NULL;
441}
442
443
444void Thread::set_name(const char* name) {
445  strncpy(name_, name, sizeof(name_));
446  name_[sizeof(name_) - 1] = '\0';
447}
448
449
450void Thread::Start() {
451  pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
452  ASSERT(IsValid());
453}
454
455
456void Thread::Join() {
457  pthread_join(thread_handle_data()->thread_, NULL);
458}
459
460
461Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
462  pthread_key_t key;
463  int result = pthread_key_create(&key, NULL);
464  USE(result);
465  ASSERT(result == 0);
466  return static_cast<LocalStorageKey>(key);
467}
468
469
470void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
471  pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
472  int result = pthread_key_delete(pthread_key);
473  USE(result);
474  ASSERT(result == 0);
475}
476
477
478void* Thread::GetThreadLocal(LocalStorageKey key) {
479  pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
480  return pthread_getspecific(pthread_key);
481}
482
483
484void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
485  pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
486  pthread_setspecific(pthread_key, value);
487}
488
489
490void Thread::YieldCPU() {
491  sched_yield();
492}
493
494
495class SolarisMutex : public Mutex {
496 public:
497
498  SolarisMutex() {
499    pthread_mutexattr_t attr;
500    pthread_mutexattr_init(&attr);
501    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
502    pthread_mutex_init(&mutex_, &attr);
503  }
504
505  ~SolarisMutex() { pthread_mutex_destroy(&mutex_); }
506
507  int Lock() { return pthread_mutex_lock(&mutex_); }
508
509  int Unlock() { return pthread_mutex_unlock(&mutex_); }
510
511  virtual bool TryLock() {
512    int result = pthread_mutex_trylock(&mutex_);
513    // Return false if the lock is busy and locking failed.
514    if (result == EBUSY) {
515      return false;
516    }
517    ASSERT(result == 0);  // Verify no other errors.
518    return true;
519  }
520
521 private:
522  pthread_mutex_t mutex_;
523};
524
525
526Mutex* OS::CreateMutex() {
527  return new SolarisMutex();
528}
529
530
531class SolarisSemaphore : public Semaphore {
532 public:
533  explicit SolarisSemaphore(int count) {  sem_init(&sem_, 0, count); }
534  virtual ~SolarisSemaphore() { sem_destroy(&sem_); }
535
536  virtual void Wait();
537  virtual bool Wait(int timeout);
538  virtual void Signal() { sem_post(&sem_); }
539 private:
540  sem_t sem_;
541};
542
543
544void SolarisSemaphore::Wait() {
545  while (true) {
546    int result = sem_wait(&sem_);
547    if (result == 0) return;  // Successfully got semaphore.
548    CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
549  }
550}
551
552
553#ifndef TIMEVAL_TO_TIMESPEC
554#define TIMEVAL_TO_TIMESPEC(tv, ts) do {                            \
555    (ts)->tv_sec = (tv)->tv_sec;                                    \
556    (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
557} while (false)
558#endif
559
560
561#ifndef timeradd
562#define timeradd(a, b, result) \
563  do { \
564    (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
565    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
566    if ((result)->tv_usec >= 1000000) { \
567      ++(result)->tv_sec; \
568      (result)->tv_usec -= 1000000; \
569    } \
570  } while (0)
571#endif
572
573
574bool SolarisSemaphore::Wait(int timeout) {
575  const long kOneSecondMicros = 1000000;  // NOLINT
576
577  // Split timeout into second and nanosecond parts.
578  struct timeval delta;
579  delta.tv_usec = timeout % kOneSecondMicros;
580  delta.tv_sec = timeout / kOneSecondMicros;
581
582  struct timeval current_time;
583  // Get the current time.
584  if (gettimeofday(&current_time, NULL) == -1) {
585    return false;
586  }
587
588  // Calculate time for end of timeout.
589  struct timeval end_time;
590  timeradd(&current_time, &delta, &end_time);
591
592  struct timespec ts;
593  TIMEVAL_TO_TIMESPEC(&end_time, &ts);
594  // Wait for semaphore signalled or timeout.
595  while (true) {
596    int result = sem_timedwait(&sem_, &ts);
597    if (result == 0) return true;  // Successfully got semaphore.
598    if (result == -1 && errno == ETIMEDOUT) return false;  // Timeout.
599    CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
600  }
601}
602
603
604Semaphore* OS::CreateSemaphore(int count) {
605  return new SolarisSemaphore(count);
606}
607
608
609#ifdef ENABLE_LOGGING_AND_PROFILING
610
611static Sampler* active_sampler_ = NULL;
612static pthread_t vm_tid_ = 0;
613
614
615static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
616  USE(info);
617  if (signal != SIGPROF) return;
618  if (active_sampler_ == NULL || !active_sampler_->IsActive()) return;
619  if (vm_tid_ != pthread_self()) return;
620
621  TickSample sample_obj;
622  TickSample* sample = CpuProfiler::TickSampleEvent();
623  if (sample == NULL) sample = &sample_obj;
624
625  // Extracting the sample from the context is extremely machine dependent.
626  ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
627  mcontext_t& mcontext = ucontext->uc_mcontext;
628  sample->state = Top::current_vm_state();
629
630#if V8_HOST_ARCH_IA32
631  sample->pc = reinterpret_cast<Address>(mcontext.gregs[KDIREG_EIP]);
632  sample->sp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_ESP]);
633  sample->fp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_EBP]);
634#elif V8_HOST_ARCH_X64
635  sample->pc = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RIP]);
636  sample->sp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RSP]);
637  sample->fp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RBP]);
638#else
639  UNIMPLEMENTED();
640#endif
641  active_sampler_->SampleStack(sample);
642  active_sampler_->Tick(sample);
643}
644
645
646class Sampler::PlatformData : public Malloced {
647 public:
648  PlatformData() {
649    signal_handler_installed_ = false;
650  }
651
652  bool signal_handler_installed_;
653  struct sigaction old_signal_handler_;
654  struct itimerval old_timer_value_;
655};
656
657
658Sampler::Sampler(int interval)
659    : interval_(interval),
660      profiling_(false),
661      active_(false),
662      samples_taken_(0) {
663  data_ = new PlatformData();
664}
665
666
667Sampler::~Sampler() {
668  delete data_;
669}
670
671
672void Sampler::Start() {
673  // There can only be one active sampler at the time on POSIX
674  // platforms.
675  if (active_sampler_ != NULL) return;
676
677  // Request profiling signals.
678  struct sigaction sa;
679  sa.sa_sigaction = ProfilerSignalHandler;
680  sigemptyset(&sa.sa_mask);
681  sa.sa_flags = SA_SIGINFO;
682  if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
683  data_->signal_handler_installed_ = true;
684
685  // Set the itimer to generate a tick for each interval.
686  itimerval itimer;
687  itimer.it_interval.tv_sec = interval_ / 1000;
688  itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
689  itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
690  itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
691  setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
692
693  // Set this sampler as the active sampler.
694  active_sampler_ = this;
695  active_ = true;
696}
697
698
699void Sampler::Stop() {
700  // Restore old signal handler
701  if (data_->signal_handler_installed_) {
702    setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
703    sigaction(SIGPROF, &data_->old_signal_handler_, 0);
704    data_->signal_handler_installed_ = false;
705  }
706
707  // This sampler is no longer the active sampler.
708  active_sampler_ = NULL;
709  active_ = false;
710}
711
712#endif  // ENABLE_LOGGING_AND_PROFILING
713
714} }  // namespace v8::internal
715