Home | History | Annotate | Download | only in src
      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 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 <semaphore.h>
     42 #include <time.h>
     43 #include <sys/time.h>  // gettimeofday(), timeradd()
     44 #include <errno.h>
     45 #include <ieeefp.h>  // finite()
     46 #include <signal.h>  // sigemptyset(), etc
     47 #include <sys/regset.h>
     48 
     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 // It seems there is a bug in some Solaris distributions (experienced in
     61 // SunOS 5.10 Generic_141445-09) which make it difficult or impossible to
     62 // access signbit() despite the availability of other C99 math functions.
     63 #ifndef signbit
     64 namespace std {
     65 // Test sign - usually defined in math.h
     66 int signbit(double x) {
     67   // We need to take care of the special case of both positive and negative
     68   // versions of zero.
     69   if (x == 0) {
     70     return fpclass(x) & FP_NZERO;
     71   } else {
     72     // This won't detect negative NaN but that should be okay since we don't
     73     // assume that behavior.
     74     return x < 0;
     75   }
     76 }
     77 }  // namespace std
     78 #endif  // signbit
     79 
     80 namespace v8 {
     81 namespace internal {
     82 
     83 
     84 static Mutex* limit_mutex = NULL;
     85 
     86 
     87 const char* OS::LocalTimezone(double time) {
     88   if (std::isnan(time)) return "";
     89   time_t tv = static_cast<time_t>(floor(time/msPerSecond));
     90   struct tm* t = localtime(&tv);
     91   if (NULL == t) return "";
     92   return tzname[0];  // The location of the timezone string on Solaris.
     93 }
     94 
     95 
     96 double OS::LocalTimeOffset() {
     97   tzset();
     98   return -static_cast<double>(timezone * msPerSecond);
     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 void* OS::Allocate(const size_t requested,
    128                    size_t* allocated,
    129                    bool is_executable) {
    130   const size_t msize = RoundUp(requested, getpagesize());
    131   int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
    132   void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
    133 
    134   if (mbase == MAP_FAILED) {
    135     LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
    136     return NULL;
    137   }
    138   *allocated = msize;
    139   UpdateAllocatedSpaceLimits(mbase, msize);
    140   return mbase;
    141 }
    142 
    143 
    144 void OS::DumpBacktrace() {
    145   // Currently unsupported.
    146 }
    147 
    148 
    149 class PosixMemoryMappedFile : public OS::MemoryMappedFile {
    150  public:
    151   PosixMemoryMappedFile(FILE* file, void* memory, int size)
    152     : file_(file), memory_(memory), size_(size) { }
    153   virtual ~PosixMemoryMappedFile();
    154   virtual void* memory() { return memory_; }
    155   virtual int size() { return size_; }
    156  private:
    157   FILE* file_;
    158   void* memory_;
    159   int size_;
    160 };
    161 
    162 
    163 OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
    164   FILE* file = fopen(name, "r+");
    165   if (file == NULL) return NULL;
    166 
    167   fseek(file, 0, SEEK_END);
    168   int size = ftell(file);
    169 
    170   void* memory =
    171       mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
    172   return new PosixMemoryMappedFile(file, memory, size);
    173 }
    174 
    175 
    176 OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
    177     void* initial) {
    178   FILE* file = fopen(name, "w+");
    179   if (file == NULL) return NULL;
    180   int result = fwrite(initial, size, 1, file);
    181   if (result < 1) {
    182     fclose(file);
    183     return NULL;
    184   }
    185   void* memory =
    186       mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
    187   return new PosixMemoryMappedFile(file, memory, size);
    188 }
    189 
    190 
    191 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
    192   if (memory_) munmap(memory_, size_);
    193   fclose(file_);
    194 }
    195 
    196 
    197 void OS::LogSharedLibraryAddresses() {
    198 }
    199 
    200 
    201 void OS::SignalCodeMovingGC() {
    202 }
    203 
    204 
    205 struct StackWalker {
    206   Vector<OS::StackFrame>& frames;
    207   int index;
    208 };
    209 
    210 
    211 static int StackWalkCallback(uintptr_t pc, int signo, void* data) {
    212   struct StackWalker* walker = static_cast<struct StackWalker*>(data);
    213   Dl_info info;
    214 
    215   int i = walker->index;
    216 
    217   walker->frames[i].address = reinterpret_cast<void*>(pc);
    218 
    219   // Make sure line termination is in place.
    220   walker->frames[i].text[OS::kStackWalkMaxTextLen - 1] = '\0';
    221 
    222   Vector<char> text = MutableCStrVector(walker->frames[i].text,
    223                                         OS::kStackWalkMaxTextLen);
    224 
    225   if (dladdr(reinterpret_cast<void*>(pc), &info) == 0) {
    226     OS::SNPrintF(text, "[0x%p]", pc);
    227   } else if ((info.dli_fname != NULL && info.dli_sname != NULL)) {
    228     // We have symbol info.
    229     OS::SNPrintF(text, "%s'%s+0x%x", info.dli_fname, info.dli_sname, pc);
    230   } else {
    231     // No local symbol info.
    232     OS::SNPrintF(text,
    233                  "%s'0x%p [0x%p]",
    234                  info.dli_fname,
    235                  pc - reinterpret_cast<uintptr_t>(info.dli_fbase),
    236                  pc);
    237   }
    238   walker->index++;
    239   return 0;
    240 }
    241 
    242 
    243 int OS::StackWalk(Vector<OS::StackFrame> frames) {
    244   ucontext_t ctx;
    245   struct StackWalker walker = { frames, 0 };
    246 
    247   if (getcontext(&ctx) < 0) return kStackWalkError;
    248 
    249   if (!walkcontext(&ctx, StackWalkCallback, &walker)) {
    250     return kStackWalkError;
    251   }
    252 
    253   return walker.index;
    254 }
    255 
    256 
    257 // Constants used for mmap.
    258 static const int kMmapFd = -1;
    259 static const int kMmapFdOffset = 0;
    260 
    261 
    262 VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
    263 
    264 
    265 VirtualMemory::VirtualMemory(size_t size)
    266     : address_(ReserveRegion(size)), size_(size) { }
    267 
    268 
    269 VirtualMemory::VirtualMemory(size_t size, size_t alignment)
    270     : address_(NULL), size_(0) {
    271   ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
    272   size_t request_size = RoundUp(size + alignment,
    273                                 static_cast<intptr_t>(OS::AllocateAlignment()));
    274   void* reservation = mmap(OS::GetRandomMmapAddr(),
    275                            request_size,
    276                            PROT_NONE,
    277                            MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
    278                            kMmapFd,
    279                            kMmapFdOffset);
    280   if (reservation == MAP_FAILED) return;
    281 
    282   Address base = static_cast<Address>(reservation);
    283   Address aligned_base = RoundUp(base, alignment);
    284   ASSERT_LE(base, aligned_base);
    285 
    286   // Unmap extra memory reserved before and after the desired block.
    287   if (aligned_base != base) {
    288     size_t prefix_size = static_cast<size_t>(aligned_base - base);
    289     OS::Free(base, prefix_size);
    290     request_size -= prefix_size;
    291   }
    292 
    293   size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
    294   ASSERT_LE(aligned_size, request_size);
    295 
    296   if (aligned_size != request_size) {
    297     size_t suffix_size = request_size - aligned_size;
    298     OS::Free(aligned_base + aligned_size, suffix_size);
    299     request_size -= suffix_size;
    300   }
    301 
    302   ASSERT(aligned_size == request_size);
    303 
    304   address_ = static_cast<void*>(aligned_base);
    305   size_ = aligned_size;
    306 }
    307 
    308 
    309 VirtualMemory::~VirtualMemory() {
    310   if (IsReserved()) {
    311     bool result = ReleaseRegion(address(), size());
    312     ASSERT(result);
    313     USE(result);
    314   }
    315 }
    316 
    317 
    318 bool VirtualMemory::IsReserved() {
    319   return address_ != NULL;
    320 }
    321 
    322 
    323 void VirtualMemory::Reset() {
    324   address_ = NULL;
    325   size_ = 0;
    326 }
    327 
    328 
    329 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
    330   return CommitRegion(address, size, is_executable);
    331 }
    332 
    333 
    334 bool VirtualMemory::Uncommit(void* address, size_t size) {
    335   return UncommitRegion(address, size);
    336 }
    337 
    338 
    339 bool VirtualMemory::Guard(void* address) {
    340   OS::Guard(address, OS::CommitPageSize());
    341   return true;
    342 }
    343 
    344 
    345 void* VirtualMemory::ReserveRegion(size_t size) {
    346   void* result = mmap(OS::GetRandomMmapAddr(),
    347                       size,
    348                       PROT_NONE,
    349                       MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
    350                       kMmapFd,
    351                       kMmapFdOffset);
    352 
    353   if (result == MAP_FAILED) return NULL;
    354 
    355   return result;
    356 }
    357 
    358 
    359 bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
    360   int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
    361   if (MAP_FAILED == mmap(base,
    362                          size,
    363                          prot,
    364                          MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
    365                          kMmapFd,
    366                          kMmapFdOffset)) {
    367     return false;
    368   }
    369 
    370   UpdateAllocatedSpaceLimits(base, size);
    371   return true;
    372 }
    373 
    374 
    375 bool VirtualMemory::UncommitRegion(void* base, size_t size) {
    376   return mmap(base,
    377               size,
    378               PROT_NONE,
    379               MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
    380               kMmapFd,
    381               kMmapFdOffset) != MAP_FAILED;
    382 }
    383 
    384 
    385 bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
    386   return munmap(base, size) == 0;
    387 }
    388 
    389 
    390 bool VirtualMemory::HasLazyCommits() {
    391   // TODO(alph): implement for the platform.
    392   return false;
    393 }
    394 
    395 
    396 class SolarisSemaphore : public Semaphore {
    397  public:
    398   explicit SolarisSemaphore(int count) {  sem_init(&sem_, 0, count); }
    399   virtual ~SolarisSemaphore() { sem_destroy(&sem_); }
    400 
    401   virtual void Wait();
    402   virtual bool Wait(int timeout);
    403   virtual void Signal() { sem_post(&sem_); }
    404  private:
    405   sem_t sem_;
    406 };
    407 
    408 
    409 void SolarisSemaphore::Wait() {
    410   while (true) {
    411     int result = sem_wait(&sem_);
    412     if (result == 0) return;  // Successfully got semaphore.
    413     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
    414   }
    415 }
    416 
    417 
    418 #ifndef TIMEVAL_TO_TIMESPEC
    419 #define TIMEVAL_TO_TIMESPEC(tv, ts) do {                            \
    420     (ts)->tv_sec = (tv)->tv_sec;                                    \
    421     (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
    422 } while (false)
    423 #endif
    424 
    425 
    426 #ifndef timeradd
    427 #define timeradd(a, b, result) \
    428   do { \
    429     (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
    430     (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
    431     if ((result)->tv_usec >= 1000000) { \
    432       ++(result)->tv_sec; \
    433       (result)->tv_usec -= 1000000; \
    434     } \
    435   } while (0)
    436 #endif
    437 
    438 
    439 bool SolarisSemaphore::Wait(int timeout) {
    440   const long kOneSecondMicros = 1000000;  // NOLINT
    441 
    442   // Split timeout into second and nanosecond parts.
    443   struct timeval delta;
    444   delta.tv_usec = timeout % kOneSecondMicros;
    445   delta.tv_sec = timeout / kOneSecondMicros;
    446 
    447   struct timeval current_time;
    448   // Get the current time.
    449   if (gettimeofday(&current_time, NULL) == -1) {
    450     return false;
    451   }
    452 
    453   // Calculate time for end of timeout.
    454   struct timeval end_time;
    455   timeradd(&current_time, &delta, &end_time);
    456 
    457   struct timespec ts;
    458   TIMEVAL_TO_TIMESPEC(&end_time, &ts);
    459   // Wait for semaphore signalled or timeout.
    460   while (true) {
    461     int result = sem_timedwait(&sem_, &ts);
    462     if (result == 0) return true;  // Successfully got semaphore.
    463     if (result == -1 && errno == ETIMEDOUT) return false;  // Timeout.
    464     CHECK(result == -1 && errno == EINTR);  // Signal caused spurious wakeup.
    465   }
    466 }
    467 
    468 
    469 Semaphore* OS::CreateSemaphore(int count) {
    470   return new SolarisSemaphore(count);
    471 }
    472 
    473 
    474 void OS::SetUp() {
    475   // Seed the random number generator.
    476   // Convert the current time to a 64-bit integer first, before converting it
    477   // to an unsigned. Going directly will cause an overflow and the seed to be
    478   // set to all ones. The seed will be identical for different instances that
    479   // call this setup code within the same millisecond.
    480   uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
    481   srandom(static_cast<unsigned int>(seed));
    482   limit_mutex = CreateMutex();
    483 }
    484 
    485 
    486 void OS::TearDown() {
    487   delete limit_mutex;
    488 }
    489 
    490 
    491 } }  // namespace v8::internal
    492