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