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 64namespace std { 65// Test sign - usually defined in math.h 66int 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 80namespace v8 { 81namespace internal { 82 83 84static Mutex* limit_mutex = NULL; 85 86 87const 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 96double 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. 107static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); 108static void* highest_ever_allocated = reinterpret_cast<void*>(0); 109 110 111static 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 122bool OS::IsOutsideAllocatedSpace(void* address) { 123 return address < lowest_ever_allocated || address >= highest_ever_allocated; 124} 125 126 127void* 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 144void OS::DumpBacktrace() { 145 // Currently unsupported. 146} 147 148 149class 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 163OS::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 176OS::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 191PosixMemoryMappedFile::~PosixMemoryMappedFile() { 192 if (memory_) munmap(memory_, size_); 193 fclose(file_); 194} 195 196 197void OS::LogSharedLibraryAddresses() { 198} 199 200 201void OS::SignalCodeMovingGC() { 202} 203 204 205struct StackWalker { 206 Vector<OS::StackFrame>& frames; 207 int index; 208}; 209 210 211static 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 243int 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. 258static const int kMmapFd = -1; 259static const int kMmapFdOffset = 0; 260 261 262VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } 263 264 265VirtualMemory::VirtualMemory(size_t size) 266 : address_(ReserveRegion(size)), size_(size) { } 267 268 269VirtualMemory::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 309VirtualMemory::~VirtualMemory() { 310 if (IsReserved()) { 311 bool result = ReleaseRegion(address(), size()); 312 ASSERT(result); 313 USE(result); 314 } 315} 316 317 318bool VirtualMemory::IsReserved() { 319 return address_ != NULL; 320} 321 322 323void VirtualMemory::Reset() { 324 address_ = NULL; 325 size_ = 0; 326} 327 328 329bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 330 return CommitRegion(address, size, is_executable); 331} 332 333 334bool VirtualMemory::Uncommit(void* address, size_t size) { 335 return UncommitRegion(address, size); 336} 337 338 339bool VirtualMemory::Guard(void* address) { 340 OS::Guard(address, OS::CommitPageSize()); 341 return true; 342} 343 344 345void* 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 359bool 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 375bool 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 385bool VirtualMemory::ReleaseRegion(void* base, size_t size) { 386 return munmap(base, size) == 0; 387} 388 389 390bool VirtualMemory::HasLazyCommits() { 391 // TODO(alph): implement for the platform. 392 return false; 393} 394 395 396class 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 409void 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 439bool 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(¤t_time, NULL) == -1) { 450 return false; 451 } 452 453 // Calculate time for end of timeout. 454 struct timeval end_time; 455 timeradd(¤t_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 469Semaphore* OS::CreateSemaphore(int count) { 470 return new SolarisSemaphore(count); 471} 472 473 474void 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 486void OS::TearDown() { 487 delete limit_mutex; 488} 489 490 491} } // namespace v8::internal 492