platform-freebsd.cc revision eab96aab0834f21954b5d6aa6366bcfb348ed811
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(&current_time, NULL) == -1) {
518    return false;
519  }
520
521  // Calculate time for end of timeout.
522  struct timeval end_time;
523  timeradd(&current_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