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