Index: src/platform-openbsd.cc |
=================================================================== |
--- src/platform-openbsd.cc (revision 9912) |
+++ src/platform-openbsd.cc (working copy) |
@@ -1,4 +1,4 @@ |
-// Copyright 2006-2011 the V8 project authors. All rights reserved. |
+// Copyright 2011 the V8 project authors. All rights reserved. |
// Redistribution and use in source and binary forms, with or without |
// modification, are permitted provided that the following conditions are |
// met: |
@@ -33,79 +33,99 @@ |
#include <signal.h> |
#include <sys/time.h> |
#include <sys/resource.h> |
+#include <sys/syscall.h> |
#include <sys/types.h> |
#include <stdlib.h> |
#include <sys/types.h> // mmap & munmap |
#include <sys/mman.h> // mmap & munmap |
#include <sys/stat.h> // open |
-#include <sys/fcntl.h> // open |
-#include <unistd.h> // getpagesize |
+#include <fcntl.h> // open |
+#include <unistd.h> // sysconf |
#include <execinfo.h> // backtrace, backtrace_symbols |
#include <strings.h> // index |
#include <errno.h> |
#include <stdarg.h> |
-#include <limits.h> |
#undef MAP_TYPE |
#include "v8.h" |
-#include "v8threads.h" |
#include "platform.h" |
+#include "v8threads.h" |
#include "vm-state-inl.h" |
namespace v8 { |
namespace internal { |
-// 0 is never a valid thread id on OpenBSD since tids and pids share a |
-// name space and pid 0 is used to kill the group (see man 2 kill). |
+// 0 is never a valid thread id on Linux and OpenBSD since tids and pids share a |
+// name space and pid 0 is reserved (see man 2 kill). |
static const pthread_t kNoThread = (pthread_t) 0; |
double ceiling(double x) { |
- // Correct as on OS X |
- if (-1.0 < x && x < 0.0) { |
- return -0.0; |
- } else { |
- return ceil(x); |
- } |
+ return ceil(x); |
} |
static Mutex* limit_mutex = NULL; |
+static void* GetRandomMmapAddr() { |
+ Isolate* isolate = Isolate::UncheckedCurrent(); |
+ // Note that the current isolate isn't set up in a call path via |
+ // CpuFeatures::Probe. We don't care about randomization in this case because |
+ // the code page is immediately freed. |
+ if (isolate != NULL) { |
+#ifdef V8_TARGET_ARCH_X64 |
+ uint64_t rnd1 = V8::RandomPrivate(isolate); |
+ uint64_t rnd2 = V8::RandomPrivate(isolate); |
+ uint64_t raw_addr = (rnd1 << 32) ^ rnd2; |
+ // Currently available CPUs have 48 bits of virtual addressing. Truncate |
+ // the hint address to 46 bits to give the kernel a fighting chance of |
+ // fulfilling our placement request. |
+ raw_addr &= V8_UINT64_C(0x3ffffffff000); |
+#else |
+ uint32_t raw_addr = V8::RandomPrivate(isolate); |
+ // The range 0x20000000 - 0x60000000 is relatively unpopulated across a |
+ // variety of ASLR modes (PAE kernel, NX compat mode, etc). |
+ raw_addr &= 0x3ffff000; |
+ raw_addr += 0x20000000; |
+#endif |
+ return reinterpret_cast<void*>(raw_addr); |
+ } |
+ return NULL; |
+} |
+ |
+ |
void OS::Setup() { |
- // Seed the random number generator. |
- // Convert the current time to a 64-bit integer first, before converting it |
- // to an unsigned. Going directly can cause an overflow and the seed to be |
- // set to all ones. The seed will be identical for different instances that |
- // call this setup code within the same millisecond. |
- uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); |
+ // Seed the random number generator. We preserve microsecond resolution. |
+ uint64_t seed = Ticks() ^ (getpid() << 16); |
srandom(static_cast<unsigned int>(seed)); |
limit_mutex = CreateMutex(); |
} |
-void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { |
- __asm__ __volatile__("" : : : "memory"); |
- *ptr = value; |
-} |
- |
- |
uint64_t OS::CpuFeaturesImpliedByPlatform() { |
- return 0; // OpenBSD runs on anything. |
+ return 0; |
} |
int OS::ActivationFrameAlignment() { |
- // 16 byte alignment on OpenBSD |
+ // With gcc 4.4 the tree vectorization optimizer can generate code |
+ // that requires 16 byte alignment such as movdqa on x86. |
return 16; |
} |
+void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { |
+ __asm__ __volatile__("" : : : "memory"); |
+ // An x86 store acts as a release barrier. |
+ *ptr = value; |
+} |
+ |
+ |
const char* OS::LocalTimezone(double time) { |
if (isnan(time)) return ""; |
time_t tv = static_cast<time_t>(floor(time/msPerSecond)); |
@@ -150,19 +170,20 @@ |
size_t OS::AllocateAlignment() { |
- return getpagesize(); |
+ return sysconf(_SC_PAGESIZE); |
} |
void* OS::Allocate(const size_t requested, |
size_t* allocated, |
- bool executable) { |
- const size_t msize = RoundUp(requested, getpagesize()); |
- int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); |
- void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0); |
- |
+ bool is_executable) { |
+ const size_t msize = RoundUp(requested, AllocateAlignment()); |
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); |
+ void* addr = GetRandomMmapAddr(); |
+ void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0); |
if (mbase == MAP_FAILED) { |
- LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed")); |
+ LOG(i::Isolate::Current(), |
+ StringEvent("OS::Allocate", "mmap failed")); |
return NULL; |
} |
*allocated = msize; |
@@ -171,9 +192,9 @@ |
} |
-void OS::Free(void* buf, const size_t length) { |
+void OS::Free(void* address, const size_t size) { |
// TODO(1240712): munmap has a return value which is ignored here. |
- int result = munmap(buf, length); |
+ int result = munmap(address, size); |
USE(result); |
ASSERT(result == 0); |
} |
@@ -192,13 +213,7 @@ |
void OS::DebugBreak() { |
-#if (defined(__arm__) || defined(__thumb__)) |
-# if defined(CAN_USE_ARMV5_INSTRUCTIONS) |
- asm("bkpt 0"); |
-# endif |
-#else |
asm("int $3"); |
-#endif |
} |
@@ -250,56 +265,90 @@ |
} |
-static unsigned StringToLong(char* buffer) { |
- return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT |
-} |
+void OS::LogSharedLibraryAddresses() { |
+ // This function assumes that the layout of the file is as follows: |
+ // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] |
+ // If we encounter an unexpected situation we abort scanning further entries. |
+ FILE* fp = fopen("/proc/self/maps", "r"); |
+ if (fp == NULL) return; |
+ // Allocate enough room to be able to store a full file name. |
+ const int kLibNameLen = FILENAME_MAX + 1; |
+ char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); |
-void OS::LogSharedLibraryAddresses() { |
- static const int MAP_LENGTH = 1024; |
- int fd = open("/proc/self/maps", O_RDONLY); |
- if (fd < 0) return; |
+ i::Isolate* isolate = ISOLATE; |
+ // This loop will terminate once the scanning hits an EOF. |
while (true) { |
- char addr_buffer[11]; |
- addr_buffer[0] = '0'; |
- addr_buffer[1] = 'x'; |
- addr_buffer[10] = 0; |
- int result = read(fd, addr_buffer + 2, 8); |
- if (result < 8) break; |
- unsigned start = StringToLong(addr_buffer); |
- result = read(fd, addr_buffer + 2, 1); |
- if (result < 1) break; |
- if (addr_buffer[2] != '-') break; |
- result = read(fd, addr_buffer + 2, 8); |
- if (result < 8) break; |
- unsigned end = StringToLong(addr_buffer); |
- char buffer[MAP_LENGTH]; |
- int bytes_read = -1; |
- do { |
- bytes_read++; |
- if (bytes_read >= MAP_LENGTH - 1) |
- break; |
- result = read(fd, buffer + bytes_read, 1); |
- if (result < 1) break; |
- } while (buffer[bytes_read] != '\n'); |
- buffer[bytes_read] = 0; |
- // Ignore mappings that are not executable. |
- if (buffer[3] != 'x') continue; |
- char* start_of_path = index(buffer, '/'); |
- // There may be no filename in this line. Skip to next. |
- if (start_of_path == NULL) continue; |
- buffer[bytes_read] = 0; |
- LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end)); |
+ uintptr_t start, end; |
+ char attr_r, attr_w, attr_x, attr_p; |
+ // Parse the addresses and permission bits at the beginning of the line. |
+ if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; |
+ if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; |
+ |
+ int c; |
+ if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { |
+ // Found a read-only executable entry. Skip characters until we reach |
+ // the beginning of the filename or the end of the line. |
+ do { |
+ c = getc(fp); |
+ } while ((c != EOF) && (c != '\n') && (c != '/')); |
+ if (c == EOF) break; // EOF: Was unexpected, just exit. |
+ |
+ // Process the filename if found. |
+ if (c == '/') { |
+ ungetc(c, fp); // Push the '/' back into the stream to be read below. |
+ |
+ // Read to the end of the line. Exit if the read fails. |
+ if (fgets(lib_name, kLibNameLen, fp) == NULL) break; |
+ |
+ // Drop the newline character read by fgets. We do not need to check |
+ // for a zero-length string because we know that we at least read the |
+ // '/' character. |
+ lib_name[strlen(lib_name) - 1] = '\0'; |
+ } else { |
+ // No library name found, just record the raw address range. |
+ snprintf(lib_name, kLibNameLen, |
+ "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); |
+ } |
+ LOG(isolate, SharedLibraryEvent(lib_name, start, end)); |
+ } else { |
+ // Entry not describing executable data. Skip to end of line to setup |
+ // reading the next entry. |
+ do { |
+ c = getc(fp); |
+ } while ((c != EOF) && (c != '\n')); |
+ if (c == EOF) break; |
+ } |
} |
- close(fd); |
+ free(lib_name); |
+ fclose(fp); |
} |
+static const char kGCFakeMmap[] = "/tmp/__v8_gc__"; |
+ |
+ |
void OS::SignalCodeMovingGC() { |
+ // Support for ll_prof.py. |
+ // |
+ // The Linux profiler built into the kernel logs all mmap's with |
+ // PROT_EXEC so that analysis tools can properly attribute ticks. We |
+ // do a mmap with a name known by ll_prof.py and immediately munmap |
+ // it. This injects a GC marker into the stream of events generated |
+ // by the kernel and allows us to synchronize V8 code log and the |
+ // kernel log. |
+ int size = sysconf(_SC_PAGESIZE); |
+ FILE* f = fopen(kGCFakeMmap, "w+"); |
+ void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE, |
+ fileno(f), 0); |
+ ASSERT(addr != MAP_FAILED); |
+ OS::Free(addr, size); |
+ fclose(f); |
} |
int OS::StackWalk(Vector<OS::StackFrame> frames) { |
+ // backtrace is a glibc extension. |
int frames_size = frames.length(); |
ScopedVector<void*> addresses(frames_size); |
@@ -331,63 +380,145 @@ |
static const int kMmapFd = -1; |
static const int kMmapFdOffset = 0; |
+VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { } |
VirtualMemory::VirtualMemory(size_t size) { |
- address_ = mmap(NULL, size, PROT_NONE, |
- MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, |
- kMmapFd, kMmapFdOffset); |
+ address_ = ReserveRegion(size); |
size_ = size; |
} |
+VirtualMemory::VirtualMemory(size_t size, size_t alignment) |
+ : address_(NULL), size_(0) { |
+ ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment()))); |
+ size_t request_size = RoundUp(size + alignment, |
+ static_cast<intptr_t>(OS::AllocateAlignment())); |
+ void* reservation = mmap(GetRandomMmapAddr(), |
+ request_size, |
+ PROT_NONE, |
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, |
+ kMmapFd, |
+ kMmapFdOffset); |
+ if (reservation == MAP_FAILED) return; |
+ |
+ Address base = static_cast<Address>(reservation); |
+ Address aligned_base = RoundUp(base, alignment); |
+ ASSERT_LE(base, aligned_base); |
+ |
+ // Unmap extra memory reserved before and after the desired block. |
+ if (aligned_base != base) { |
+ size_t prefix_size = static_cast<size_t>(aligned_base - base); |
+ OS::Free(base, prefix_size); |
+ request_size -= prefix_size; |
+ } |
+ |
+ size_t aligned_size = RoundUp(size, OS::AllocateAlignment()); |
+ ASSERT_LE(aligned_size, request_size); |
+ |
+ if (aligned_size != request_size) { |
+ size_t suffix_size = request_size - aligned_size; |
+ OS::Free(aligned_base + aligned_size, suffix_size); |
+ request_size -= suffix_size; |
+ } |
+ |
+ ASSERT(aligned_size == request_size); |
+ |
+ address_ = static_cast<void*>(aligned_base); |
+ size_ = aligned_size; |
+} |
+ |
+ |
VirtualMemory::~VirtualMemory() { |
if (IsReserved()) { |
- OS::Free(address(), size()); |
- address_ = MAP_FAILED |
+ bool result = ReleaseRegion(address(), size()); |
+ ASSERT(result); |
+ USE(result); |
} |
} |
bool VirtualMemory::IsReserved() { |
- return address_ != MAP_FAILED; |
+ return address_ != NULL; |
} |
-bool VirtualMemory::Commit(void* address, size_t size, bool executable) { |
- int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0); |
- if (MAP_FAILED == mmap(address, size, prot, |
+void VirtualMemory::Reset() { |
+ address_ = NULL; |
+ size_ = 0; |
+} |
+ |
+ |
+bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { |
+ return CommitRegion(address, size, is_executable); |
+} |
+ |
+ |
+bool VirtualMemory::Uncommit(void* address, size_t size) { |
+ return UncommitRegion(address, size); |
+} |
+ |
+ |
+void* VirtualMemory::ReserveRegion(size_t size) { |
+ void* result = mmap(GetRandomMmapAddr(), |
+ size, |
+ PROT_NONE, |
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, |
+ kMmapFd, |
+ kMmapFdOffset); |
+ |
+ if (result == MAP_FAILED) return NULL; |
+ |
+ return result; |
+} |
+ |
+ |
+bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) { |
+ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); |
+ if (MAP_FAILED == mmap(base, |
+ size, |
+ prot, |
MAP_PRIVATE | MAP_ANON | MAP_FIXED, |
- kMmapFd, kMmapFdOffset)) { |
+ kMmapFd, |
+ kMmapFdOffset)) { |
return false; |
} |
- UpdateAllocatedSpaceLimits(address, size); |
+ UpdateAllocatedSpaceLimits(base, size); |
return true; |
} |
-bool VirtualMemory::Uncommit(void* address, size_t size) { |
- return mmap(address, size, PROT_NONE, |
+bool VirtualMemory::UncommitRegion(void* base, size_t size) { |
+ return mmap(base, |
+ size, |
+ PROT_NONE, |
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED, |
- kMmapFd, kMmapFdOffset) != MAP_FAILED; |
+ kMmapFd, |
+ kMmapFdOffset) != MAP_FAILED; |
} |
+bool VirtualMemory::ReleaseRegion(void* base, size_t size) { |
+ return munmap(base, size) == 0; |
+} |
+ |
+ |
class Thread::PlatformData : public Malloced { |
public: |
+ PlatformData() : thread_(kNoThread) {} |
+ |
pthread_t thread_; // Thread handle for pthread. |
}; |
- |
Thread::Thread(const Options& options) |
- : data_(new PlatformData), |
+ : data_(new PlatformData()), |
stack_size_(options.stack_size) { |
set_name(options.name); |
} |
Thread::Thread(const char* name) |
- : data_(new PlatformData), |
+ : data_(new PlatformData()), |
stack_size_(0) { |
set_name(name); |
} |
@@ -403,6 +534,11 @@ |
// This is also initialized by the first argument to pthread_create() but we |
// don't know which thread will run first (the original thread or the new |
// one) so we initialize it here too. |
+#ifdef PR_SET_NAME |
+ prctl(PR_SET_NAME, |
+ reinterpret_cast<unsigned long>(thread->name()), // NOLINT |
+ 0, 0, 0); |
+#endif |
thread->data()->thread_ = pthread_self(); |
ASSERT(thread->data()->thread_ != kNoThread); |
thread->Run(); |
@@ -478,6 +614,7 @@ |
ASSERT(result == 0); |
result = pthread_mutex_init(&mutex_, &attrs); |
ASSERT(result == 0); |
+ USE(result); |
} |
virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); } |
@@ -534,6 +671,14 @@ |
} |
+#ifndef TIMEVAL_TO_TIMESPEC |
+#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ |
+ (ts)->tv_sec = (tv)->tv_sec; \ |
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \ |
+} while (false) |
+#endif |
+ |
+ |
bool OpenBSDSemaphore::Wait(int timeout) { |
const long kOneSecondMicros = 1000000; // NOLINT |
@@ -567,29 +712,15 @@ |
} |
} |
- |
Semaphore* OS::CreateSemaphore(int count) { |
return new OpenBSDSemaphore(count); |
} |
static pthread_t GetThreadID() { |
- pthread_t thread_id = pthread_self(); |
- return thread_id; |
+ return pthread_self(); |
} |
- |
-class Sampler::PlatformData : public Malloced { |
- public: |
- PlatformData() : vm_tid_(GetThreadID()) {} |
- |
- pthread_t vm_tid() const { return vm_tid_; } |
- |
- private: |
- pthread_t vm_tid_; |
-}; |
- |
- |
static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { |
USE(info); |
if (signal != SIGPROF) return; |
@@ -621,16 +752,23 @@ |
sample->pc = reinterpret_cast<Address>(ucontext->sc_rip); |
sample->sp = reinterpret_cast<Address>(ucontext->sc_rsp); |
sample->fp = reinterpret_cast<Address>(ucontext->sc_rbp); |
-#elif V8_HOST_ARCH_ARM |
- sample->pc = reinterpret_cast<Address>(ucontext->sc_r15); |
- sample->sp = reinterpret_cast<Address>(ucontext->sc_r13); |
- sample->fp = reinterpret_cast<Address>(ucontext->sc_r11); |
#endif |
sampler->SampleStack(sample); |
sampler->Tick(sample); |
} |
+class Sampler::PlatformData : public Malloced { |
+ public: |
+ PlatformData() : vm_tid_(GetThreadID()) {} |
+ |
+ pthread_t vm_tid() const { return vm_tid_; } |
+ |
+ private: |
+ pthread_t vm_tid_; |
+}; |
+ |
+ |
class SignalSender : public Thread { |
public: |
enum SleepInterval { |
@@ -640,21 +778,31 @@ |
explicit SignalSender(int interval) |
: Thread("SignalSender"), |
+ vm_tgid_(getpid()), |
interval_(interval) {} |
+ static void InstallSignalHandler() { |
+ struct sigaction sa; |
+ sa.sa_sigaction = ProfilerSignalHandler; |
+ sigemptyset(&sa.sa_mask); |
+ sa.sa_flags = SA_RESTART | SA_SIGINFO; |
+ signal_handler_installed_ = |
+ (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); |
+ } |
+ |
+ static void RestoreSignalHandler() { |
+ if (signal_handler_installed_) { |
+ sigaction(SIGPROF, &old_signal_handler_, 0); |
+ signal_handler_installed_ = false; |
+ } |
+ } |
+ |
static void AddActiveSampler(Sampler* sampler) { |
ScopedLock lock(mutex_); |
SamplerRegistry::AddActiveSampler(sampler); |
if (instance_ == NULL) { |
- // Install a signal handler. |
- struct sigaction sa; |
- sa.sa_sigaction = ProfilerSignalHandler; |
- sigemptyset(&sa.sa_mask); |
- sa.sa_flags = SA_RESTART | SA_SIGINFO; |
- signal_handler_installed_ = |
- (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); |
- |
- // Start a thread that sends SIGPROF signal to VM threads. |
+ // Start a thread that will send SIGPROF signal to VM threads, |
+ // when CPU profiling will be enabled. |
instance_ = new SignalSender(sampler->interval()); |
instance_->Start(); |
} else { |
@@ -669,12 +817,7 @@ |
RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_); |
delete instance_; |
instance_ = NULL; |
- |
- // Restore the old signal handler. |
- if (signal_handler_installed_) { |
- sigaction(SIGPROF, &old_signal_handler_, 0); |
- signal_handler_installed_ = false; |
- } |
+ RestoreSignalHandler(); |
} |
} |
@@ -686,6 +829,11 @@ |
bool cpu_profiling_enabled = |
(state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS); |
bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled(); |
+ if (cpu_profiling_enabled && !signal_handler_installed_) { |
+ InstallSignalHandler(); |
+ } else if (!cpu_profiling_enabled && signal_handler_installed_) { |
+ RestoreSignalHandler(); |
+ } |
// When CPU profiling is enabled both JavaScript and C++ code is |
// profiled. We must not suspend. |
if (!cpu_profiling_enabled) { |
@@ -752,6 +900,7 @@ |
USE(result); |
} |
+ const int vm_tgid_; |
const int interval_; |
RuntimeProfilerRateLimiter rate_limiter_; |
@@ -764,6 +913,7 @@ |
DISALLOW_COPY_AND_ASSIGN(SignalSender); |
}; |
+ |
Mutex* SignalSender::mutex_ = OS::CreateMutex(); |
SignalSender* SignalSender::instance_ = NULL; |
struct sigaction SignalSender::old_signal_handler_; |