platform-freebsd.cc revision 402d937239b0e2fd11bf2f4fe972ad78aa9fd481
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 58namespace v8 { 59namespace 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). 63static const pthread_t kNoThread = (pthread_t) 0; 64 65 66double 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 76void 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 87uint64_t OS::CpuFeaturesImpliedByPlatform() { 88 return 0; // FreeBSD runs on anything. 89} 90 91 92int OS::ActivationFrameAlignment() { 93 // 16 byte alignment on FreeBSD 94 return 16; 95} 96 97 98const 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 107double 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. 121static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); 122static void* highest_ever_allocated = reinterpret_cast<void*>(0); 123 124 125static 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 133bool OS::IsOutsideAllocatedSpace(void* address) { 134 return address < lowest_ever_allocated || address >= highest_ever_allocated; 135} 136 137 138size_t OS::AllocateAlignment() { 139 return getpagesize(); 140} 141 142 143void* 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 160void 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 170void OS::Protect(void* address, size_t size) { 171 UNIMPLEMENTED(); 172} 173 174 175void OS::Unprotect(void* address, size_t size, bool is_executable) { 176 UNIMPLEMENTED(); 177} 178 179#endif 180 181 182void OS::Sleep(int milliseconds) { 183 unsigned int ms = static_cast<unsigned int>(milliseconds); 184 usleep(1000 * ms); 185} 186 187 188void OS::Abort() { 189 // Redirect to std abort to signal abnormal program termination. 190 abort(); 191} 192 193 194void 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 204class 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 217OS::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 232PosixMemoryMappedFile::~PosixMemoryMappedFile() { 233 if (memory_) munmap(memory_, size_); 234 fclose(file_); 235} 236 237 238#ifdef ENABLE_LOGGING_AND_PROFILING 239static unsigned StringToLong(char* buffer) { 240 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT 241} 242#endif 243 244 245void 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 287int 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. 319static const int kMmapFd = -1; 320static const int kMmapFdOffset = 0; 321 322 323VirtualMemory::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 331VirtualMemory::~VirtualMemory() { 332 if (IsReserved()) { 333 if (0 == munmap(address(), size())) address_ = MAP_FAILED; 334 } 335} 336 337 338bool VirtualMemory::IsReserved() { 339 return address_ != MAP_FAILED; 340} 341 342 343bool 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 356bool 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 363class 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 379ThreadHandle::ThreadHandle(Kind kind) { 380 data_ = new PlatformData(kind); 381} 382 383 384void ThreadHandle::Initialize(ThreadHandle::Kind kind) { 385 data_->Initialize(kind); 386} 387 388 389ThreadHandle::~ThreadHandle() { 390 delete data_; 391} 392 393 394bool ThreadHandle::IsSelf() const { 395 return pthread_equal(data_->thread_, pthread_self()); 396} 397 398 399bool ThreadHandle::IsValid() const { 400 return data_->thread_ != kNoThread; 401} 402 403 404Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) { 405} 406 407 408Thread::~Thread() { 409} 410 411 412static 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 424void Thread::Start() { 425 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this); 426 ASSERT(IsValid()); 427} 428 429 430void Thread::Join() { 431 pthread_join(thread_handle_data()->thread_, NULL); 432} 433 434 435Thread::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 444void 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 452void* 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 458void 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 464void Thread::YieldCPU() { 465 sched_yield(); 466} 467 468 469class 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 499Mutex* OS::CreateMutex() { 500 return new FreeBSDMutex(); 501} 502 503 504class 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 517void 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 526bool 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 555Semaphore* OS::CreateSemaphore(int count) { 556 return new FreeBSDSemaphore(count); 557} 558 559 560#ifdef ENABLE_LOGGING_AND_PROFILING 561 562static Sampler* active_sampler_ = NULL; 563 564static 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 599class 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 611Sampler::Sampler(int interval, bool profiling) 612 : interval_(interval), profiling_(profiling), active_(false) { 613 data_ = new PlatformData(); 614} 615 616 617Sampler::~Sampler() { 618 delete data_; 619} 620 621 622void 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 649void 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