1// Copyright 2012 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// If you don't have execinfo.h then you need devel/libexecinfo from ports. 46#include <execinfo.h> // backtrace, backtrace_symbols 47#include <strings.h> // index 48#include <errno.h> 49#include <stdarg.h> 50#include <limits.h> 51 52#undef MAP_TYPE 53 54#include "v8.h" 55#include "v8threads.h" 56 57#include "platform-posix.h" 58#include "platform.h" 59#include "vm-state-inl.h" 60 61 62namespace v8 { 63namespace internal { 64 65 66static Mutex* limit_mutex = NULL; 67 68 69const char* OS::LocalTimezone(double time) { 70 if (std::isnan(time)) return ""; 71 time_t tv = static_cast<time_t>(floor(time/msPerSecond)); 72 struct tm* t = localtime(&tv); 73 if (NULL == t) return ""; 74 return t->tm_zone; 75} 76 77 78double OS::LocalTimeOffset() { 79 time_t tv = time(NULL); 80 struct tm* t = localtime(&tv); 81 // tm_gmtoff includes any daylight savings offset, so subtract it. 82 return static_cast<double>(t->tm_gmtoff * msPerSecond - 83 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0)); 84} 85 86 87// We keep the lowest and highest addresses mapped as a quick way of 88// determining that pointers are outside the heap (used mostly in assertions 89// and verification). The estimate is conservative, i.e., not all addresses in 90// 'allocated' space are actually allocated to our heap. The range is 91// [lowest, highest), inclusive on the low and and exclusive on the high end. 92static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); 93static void* highest_ever_allocated = reinterpret_cast<void*>(0); 94 95 96static void UpdateAllocatedSpaceLimits(void* address, int size) { 97 ASSERT(limit_mutex != NULL); 98 ScopedLock lock(limit_mutex); 99 100 lowest_ever_allocated = Min(lowest_ever_allocated, address); 101 highest_ever_allocated = 102 Max(highest_ever_allocated, 103 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); 104} 105 106 107bool OS::IsOutsideAllocatedSpace(void* address) { 108 return address < lowest_ever_allocated || address >= highest_ever_allocated; 109} 110 111 112void* OS::Allocate(const size_t requested, 113 size_t* allocated, 114 bool executable) { 115 const size_t msize = RoundUp(requested, getpagesize()); 116 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); 117 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0); 118 119 if (mbase == MAP_FAILED) { 120 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed")); 121 return NULL; 122 } 123 *allocated = msize; 124 UpdateAllocatedSpaceLimits(mbase, msize); 125 return mbase; 126} 127 128 129void OS::DumpBacktrace() { 130 POSIXBacktraceHelper<backtrace, backtrace_symbols>::DumpBacktrace(); 131} 132 133 134class PosixMemoryMappedFile : public OS::MemoryMappedFile { 135 public: 136 PosixMemoryMappedFile(FILE* file, void* memory, int size) 137 : file_(file), memory_(memory), size_(size) { } 138 virtual ~PosixMemoryMappedFile(); 139 virtual void* memory() { return memory_; } 140 virtual int size() { return size_; } 141 private: 142 FILE* file_; 143 void* memory_; 144 int size_; 145}; 146 147 148OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { 149 FILE* file = fopen(name, "r+"); 150 if (file == NULL) return NULL; 151 152 fseek(file, 0, SEEK_END); 153 int size = ftell(file); 154 155 void* memory = 156 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 157 return new PosixMemoryMappedFile(file, memory, size); 158} 159 160 161OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 162 void* initial) { 163 FILE* file = fopen(name, "w+"); 164 if (file == NULL) return NULL; 165 int result = fwrite(initial, size, 1, file); 166 if (result < 1) { 167 fclose(file); 168 return NULL; 169 } 170 void* memory = 171 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 172 return new PosixMemoryMappedFile(file, memory, size); 173} 174 175 176PosixMemoryMappedFile::~PosixMemoryMappedFile() { 177 if (memory_) munmap(memory_, size_); 178 fclose(file_); 179} 180 181 182static unsigned StringToLong(char* buffer) { 183 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT 184} 185 186 187void OS::LogSharedLibraryAddresses() { 188 static const int MAP_LENGTH = 1024; 189 int fd = open("/proc/self/maps", O_RDONLY); 190 if (fd < 0) return; 191 while (true) { 192 char addr_buffer[11]; 193 addr_buffer[0] = '0'; 194 addr_buffer[1] = 'x'; 195 addr_buffer[10] = 0; 196 int result = read(fd, addr_buffer + 2, 8); 197 if (result < 8) break; 198 unsigned start = StringToLong(addr_buffer); 199 result = read(fd, addr_buffer + 2, 1); 200 if (result < 1) break; 201 if (addr_buffer[2] != '-') break; 202 result = read(fd, addr_buffer + 2, 8); 203 if (result < 8) break; 204 unsigned end = StringToLong(addr_buffer); 205 char buffer[MAP_LENGTH]; 206 int bytes_read = -1; 207 do { 208 bytes_read++; 209 if (bytes_read >= MAP_LENGTH - 1) 210 break; 211 result = read(fd, buffer + bytes_read, 1); 212 if (result < 1) break; 213 } while (buffer[bytes_read] != '\n'); 214 buffer[bytes_read] = 0; 215 // Ignore mappings that are not executable. 216 if (buffer[3] != 'x') continue; 217 char* start_of_path = index(buffer, '/'); 218 // There may be no filename in this line. Skip to next. 219 if (start_of_path == NULL) continue; 220 buffer[bytes_read] = 0; 221 LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end)); 222 } 223 close(fd); 224} 225 226 227void OS::SignalCodeMovingGC() { 228} 229 230 231int OS::StackWalk(Vector<OS::StackFrame> frames) { 232 return POSIXBacktraceHelper<backtrace, backtrace_symbols>::StackWalk(frames); 233} 234 235 236// Constants used for mmap. 237static const int kMmapFd = -1; 238static const int kMmapFdOffset = 0; 239 240 241VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } 242 243 244VirtualMemory::VirtualMemory(size_t size) 245 : address_(ReserveRegion(size)), size_(size) { } 246 247 248VirtualMemory::VirtualMemory(size_t size, size_t alignment) 249 : address_(NULL), size_(0) { 250 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); 251 size_t request_size = RoundUp(size + alignment, 252 static_cast<intptr_t>(OS::AllocateAlignment())); 253 void* reservation = mmap(OS::GetRandomMmapAddr(), 254 request_size, 255 PROT_NONE, 256 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, 257 kMmapFd, 258 kMmapFdOffset); 259 if (reservation == MAP_FAILED) return; 260 261 Address base = static_cast<Address>(reservation); 262 Address aligned_base = RoundUp(base, alignment); 263 ASSERT_LE(base, aligned_base); 264 265 // Unmap extra memory reserved before and after the desired block. 266 if (aligned_base != base) { 267 size_t prefix_size = static_cast<size_t>(aligned_base - base); 268 OS::Free(base, prefix_size); 269 request_size -= prefix_size; 270 } 271 272 size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); 273 ASSERT_LE(aligned_size, request_size); 274 275 if (aligned_size != request_size) { 276 size_t suffix_size = request_size - aligned_size; 277 OS::Free(aligned_base + aligned_size, suffix_size); 278 request_size -= suffix_size; 279 } 280 281 ASSERT(aligned_size == request_size); 282 283 address_ = static_cast<void*>(aligned_base); 284 size_ = aligned_size; 285} 286 287 288VirtualMemory::~VirtualMemory() { 289 if (IsReserved()) { 290 bool result = ReleaseRegion(address(), size()); 291 ASSERT(result); 292 USE(result); 293 } 294} 295 296 297bool VirtualMemory::IsReserved() { 298 return address_ != NULL; 299} 300 301 302void VirtualMemory::Reset() { 303 address_ = NULL; 304 size_ = 0; 305} 306 307 308bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 309 return CommitRegion(address, size, is_executable); 310} 311 312 313bool VirtualMemory::Uncommit(void* address, size_t size) { 314 return UncommitRegion(address, size); 315} 316 317 318bool VirtualMemory::Guard(void* address) { 319 OS::Guard(address, OS::CommitPageSize()); 320 return true; 321} 322 323 324void* VirtualMemory::ReserveRegion(size_t size) { 325 void* result = mmap(OS::GetRandomMmapAddr(), 326 size, 327 PROT_NONE, 328 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, 329 kMmapFd, 330 kMmapFdOffset); 331 332 if (result == MAP_FAILED) return NULL; 333 334 return result; 335} 336 337 338bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { 339 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 340 if (MAP_FAILED == mmap(base, 341 size, 342 prot, 343 MAP_PRIVATE | MAP_ANON | MAP_FIXED, 344 kMmapFd, 345 kMmapFdOffset)) { 346 return false; 347 } 348 349 UpdateAllocatedSpaceLimits(base, size); 350 return true; 351} 352 353 354bool VirtualMemory::UncommitRegion(void* base, size_t size) { 355 return mmap(base, 356 size, 357 PROT_NONE, 358 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, 359 kMmapFd, 360 kMmapFdOffset) != MAP_FAILED; 361} 362 363 364bool VirtualMemory::ReleaseRegion(void* base, size_t size) { 365 return munmap(base, size) == 0; 366} 367 368 369bool VirtualMemory::HasLazyCommits() { 370 // TODO(alph): implement for the platform. 371 return false; 372} 373 374 375class FreeBSDSemaphore : public Semaphore { 376 public: 377 explicit FreeBSDSemaphore(int count) { sem_init(&sem_, 0, count); } 378 virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); } 379 380 virtual void Wait(); 381 virtual bool Wait(int timeout); 382 virtual void Signal() { sem_post(&sem_); } 383 private: 384 sem_t sem_; 385}; 386 387 388void FreeBSDSemaphore::Wait() { 389 while (true) { 390 int result = sem_wait(&sem_); 391 if (result == 0) return; // Successfully got semaphore. 392 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 393 } 394} 395 396 397bool FreeBSDSemaphore::Wait(int timeout) { 398 const long kOneSecondMicros = 1000000; // NOLINT 399 400 // Split timeout into second and nanosecond parts. 401 struct timeval delta; 402 delta.tv_usec = timeout % kOneSecondMicros; 403 delta.tv_sec = timeout / kOneSecondMicros; 404 405 struct timeval current_time; 406 // Get the current time. 407 if (gettimeofday(¤t_time, NULL) == -1) { 408 return false; 409 } 410 411 // Calculate time for end of timeout. 412 struct timeval end_time; 413 timeradd(¤t_time, &delta, &end_time); 414 415 struct timespec ts; 416 TIMEVAL_TO_TIMESPEC(&end_time, &ts); 417 while (true) { 418 int result = sem_timedwait(&sem_, &ts); 419 if (result == 0) return true; // Successfully got semaphore. 420 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout. 421 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 422 } 423} 424 425 426Semaphore* OS::CreateSemaphore(int count) { 427 return new FreeBSDSemaphore(count); 428} 429 430 431void OS::SetUp() { 432 // Seed the random number generator. 433 // Convert the current time to a 64-bit integer first, before converting it 434 // to an unsigned. Going directly can cause an overflow and the seed to be 435 // set to all ones. The seed will be identical for different instances that 436 // call this setup code within the same millisecond. 437 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); 438 srandom(static_cast<unsigned int>(seed)); 439 limit_mutex = CreateMutex(); 440} 441 442 443void OS::TearDown() { 444 delete limit_mutex; 445} 446 447 448} } // namespace v8::internal 449