blob: b698d16b9a480443f3d886b3c5a0a3ed6e21963f [file] [log] [blame]
Steve Blockd0582a62009-12-15 09:54:21 +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"
Ben Murdochb0fe1622011-05-05 13:52:32 +010055#include "vm-state-inl.h"
Steve Blockd0582a62009-12-15 09:54:21 +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
Ben Murdoch3bec4d22010-07-22 14:51:16 +010087void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
88 __asm__ __volatile__("" : : : "memory");
89 *ptr = value;
90}
91
92
Steve Blockd0582a62009-12-15 09:54:21 +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
Leon Clarked91b9f72010-01-27 17:25:45 +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
Steve Blockd0582a62009-12-15 09:54:21 +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() {
Iain Merrick75681382010-08-19 15:07:18 +0100200#if (defined(__arm__) || defined(__thumb__))
201# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
Steve Blockd0582a62009-12-15 09:54:21 +0000202 asm("bkpt 0");
Iain Merrick75681382010-08-19 15:07:18 +0100203# endif
Steve Blockd0582a62009-12-15 09:54:21 +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_; }
216 private:
217 FILE* file_;
218 void* memory_;
219 int size_;
220};
221
222
223OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
224 void* initial) {
225 FILE* file = fopen(name, "w+");
226 if (file == NULL) return NULL;
227 int result = fwrite(initial, size, 1, file);
228 if (result < 1) {
229 fclose(file);
230 return NULL;
231 }
232 void* memory =
233 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
234 return new PosixMemoryMappedFile(file, memory, size);
235}
236
237
238PosixMemoryMappedFile::~PosixMemoryMappedFile() {
239 if (memory_) munmap(memory_, size_);
240 fclose(file_);
241}
242
243
244#ifdef ENABLE_LOGGING_AND_PROFILING
245static unsigned StringToLong(char* buffer) {
246 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
247}
248#endif
249
250
251void OS::LogSharedLibraryAddresses() {
252#ifdef ENABLE_LOGGING_AND_PROFILING
253 static const int MAP_LENGTH = 1024;
254 int fd = open("/proc/self/maps", O_RDONLY);
255 if (fd < 0) return;
256 while (true) {
257 char addr_buffer[11];
258 addr_buffer[0] = '0';
259 addr_buffer[1] = 'x';
260 addr_buffer[10] = 0;
261 int result = read(fd, addr_buffer + 2, 8);
262 if (result < 8) break;
263 unsigned start = StringToLong(addr_buffer);
264 result = read(fd, addr_buffer + 2, 1);
265 if (result < 1) break;
266 if (addr_buffer[2] != '-') break;
267 result = read(fd, addr_buffer + 2, 8);
268 if (result < 8) break;
269 unsigned end = StringToLong(addr_buffer);
270 char buffer[MAP_LENGTH];
271 int bytes_read = -1;
272 do {
273 bytes_read++;
274 if (bytes_read >= MAP_LENGTH - 1)
275 break;
276 result = read(fd, buffer + bytes_read, 1);
277 if (result < 1) break;
278 } while (buffer[bytes_read] != '\n');
279 buffer[bytes_read] = 0;
280 // Ignore mappings that are not executable.
281 if (buffer[3] != 'x') continue;
282 char* start_of_path = index(buffer, '/');
283 // There may be no filename in this line. Skip to next.
284 if (start_of_path == NULL) continue;
285 buffer[bytes_read] = 0;
286 LOG(SharedLibraryEvent(start_of_path, start, end));
287 }
288 close(fd);
289#endif
290}
291
292
Ben Murdochf87a2032010-10-22 12:50:53 +0100293void OS::SignalCodeMovingGC() {
294}
295
296
Steve Blockd0582a62009-12-15 09:54:21 +0000297int OS::StackWalk(Vector<OS::StackFrame> frames) {
298 UNIMPLEMENTED();
299 return 1;
300}
301
302
303// Constants used for mmap.
304static const int kMmapFd = -1;
305static const int kMmapFdOffset = 0;
306
307
308VirtualMemory::VirtualMemory(size_t size) {
309 address_ = mmap(NULL, size, PROT_NONE,
310 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
311 kMmapFd, kMmapFdOffset);
312 size_ = size;
313}
314
315
316VirtualMemory::~VirtualMemory() {
317 if (IsReserved()) {
318 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
319 }
320}
321
322
323bool VirtualMemory::IsReserved() {
324 return address_ != MAP_FAILED;
325}
326
327
328bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
329 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
330 if (MAP_FAILED == mmap(address, size, prot,
331 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
332 kMmapFd, kMmapFdOffset)) {
333 return false;
334 }
335
336 UpdateAllocatedSpaceLimits(address, size);
337 return true;
338}
339
340
341bool VirtualMemory::Uncommit(void* address, size_t size) {
342 return mmap(address, size, PROT_NONE,
343 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
344 kMmapFd, kMmapFdOffset) != MAP_FAILED;
345}
346
347
348class ThreadHandle::PlatformData : public Malloced {
349 public:
350 explicit PlatformData(ThreadHandle::Kind kind) {
351 Initialize(kind);
352 }
353
354 void Initialize(ThreadHandle::Kind kind) {
355 switch (kind) {
356 case ThreadHandle::SELF: thread_ = pthread_self(); break;
357 case ThreadHandle::INVALID: thread_ = kNoThread; break;
358 }
359 }
360 pthread_t thread_; // Thread handle for pthread.
361};
362
363
364ThreadHandle::ThreadHandle(Kind kind) {
365 data_ = new PlatformData(kind);
366}
367
368
369void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
370 data_->Initialize(kind);
371}
372
373
374ThreadHandle::~ThreadHandle() {
375 delete data_;
376}
377
378
379bool ThreadHandle::IsSelf() const {
380 return pthread_equal(data_->thread_, pthread_self());
381}
382
383
384bool ThreadHandle::IsValid() const {
385 return data_->thread_ != kNoThread;
386}
387
388
389Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
390}
391
392
393Thread::~Thread() {
394}
395
396
397static void* ThreadEntry(void* arg) {
398 Thread* thread = reinterpret_cast<Thread*>(arg);
399 // This is also initialized by the first argument to pthread_create() but we
400 // don't know which thread will run first (the original thread or the new
401 // one) so we initialize it here too.
402 thread->thread_handle_data()->thread_ = pthread_self();
403 ASSERT(thread->IsValid());
404 thread->Run();
405 return NULL;
406}
407
408
409void Thread::Start() {
410 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
411 ASSERT(IsValid());
412}
413
414
415void Thread::Join() {
416 pthread_join(thread_handle_data()->thread_, NULL);
417}
418
419
420Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
421 pthread_key_t key;
422 int result = pthread_key_create(&key, NULL);
423 USE(result);
424 ASSERT(result == 0);
425 return static_cast<LocalStorageKey>(key);
426}
427
428
429void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
430 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
431 int result = pthread_key_delete(pthread_key);
432 USE(result);
433 ASSERT(result == 0);
434}
435
436
437void* Thread::GetThreadLocal(LocalStorageKey key) {
438 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
439 return pthread_getspecific(pthread_key);
440}
441
442
443void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
444 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
445 pthread_setspecific(pthread_key, value);
446}
447
448
449void Thread::YieldCPU() {
450 sched_yield();
451}
452
453
454class OpenBSDMutex : public Mutex {
455 public:
456
457 OpenBSDMutex() {
458 pthread_mutexattr_t attrs;
459 int result = pthread_mutexattr_init(&attrs);
460 ASSERT(result == 0);
461 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
462 ASSERT(result == 0);
463 result = pthread_mutex_init(&mutex_, &attrs);
464 ASSERT(result == 0);
465 }
466
467 virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); }
468
469 virtual int Lock() {
470 int result = pthread_mutex_lock(&mutex_);
471 return result;
472 }
473
474 virtual int Unlock() {
475 int result = pthread_mutex_unlock(&mutex_);
476 return result;
477 }
478
479 private:
480 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
481};
482
483
484Mutex* OS::CreateMutex() {
485 return new OpenBSDMutex();
486}
487
488
489class OpenBSDSemaphore : public Semaphore {
490 public:
491 explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
492 virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
493
494 virtual void Wait();
495 virtual bool Wait(int timeout);
496 virtual void Signal() { sem_post(&sem_); }
497 private:
498 sem_t sem_;
499};
500
501
502void OpenBSDSemaphore::Wait() {
503 while (true) {
504 int result = sem_wait(&sem_);
505 if (result == 0) return; // Successfully got semaphore.
506 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
507 }
508}
509
510
511bool OpenBSDSemaphore::Wait(int timeout) {
512 const long kOneSecondMicros = 1000000; // NOLINT
513
514 // Split timeout into second and nanosecond parts.
515 struct timeval delta;
516 delta.tv_usec = timeout % kOneSecondMicros;
517 delta.tv_sec = timeout / kOneSecondMicros;
518
519 struct timeval current_time;
520 // Get the current time.
521 if (gettimeofday(&current_time, NULL) == -1) {
522 return false;
523 }
524
525 // Calculate time for end of timeout.
526 struct timeval end_time;
527 timeradd(&current_time, &delta, &end_time);
528
529 struct timespec ts;
530 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
531 while (true) {
532 int result = sem_trywait(&sem_);
533 if (result == 0) return true; // Successfully got semaphore.
534 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
535 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
536 }
537}
538
539
540Semaphore* OS::CreateSemaphore(int count) {
541 return new OpenBSDSemaphore(count);
542}
543
544
545#ifdef ENABLE_LOGGING_AND_PROFILING
546
547static Sampler* active_sampler_ = NULL;
548
549static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
550 USE(info);
551 if (signal != SIGPROF) return;
552 if (active_sampler_ == NULL) return;
553
554 TickSample sample;
555
556 // We always sample the VM state.
Steve Block6ded16b2010-05-10 14:33:55 +0100557 sample.state = VMState::current_state();
Steve Blockd0582a62009-12-15 09:54:21 +0000558
559 active_sampler_->Tick(&sample);
560}
561
562
563class Sampler::PlatformData : public Malloced {
564 public:
565 PlatformData() {
566 signal_handler_installed_ = false;
567 }
568
569 bool signal_handler_installed_;
570 struct sigaction old_signal_handler_;
571 struct itimerval old_timer_value_;
572};
573
574
Ben Murdochb0fe1622011-05-05 13:52:32 +0100575Sampler::Sampler(int interval)
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800576 : interval_(interval),
Ben Murdochb0fe1622011-05-05 13:52:32 +0100577 profiling_(false),
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800578 active_(false),
579 samples_taken_(0) {
Steve Blockd0582a62009-12-15 09:54:21 +0000580 data_ = new PlatformData();
581}
582
583
584Sampler::~Sampler() {
585 delete data_;
586}
587
588
589void Sampler::Start() {
590 // There can only be one active sampler at the time on POSIX
591 // platforms.
592 if (active_sampler_ != NULL) return;
593
594 // Request profiling signals.
595 struct sigaction sa;
596 sa.sa_sigaction = ProfilerSignalHandler;
597 sigemptyset(&sa.sa_mask);
598 sa.sa_flags = SA_SIGINFO;
599 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
600 data_->signal_handler_installed_ = true;
601
602 // Set the itimer to generate a tick for each interval.
603 itimerval itimer;
604 itimer.it_interval.tv_sec = interval_ / 1000;
605 itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
606 itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
607 itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
608 setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
609
610 // Set this sampler as the active sampler.
611 active_sampler_ = this;
612 active_ = true;
613}
614
615
616void Sampler::Stop() {
617 // Restore old signal handler
618 if (data_->signal_handler_installed_) {
619 setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
620 sigaction(SIGPROF, &data_->old_signal_handler_, 0);
621 data_->signal_handler_installed_ = false;
622 }
623
624 // This sampler is no longer the active sampler.
625 active_sampler_ = NULL;
626 active_ = false;
627}
628
629#endif // ENABLE_LOGGING_AND_PROFILING
630
631} } // namespace v8::internal