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