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