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