platform-freebsd.cc revision 888f6729be6a6f6fbe246cb5a9f122e2dbe455b7
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 asm("bkpt 0"); 197#else 198 asm("int $3"); 199#endif 200} 201 202 203class PosixMemoryMappedFile : public OS::MemoryMappedFile { 204 public: 205 PosixMemoryMappedFile(FILE* file, void* memory, int size) 206 : file_(file), memory_(memory), size_(size) { } 207 virtual ~PosixMemoryMappedFile(); 208 virtual void* memory() { return memory_; } 209 private: 210 FILE* file_; 211 void* memory_; 212 int size_; 213}; 214 215 216OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 217 void* initial) { 218 FILE* file = fopen(name, "w+"); 219 if (file == NULL) return NULL; 220 int result = fwrite(initial, size, 1, file); 221 if (result < 1) { 222 fclose(file); 223 return NULL; 224 } 225 void* memory = 226 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 227 return new PosixMemoryMappedFile(file, memory, size); 228} 229 230 231PosixMemoryMappedFile::~PosixMemoryMappedFile() { 232 if (memory_) munmap(memory_, size_); 233 fclose(file_); 234} 235 236 237#ifdef ENABLE_LOGGING_AND_PROFILING 238static unsigned StringToLong(char* buffer) { 239 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT 240} 241#endif 242 243 244void OS::LogSharedLibraryAddresses() { 245#ifdef ENABLE_LOGGING_AND_PROFILING 246 static const int MAP_LENGTH = 1024; 247 int fd = open("/proc/self/maps", O_RDONLY); 248 if (fd < 0) return; 249 while (true) { 250 char addr_buffer[11]; 251 addr_buffer[0] = '0'; 252 addr_buffer[1] = 'x'; 253 addr_buffer[10] = 0; 254 int result = read(fd, addr_buffer + 2, 8); 255 if (result < 8) break; 256 unsigned start = StringToLong(addr_buffer); 257 result = read(fd, addr_buffer + 2, 1); 258 if (result < 1) break; 259 if (addr_buffer[2] != '-') break; 260 result = read(fd, addr_buffer + 2, 8); 261 if (result < 8) break; 262 unsigned end = StringToLong(addr_buffer); 263 char buffer[MAP_LENGTH]; 264 int bytes_read = -1; 265 do { 266 bytes_read++; 267 if (bytes_read >= MAP_LENGTH - 1) 268 break; 269 result = read(fd, buffer + bytes_read, 1); 270 if (result < 1) break; 271 } while (buffer[bytes_read] != '\n'); 272 buffer[bytes_read] = 0; 273 // Ignore mappings that are not executable. 274 if (buffer[3] != 'x') continue; 275 char* start_of_path = index(buffer, '/'); 276 // There may be no filename in this line. Skip to next. 277 if (start_of_path == NULL) continue; 278 buffer[bytes_read] = 0; 279 LOG(SharedLibraryEvent(start_of_path, start, end)); 280 } 281 close(fd); 282#endif 283} 284 285 286int OS::StackWalk(Vector<OS::StackFrame> frames) { 287 int frames_size = frames.length(); 288 void** addresses = NewArray<void*>(frames_size); 289 290 int frames_count = backtrace(addresses, frames_size); 291 292 char** symbols; 293 symbols = backtrace_symbols(addresses, frames_count); 294 if (symbols == NULL) { 295 DeleteArray(addresses); 296 return kStackWalkError; 297 } 298 299 for (int i = 0; i < frames_count; i++) { 300 frames[i].address = addresses[i]; 301 // Format a text representation of the frame based on the information 302 // available. 303 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen), 304 "%s", 305 symbols[i]); 306 // Make sure line termination is in place. 307 frames[i].text[kStackWalkMaxTextLen - 1] = '\0'; 308 } 309 310 DeleteArray(addresses); 311 free(symbols); 312 313 return frames_count; 314} 315 316 317// Constants used for mmap. 318static const int kMmapFd = -1; 319static const int kMmapFdOffset = 0; 320 321 322VirtualMemory::VirtualMemory(size_t size) { 323 address_ = mmap(NULL, size, PROT_NONE, 324 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, 325 kMmapFd, kMmapFdOffset); 326 size_ = size; 327} 328 329 330VirtualMemory::~VirtualMemory() { 331 if (IsReserved()) { 332 if (0 == munmap(address(), size())) address_ = MAP_FAILED; 333 } 334} 335 336 337bool VirtualMemory::IsReserved() { 338 return address_ != MAP_FAILED; 339} 340 341 342bool VirtualMemory::Commit(void* address, size_t size, bool executable) { 343 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); 344 if (MAP_FAILED == mmap(address, size, prot, 345 MAP_PRIVATE | MAP_ANON | MAP_FIXED, 346 kMmapFd, kMmapFdOffset)) { 347 return false; 348 } 349 350 UpdateAllocatedSpaceLimits(address, size); 351 return true; 352} 353 354 355bool VirtualMemory::Uncommit(void* address, size_t size) { 356 return mmap(address, size, PROT_NONE, 357 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, 358 kMmapFd, kMmapFdOffset) != MAP_FAILED; 359} 360 361 362class ThreadHandle::PlatformData : public Malloced { 363 public: 364 explicit PlatformData(ThreadHandle::Kind kind) { 365 Initialize(kind); 366 } 367 368 void Initialize(ThreadHandle::Kind kind) { 369 switch (kind) { 370 case ThreadHandle::SELF: thread_ = pthread_self(); break; 371 case ThreadHandle::INVALID: thread_ = kNoThread; break; 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} 405 406 407Thread::~Thread() { 408} 409 410 411static void* ThreadEntry(void* arg) { 412 Thread* thread = reinterpret_cast<Thread*>(arg); 413 // This is also initialized by the first argument to pthread_create() but we 414 // don't know which thread will run first (the original thread or the new 415 // one) so we initialize it here too. 416 thread->thread_handle_data()->thread_ = pthread_self(); 417 ASSERT(thread->IsValid()); 418 thread->Run(); 419 return NULL; 420} 421 422 423void Thread::Start() { 424 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this); 425 ASSERT(IsValid()); 426} 427 428 429void Thread::Join() { 430 pthread_join(thread_handle_data()->thread_, NULL); 431} 432 433 434Thread::LocalStorageKey Thread::CreateThreadLocalKey() { 435 pthread_key_t key; 436 int result = pthread_key_create(&key, NULL); 437 USE(result); 438 ASSERT(result == 0); 439 return static_cast<LocalStorageKey>(key); 440} 441 442 443void Thread::DeleteThreadLocalKey(LocalStorageKey key) { 444 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); 445 int result = pthread_key_delete(pthread_key); 446 USE(result); 447 ASSERT(result == 0); 448} 449 450 451void* Thread::GetThreadLocal(LocalStorageKey key) { 452 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); 453 return pthread_getspecific(pthread_key); 454} 455 456 457void Thread::SetThreadLocal(LocalStorageKey key, void* value) { 458 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); 459 pthread_setspecific(pthread_key, value); 460} 461 462 463void Thread::YieldCPU() { 464 sched_yield(); 465} 466 467 468class FreeBSDMutex : public Mutex { 469 public: 470 471 FreeBSDMutex() { 472 pthread_mutexattr_t attrs; 473 int result = pthread_mutexattr_init(&attrs); 474 ASSERT(result == 0); 475 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE); 476 ASSERT(result == 0); 477 result = pthread_mutex_init(&mutex_, &attrs); 478 ASSERT(result == 0); 479 } 480 481 virtual ~FreeBSDMutex() { pthread_mutex_destroy(&mutex_); } 482 483 virtual int Lock() { 484 int result = pthread_mutex_lock(&mutex_); 485 return result; 486 } 487 488 virtual int Unlock() { 489 int result = pthread_mutex_unlock(&mutex_); 490 return result; 491 } 492 493 private: 494 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms. 495}; 496 497 498Mutex* OS::CreateMutex() { 499 return new FreeBSDMutex(); 500} 501 502 503class FreeBSDSemaphore : public Semaphore { 504 public: 505 explicit FreeBSDSemaphore(int count) { sem_init(&sem_, 0, count); } 506 virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); } 507 508 virtual void Wait(); 509 virtual bool Wait(int timeout); 510 virtual void Signal() { sem_post(&sem_); } 511 private: 512 sem_t sem_; 513}; 514 515 516void FreeBSDSemaphore::Wait() { 517 while (true) { 518 int result = sem_wait(&sem_); 519 if (result == 0) return; // Successfully got semaphore. 520 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 521 } 522} 523 524 525bool FreeBSDSemaphore::Wait(int timeout) { 526 const long kOneSecondMicros = 1000000; // NOLINT 527 528 // Split timeout into second and nanosecond parts. 529 struct timeval delta; 530 delta.tv_usec = timeout % kOneSecondMicros; 531 delta.tv_sec = timeout / kOneSecondMicros; 532 533 struct timeval current_time; 534 // Get the current time. 535 if (gettimeofday(¤t_time, NULL) == -1) { 536 return false; 537 } 538 539 // Calculate time for end of timeout. 540 struct timeval end_time; 541 timeradd(¤t_time, &delta, &end_time); 542 543 struct timespec ts; 544 TIMEVAL_TO_TIMESPEC(&end_time, &ts); 545 while (true) { 546 int result = sem_timedwait(&sem_, &ts); 547 if (result == 0) return true; // Successfully got semaphore. 548 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout. 549 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 550 } 551} 552 553 554Semaphore* OS::CreateSemaphore(int count) { 555 return new FreeBSDSemaphore(count); 556} 557 558 559#ifdef ENABLE_LOGGING_AND_PROFILING 560 561static Sampler* active_sampler_ = NULL; 562 563static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { 564 USE(info); 565 if (signal != SIGPROF) return; 566 if (active_sampler_ == NULL) return; 567 568 TickSample sample; 569 570 // If profiling, we extract the current pc and sp. 571 if (active_sampler_->IsProfiling()) { 572 // Extracting the sample from the context is extremely machine dependent. 573 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); 574 mcontext_t& mcontext = ucontext->uc_mcontext; 575#if V8_HOST_ARCH_IA32 576 sample.pc = reinterpret_cast<Address>(mcontext.mc_eip); 577 sample.sp = reinterpret_cast<Address>(mcontext.mc_esp); 578 sample.fp = reinterpret_cast<Address>(mcontext.mc_ebp); 579#elif V8_HOST_ARCH_X64 580 sample.pc = reinterpret_cast<Address>(mcontext.mc_rip); 581 sample.sp = reinterpret_cast<Address>(mcontext.mc_rsp); 582 sample.fp = reinterpret_cast<Address>(mcontext.mc_rbp); 583#elif V8_HOST_ARCH_ARM 584 sample.pc = reinterpret_cast<Address>(mcontext.mc_r15); 585 sample.sp = reinterpret_cast<Address>(mcontext.mc_r13); 586 sample.fp = reinterpret_cast<Address>(mcontext.mc_r11); 587#endif 588 active_sampler_->SampleStack(&sample); 589 } 590 591 // We always sample the VM state. 592 sample.state = Logger::state(); 593 594 active_sampler_->Tick(&sample); 595} 596 597 598class Sampler::PlatformData : public Malloced { 599 public: 600 PlatformData() { 601 signal_handler_installed_ = false; 602 } 603 604 bool signal_handler_installed_; 605 struct sigaction old_signal_handler_; 606 struct itimerval old_timer_value_; 607}; 608 609 610Sampler::Sampler(int interval, bool profiling) 611 : interval_(interval), profiling_(profiling), active_(false) { 612 data_ = new PlatformData(); 613} 614 615 616Sampler::~Sampler() { 617 delete data_; 618} 619 620 621void Sampler::Start() { 622 // There can only be one active sampler at the time on POSIX 623 // platforms. 624 if (active_sampler_ != NULL) return; 625 626 // Request profiling signals. 627 struct sigaction sa; 628 sa.sa_sigaction = ProfilerSignalHandler; 629 sigemptyset(&sa.sa_mask); 630 sa.sa_flags = SA_SIGINFO; 631 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; 632 data_->signal_handler_installed_ = true; 633 634 // Set the itimer to generate a tick for each interval. 635 itimerval itimer; 636 itimer.it_interval.tv_sec = interval_ / 1000; 637 itimer.it_interval.tv_usec = (interval_ % 1000) * 1000; 638 itimer.it_value.tv_sec = itimer.it_interval.tv_sec; 639 itimer.it_value.tv_usec = itimer.it_interval.tv_usec; 640 setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_); 641 642 // Set this sampler as the active sampler. 643 active_sampler_ = this; 644 active_ = true; 645} 646 647 648void Sampler::Stop() { 649 // Restore old signal handler 650 if (data_->signal_handler_installed_) { 651 setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL); 652 sigaction(SIGPROF, &data_->old_signal_handler_, 0); 653 data_->signal_handler_installed_ = false; 654 } 655 656 // This sampler is no longer the active sampler. 657 active_sampler_ = NULL; 658 active_ = false; 659} 660 661#endif // ENABLE_LOGGING_AND_PROFILING 662 663} } // namespace v8::internal 664