blob: 0002dd7620f0ee866753e0f48c5061ba8f7c6357 [file] [log] [blame]
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +00001// Copyright 2006-2009 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 OpenBSD 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 <stdlib.h>
38
39#include <sys/types.h> // mmap & munmap
40#include <sys/mman.h> // mmap & munmap
41#include <sys/stat.h> // open
42#include <sys/fcntl.h> // open
43#include <unistd.h> // getpagesize
44#include <execinfo.h> // backtrace, backtrace_symbols
45#include <strings.h> // index
46#include <errno.h>
47#include <stdarg.h>
48#include <limits.h>
49
50#undef MAP_TYPE
51
52#include "v8.h"
53
54#include "platform.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000055#include "vm-state-inl.h"
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000056
57
58namespace v8 {
59namespace internal {
60
61// 0 is never a valid thread id on OpenBSD 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
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +000087void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
88 __asm__ __volatile__("" : : : "memory");
89 *ptr = value;
90}
91
92
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000093uint64_t OS::CpuFeaturesImpliedByPlatform() {
94 return 0; // OpenBSD runs on anything.
95}
96
97
98int OS::ActivationFrameAlignment() {
99 // 16 byte alignment on OpenBSD
100 return 16;
101}
102
103
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000104const char* OS::LocalTimezone(double time) {
105 if (isnan(time)) return "";
106 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
107 struct tm* t = localtime(&tv);
108 if (NULL == t) return "";
109 return t->tm_zone;
110}
111
112
113double OS::LocalTimeOffset() {
114 time_t tv = time(NULL);
115 struct tm* t = localtime(&tv);
116 // tm_gmtoff includes any daylight savings offset, so subtract it.
117 return static_cast<double>(t->tm_gmtoff * msPerSecond -
118 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
119}
120
121
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000122// We keep the lowest and highest addresses mapped as a quick way of
123// determining that pointers are outside the heap (used mostly in assertions
124// and verification). The estimate is conservative, ie, not all addresses in
125// 'allocated' space are actually allocated to our heap. The range is
126// [lowest, highest), inclusive on the low and and exclusive on the high end.
127static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
128static void* highest_ever_allocated = reinterpret_cast<void*>(0);
129
130
131static void UpdateAllocatedSpaceLimits(void* address, int size) {
132 lowest_ever_allocated = Min(lowest_ever_allocated, address);
133 highest_ever_allocated =
134 Max(highest_ever_allocated,
135 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
136}
137
138
139bool OS::IsOutsideAllocatedSpace(void* address) {
140 return address < lowest_ever_allocated || address >= highest_ever_allocated;
141}
142
143
144size_t OS::AllocateAlignment() {
145 return getpagesize();
146}
147
148
149void* OS::Allocate(const size_t requested,
150 size_t* allocated,
151 bool executable) {
152 const size_t msize = RoundUp(requested, getpagesize());
153 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
154 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
155
156 if (mbase == MAP_FAILED) {
157 LOG(StringEvent("OS::Allocate", "mmap failed"));
158 return NULL;
159 }
160 *allocated = msize;
161 UpdateAllocatedSpaceLimits(mbase, msize);
162 return mbase;
163}
164
165
166void OS::Free(void* buf, const size_t length) {
167 int result = munmap(buf, length);
168 USE(result);
169 ASSERT(result == 0);
170}
171
172
173#ifdef ENABLE_HEAP_PROTECTION
174
175void OS::Protect(void* address, size_t size) {
176 UNIMPLEMENTED();
177}
178
179
180void OS::Unprotect(void* address, size_t size, bool is_executable) {
181 UNIMPLEMENTED();
182}
183
184#endif
185
186
187void OS::Sleep(int milliseconds) {
188 unsigned int ms = static_cast<unsigned int>(milliseconds);
189 usleep(1000 * ms);
190}
191
192
193void OS::Abort() {
194 // Redirect to std abort to signal abnormal program termination.
195 abort();
196}
197
198
199void OS::DebugBreak() {
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000200#if (defined(__arm__) || defined(__thumb__))
201# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000202 asm("bkpt 0");
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000203# endif
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000204#else
205 asm("int $3");
206#endif
207}
208
209
210class PosixMemoryMappedFile : public OS::MemoryMappedFile {
211 public:
212 PosixMemoryMappedFile(FILE* file, void* memory, int size)
213 : file_(file), memory_(memory), size_(size) { }
214 virtual ~PosixMemoryMappedFile();
215 virtual void* memory() { return memory_; }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000216 virtual int size() { return size_; }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000217 private:
218 FILE* file_;
219 void* memory_;
220 int size_;
221};
222
223
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000224OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
225 FILE* file = fopen(name, "w+");
226 if (file == NULL) return NULL;
227
228 fseek(file, 0, SEEK_END);
229 int size = ftell(file);
230
231 void* memory =
232 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
233 return new PosixMemoryMappedFile(file, memory, size);
234}
235
236
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000237OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
238 void* initial) {
239 FILE* file = fopen(name, "w+");
240 if (file == NULL) return NULL;
241 int result = fwrite(initial, size, 1, file);
242 if (result < 1) {
243 fclose(file);
244 return NULL;
245 }
246 void* memory =
247 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
248 return new PosixMemoryMappedFile(file, memory, size);
249}
250
251
252PosixMemoryMappedFile::~PosixMemoryMappedFile() {
253 if (memory_) munmap(memory_, size_);
254 fclose(file_);
255}
256
257
258#ifdef ENABLE_LOGGING_AND_PROFILING
259static unsigned StringToLong(char* buffer) {
260 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
261}
262#endif
263
264
265void OS::LogSharedLibraryAddresses() {
266#ifdef ENABLE_LOGGING_AND_PROFILING
267 static const int MAP_LENGTH = 1024;
268 int fd = open("/proc/self/maps", O_RDONLY);
269 if (fd < 0) return;
270 while (true) {
271 char addr_buffer[11];
272 addr_buffer[0] = '0';
273 addr_buffer[1] = 'x';
274 addr_buffer[10] = 0;
275 int result = read(fd, addr_buffer + 2, 8);
276 if (result < 8) break;
277 unsigned start = StringToLong(addr_buffer);
278 result = read(fd, addr_buffer + 2, 1);
279 if (result < 1) break;
280 if (addr_buffer[2] != '-') break;
281 result = read(fd, addr_buffer + 2, 8);
282 if (result < 8) break;
283 unsigned end = StringToLong(addr_buffer);
284 char buffer[MAP_LENGTH];
285 int bytes_read = -1;
286 do {
287 bytes_read++;
288 if (bytes_read >= MAP_LENGTH - 1)
289 break;
290 result = read(fd, buffer + bytes_read, 1);
291 if (result < 1) break;
292 } while (buffer[bytes_read] != '\n');
293 buffer[bytes_read] = 0;
294 // Ignore mappings that are not executable.
295 if (buffer[3] != 'x') continue;
296 char* start_of_path = index(buffer, '/');
297 // There may be no filename in this line. Skip to next.
298 if (start_of_path == NULL) continue;
299 buffer[bytes_read] = 0;
300 LOG(SharedLibraryEvent(start_of_path, start, end));
301 }
302 close(fd);
303#endif
304}
305
306
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000307void OS::SignalCodeMovingGC() {
308}
309
310
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000311int OS::StackWalk(Vector<OS::StackFrame> frames) {
312 UNIMPLEMENTED();
313 return 1;
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,
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) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000404 set_name("v8:<unknown>");
405}
406
407
408Thread::Thread(const char* name) : ThreadHandle(ThreadHandle::INVALID) {
409 set_name(name);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000410}
411
412
413Thread::~Thread() {
414}
415
416
417static void* ThreadEntry(void* arg) {
418 Thread* thread = reinterpret_cast<Thread*>(arg);
419 // This is also initialized by the first argument to pthread_create() but we
420 // don't know which thread will run first (the original thread or the new
421 // one) so we initialize it here too.
422 thread->thread_handle_data()->thread_ = pthread_self();
423 ASSERT(thread->IsValid());
424 thread->Run();
425 return NULL;
426}
427
428
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000429void Thread::set_name(const char* name) {
430 strncpy(name_, name, sizeof(name_));
431 name_[sizeof(name_) - 1] = '\0';
432}
433
434
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000435void Thread::Start() {
436 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
437 ASSERT(IsValid());
438}
439
440
441void Thread::Join() {
442 pthread_join(thread_handle_data()->thread_, NULL);
443}
444
445
446Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
447 pthread_key_t key;
448 int result = pthread_key_create(&key, NULL);
449 USE(result);
450 ASSERT(result == 0);
451 return static_cast<LocalStorageKey>(key);
452}
453
454
455void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
456 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
457 int result = pthread_key_delete(pthread_key);
458 USE(result);
459 ASSERT(result == 0);
460}
461
462
463void* Thread::GetThreadLocal(LocalStorageKey key) {
464 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
465 return pthread_getspecific(pthread_key);
466}
467
468
469void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
470 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
471 pthread_setspecific(pthread_key, value);
472}
473
474
475void Thread::YieldCPU() {
476 sched_yield();
477}
478
479
480class OpenBSDMutex : public Mutex {
481 public:
482
483 OpenBSDMutex() {
484 pthread_mutexattr_t attrs;
485 int result = pthread_mutexattr_init(&attrs);
486 ASSERT(result == 0);
487 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
488 ASSERT(result == 0);
489 result = pthread_mutex_init(&mutex_, &attrs);
490 ASSERT(result == 0);
491 }
492
493 virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); }
494
495 virtual int Lock() {
496 int result = pthread_mutex_lock(&mutex_);
497 return result;
498 }
499
500 virtual int Unlock() {
501 int result = pthread_mutex_unlock(&mutex_);
502 return result;
503 }
504
505 private:
506 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
507};
508
509
510Mutex* OS::CreateMutex() {
511 return new OpenBSDMutex();
512}
513
514
515class OpenBSDSemaphore : public Semaphore {
516 public:
517 explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
518 virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
519
520 virtual void Wait();
521 virtual bool Wait(int timeout);
522 virtual void Signal() { sem_post(&sem_); }
523 private:
524 sem_t sem_;
525};
526
527
528void OpenBSDSemaphore::Wait() {
529 while (true) {
530 int result = sem_wait(&sem_);
531 if (result == 0) return; // Successfully got semaphore.
532 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
533 }
534}
535
536
537bool OpenBSDSemaphore::Wait(int timeout) {
538 const long kOneSecondMicros = 1000000; // NOLINT
539
540 // Split timeout into second and nanosecond parts.
541 struct timeval delta;
542 delta.tv_usec = timeout % kOneSecondMicros;
543 delta.tv_sec = timeout / kOneSecondMicros;
544
545 struct timeval current_time;
546 // Get the current time.
547 if (gettimeofday(&current_time, NULL) == -1) {
548 return false;
549 }
550
551 // Calculate time for end of timeout.
552 struct timeval end_time;
553 timeradd(&current_time, &delta, &end_time);
554
555 struct timespec ts;
556 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
557 while (true) {
558 int result = sem_trywait(&sem_);
559 if (result == 0) return true; // Successfully got semaphore.
560 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
561 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
562 }
563}
564
565
566Semaphore* OS::CreateSemaphore(int count) {
567 return new OpenBSDSemaphore(count);
568}
569
570
571#ifdef ENABLE_LOGGING_AND_PROFILING
572
573static Sampler* active_sampler_ = NULL;
574
575static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
576 USE(info);
577 if (signal != SIGPROF) return;
578 if (active_sampler_ == NULL) return;
579
580 TickSample sample;
581
582 // We always sample the VM state.
ager@chromium.org357bf652010-04-12 11:30:10 +0000583 sample.state = VMState::current_state();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000584
585 active_sampler_->Tick(&sample);
586}
587
588
589class Sampler::PlatformData : public Malloced {
590 public:
591 PlatformData() {
592 signal_handler_installed_ = false;
593 }
594
595 bool signal_handler_installed_;
596 struct sigaction old_signal_handler_;
597 struct itimerval old_timer_value_;
598};
599
600
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000601Sampler::Sampler(int interval)
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000602 : interval_(interval),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000603 profiling_(false),
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000604 active_(false),
605 samples_taken_(0) {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000606 data_ = new PlatformData();
607}
608
609
610Sampler::~Sampler() {
611 delete data_;
612}
613
614
615void Sampler::Start() {
616 // There can only be one active sampler at the time on POSIX
617 // platforms.
618 if (active_sampler_ != NULL) return;
619
620 // Request profiling signals.
621 struct sigaction sa;
622 sa.sa_sigaction = ProfilerSignalHandler;
623 sigemptyset(&sa.sa_mask);
624 sa.sa_flags = SA_SIGINFO;
625 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
626 data_->signal_handler_installed_ = true;
627
628 // Set the itimer to generate a tick for each interval.
629 itimerval itimer;
630 itimer.it_interval.tv_sec = interval_ / 1000;
631 itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
632 itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
633 itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
634 setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
635
636 // Set this sampler as the active sampler.
637 active_sampler_ = this;
638 active_ = true;
639}
640
641
642void Sampler::Stop() {
643 // Restore old signal handler
644 if (data_->signal_handler_installed_) {
645 setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
646 sigaction(SIGPROF, &data_->old_signal_handler_, 0);
647 data_->signal_handler_installed_ = false;
648 }
649
650 // This sampler is no longer the active sampler.
651 active_sampler_ = NULL;
652 active_ = false;
653}
654
655#endif // ENABLE_LOGGING_AND_PROFILING
656
657} } // namespace v8::internal