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