blob: e03059ad7211ee98f15a4ec58d2e5b72458b81b9 [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"
55
56
57namespace v8 {
58namespace internal {
59
60// 0 is never a valid thread id on OpenBSD since tids and pids share a
61// name space and pid 0 is used to kill the group (see man 2 kill).
62static const pthread_t kNoThread = (pthread_t) 0;
63
64
65double ceiling(double x) {
66 // Correct as on OS X
67 if (-1.0 < x && x < 0.0) {
68 return -0.0;
69 } else {
70 return ceil(x);
71 }
72}
73
74
75void OS::Setup() {
76 // Seed the random number generator.
77 // Convert the current time to a 64-bit integer first, before converting it
78 // to an unsigned. Going directly can cause an overflow and the seed to be
79 // set to all ones. The seed will be identical for different instances that
80 // call this setup code within the same millisecond.
81 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
82 srandom(static_cast<unsigned int>(seed));
83}
84
85
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +000086void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
87 __asm__ __volatile__("" : : : "memory");
88 *ptr = value;
89}
90
91
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000092uint64_t OS::CpuFeaturesImpliedByPlatform() {
93 return 0; // OpenBSD runs on anything.
94}
95
96
97int OS::ActivationFrameAlignment() {
98 // 16 byte alignment on OpenBSD
99 return 16;
100}
101
102
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000103const char* OS::LocalTimezone(double time) {
104 if (isnan(time)) return "";
105 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
106 struct tm* t = localtime(&tv);
107 if (NULL == t) return "";
108 return t->tm_zone;
109}
110
111
112double OS::LocalTimeOffset() {
113 time_t tv = time(NULL);
114 struct tm* t = localtime(&tv);
115 // tm_gmtoff includes any daylight savings offset, so subtract it.
116 return static_cast<double>(t->tm_gmtoff * msPerSecond -
117 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
118}
119
120
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000121// We keep the lowest and highest addresses mapped as a quick way of
122// determining that pointers are outside the heap (used mostly in assertions
123// and verification). The estimate is conservative, ie, not all addresses in
124// 'allocated' space are actually allocated to our heap. The range is
125// [lowest, highest), inclusive on the low and and exclusive on the high end.
126static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
127static void* highest_ever_allocated = reinterpret_cast<void*>(0);
128
129
130static void UpdateAllocatedSpaceLimits(void* address, int size) {
131 lowest_ever_allocated = Min(lowest_ever_allocated, address);
132 highest_ever_allocated =
133 Max(highest_ever_allocated,
134 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
135}
136
137
138bool OS::IsOutsideAllocatedSpace(void* address) {
139 return address < lowest_ever_allocated || address >= highest_ever_allocated;
140}
141
142
143size_t OS::AllocateAlignment() {
144 return getpagesize();
145}
146
147
148void* OS::Allocate(const size_t requested,
149 size_t* allocated,
150 bool executable) {
151 const size_t msize = RoundUp(requested, getpagesize());
152 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
153 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
154
155 if (mbase == MAP_FAILED) {
156 LOG(StringEvent("OS::Allocate", "mmap failed"));
157 return NULL;
158 }
159 *allocated = msize;
160 UpdateAllocatedSpaceLimits(mbase, msize);
161 return mbase;
162}
163
164
165void OS::Free(void* buf, const size_t length) {
166 int result = munmap(buf, length);
167 USE(result);
168 ASSERT(result == 0);
169}
170
171
172#ifdef ENABLE_HEAP_PROTECTION
173
174void OS::Protect(void* address, size_t size) {
175 UNIMPLEMENTED();
176}
177
178
179void OS::Unprotect(void* address, size_t size, bool is_executable) {
180 UNIMPLEMENTED();
181}
182
183#endif
184
185
186void OS::Sleep(int milliseconds) {
187 unsigned int ms = static_cast<unsigned int>(milliseconds);
188 usleep(1000 * ms);
189}
190
191
192void OS::Abort() {
193 // Redirect to std abort to signal abnormal program termination.
194 abort();
195}
196
197
198void OS::DebugBreak() {
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000199#if (defined(__arm__) || defined(__thumb__))
200# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000201 asm("bkpt 0");
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000202# endif
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000203#else
204 asm("int $3");
205#endif
206}
207
208
209class PosixMemoryMappedFile : public OS::MemoryMappedFile {
210 public:
211 PosixMemoryMappedFile(FILE* file, void* memory, int size)
212 : file_(file), memory_(memory), size_(size) { }
213 virtual ~PosixMemoryMappedFile();
214 virtual void* memory() { return memory_; }
215 private:
216 FILE* file_;
217 void* memory_;
218 int size_;
219};
220
221
222OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
223 void* initial) {
224 FILE* file = fopen(name, "w+");
225 if (file == NULL) return NULL;
226 int result = fwrite(initial, size, 1, file);
227 if (result < 1) {
228 fclose(file);
229 return NULL;
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
237PosixMemoryMappedFile::~PosixMemoryMappedFile() {
238 if (memory_) munmap(memory_, size_);
239 fclose(file_);
240}
241
242
243#ifdef ENABLE_LOGGING_AND_PROFILING
244static unsigned StringToLong(char* buffer) {
245 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
246}
247#endif
248
249
250void OS::LogSharedLibraryAddresses() {
251#ifdef ENABLE_LOGGING_AND_PROFILING
252 static const int MAP_LENGTH = 1024;
253 int fd = open("/proc/self/maps", O_RDONLY);
254 if (fd < 0) return;
255 while (true) {
256 char addr_buffer[11];
257 addr_buffer[0] = '0';
258 addr_buffer[1] = 'x';
259 addr_buffer[10] = 0;
260 int result = read(fd, addr_buffer + 2, 8);
261 if (result < 8) break;
262 unsigned start = StringToLong(addr_buffer);
263 result = read(fd, addr_buffer + 2, 1);
264 if (result < 1) break;
265 if (addr_buffer[2] != '-') break;
266 result = read(fd, addr_buffer + 2, 8);
267 if (result < 8) break;
268 unsigned end = StringToLong(addr_buffer);
269 char buffer[MAP_LENGTH];
270 int bytes_read = -1;
271 do {
272 bytes_read++;
273 if (bytes_read >= MAP_LENGTH - 1)
274 break;
275 result = read(fd, buffer + bytes_read, 1);
276 if (result < 1) break;
277 } while (buffer[bytes_read] != '\n');
278 buffer[bytes_read] = 0;
279 // Ignore mappings that are not executable.
280 if (buffer[3] != 'x') continue;
281 char* start_of_path = index(buffer, '/');
282 // There may be no filename in this line. Skip to next.
283 if (start_of_path == NULL) continue;
284 buffer[bytes_read] = 0;
285 LOG(SharedLibraryEvent(start_of_path, start, end));
286 }
287 close(fd);
288#endif
289}
290
291
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000292void OS::SignalCodeMovingGC() {
293}
294
295
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000296int OS::StackWalk(Vector<OS::StackFrame> frames) {
297 UNIMPLEMENTED();
298 return 1;
299}
300
301
302// Constants used for mmap.
303static const int kMmapFd = -1;
304static const int kMmapFdOffset = 0;
305
306
307VirtualMemory::VirtualMemory(size_t size) {
308 address_ = mmap(NULL, size, PROT_NONE,
309 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
310 kMmapFd, kMmapFdOffset);
311 size_ = size;
312}
313
314
315VirtualMemory::~VirtualMemory() {
316 if (IsReserved()) {
317 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
318 }
319}
320
321
322bool VirtualMemory::IsReserved() {
323 return address_ != MAP_FAILED;
324}
325
326
327bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
328 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
329 if (MAP_FAILED == mmap(address, size, prot,
330 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
331 kMmapFd, kMmapFdOffset)) {
332 return false;
333 }
334
335 UpdateAllocatedSpaceLimits(address, size);
336 return true;
337}
338
339
340bool VirtualMemory::Uncommit(void* address, size_t size) {
341 return mmap(address, size, PROT_NONE,
342 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
343 kMmapFd, kMmapFdOffset) != MAP_FAILED;
344}
345
346
347class ThreadHandle::PlatformData : public Malloced {
348 public:
349 explicit PlatformData(ThreadHandle::Kind kind) {
350 Initialize(kind);
351 }
352
353 void Initialize(ThreadHandle::Kind kind) {
354 switch (kind) {
355 case ThreadHandle::SELF: thread_ = pthread_self(); break;
356 case ThreadHandle::INVALID: thread_ = kNoThread; break;
357 }
358 }
359 pthread_t thread_; // Thread handle for pthread.
360};
361
362
363ThreadHandle::ThreadHandle(Kind kind) {
364 data_ = new PlatformData(kind);
365}
366
367
368void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
369 data_->Initialize(kind);
370}
371
372
373ThreadHandle::~ThreadHandle() {
374 delete data_;
375}
376
377
378bool ThreadHandle::IsSelf() const {
379 return pthread_equal(data_->thread_, pthread_self());
380}
381
382
383bool ThreadHandle::IsValid() const {
384 return data_->thread_ != kNoThread;
385}
386
387
388Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
389}
390
391
392Thread::~Thread() {
393}
394
395
396static void* ThreadEntry(void* arg) {
397 Thread* thread = reinterpret_cast<Thread*>(arg);
398 // This is also initialized by the first argument to pthread_create() but we
399 // don't know which thread will run first (the original thread or the new
400 // one) so we initialize it here too.
401 thread->thread_handle_data()->thread_ = pthread_self();
402 ASSERT(thread->IsValid());
403 thread->Run();
404 return NULL;
405}
406
407
408void Thread::Start() {
409 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
410 ASSERT(IsValid());
411}
412
413
414void Thread::Join() {
415 pthread_join(thread_handle_data()->thread_, NULL);
416}
417
418
419Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
420 pthread_key_t key;
421 int result = pthread_key_create(&key, NULL);
422 USE(result);
423 ASSERT(result == 0);
424 return static_cast<LocalStorageKey>(key);
425}
426
427
428void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
429 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
430 int result = pthread_key_delete(pthread_key);
431 USE(result);
432 ASSERT(result == 0);
433}
434
435
436void* Thread::GetThreadLocal(LocalStorageKey key) {
437 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
438 return pthread_getspecific(pthread_key);
439}
440
441
442void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
443 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
444 pthread_setspecific(pthread_key, value);
445}
446
447
448void Thread::YieldCPU() {
449 sched_yield();
450}
451
452
453class OpenBSDMutex : public Mutex {
454 public:
455
456 OpenBSDMutex() {
457 pthread_mutexattr_t attrs;
458 int result = pthread_mutexattr_init(&attrs);
459 ASSERT(result == 0);
460 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
461 ASSERT(result == 0);
462 result = pthread_mutex_init(&mutex_, &attrs);
463 ASSERT(result == 0);
464 }
465
466 virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); }
467
468 virtual int Lock() {
469 int result = pthread_mutex_lock(&mutex_);
470 return result;
471 }
472
473 virtual int Unlock() {
474 int result = pthread_mutex_unlock(&mutex_);
475 return result;
476 }
477
478 private:
479 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
480};
481
482
483Mutex* OS::CreateMutex() {
484 return new OpenBSDMutex();
485}
486
487
488class OpenBSDSemaphore : public Semaphore {
489 public:
490 explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
491 virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
492
493 virtual void Wait();
494 virtual bool Wait(int timeout);
495 virtual void Signal() { sem_post(&sem_); }
496 private:
497 sem_t sem_;
498};
499
500
501void OpenBSDSemaphore::Wait() {
502 while (true) {
503 int result = sem_wait(&sem_);
504 if (result == 0) return; // Successfully got semaphore.
505 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
506 }
507}
508
509
510bool OpenBSDSemaphore::Wait(int timeout) {
511 const long kOneSecondMicros = 1000000; // NOLINT
512
513 // Split timeout into second and nanosecond parts.
514 struct timeval delta;
515 delta.tv_usec = timeout % kOneSecondMicros;
516 delta.tv_sec = timeout / kOneSecondMicros;
517
518 struct timeval current_time;
519 // Get the current time.
520 if (gettimeofday(&current_time, NULL) == -1) {
521 return false;
522 }
523
524 // Calculate time for end of timeout.
525 struct timeval end_time;
526 timeradd(&current_time, &delta, &end_time);
527
528 struct timespec ts;
529 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
530 while (true) {
531 int result = sem_trywait(&sem_);
532 if (result == 0) return true; // Successfully got semaphore.
533 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
534 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
535 }
536}
537
538
539Semaphore* OS::CreateSemaphore(int count) {
540 return new OpenBSDSemaphore(count);
541}
542
543
544#ifdef ENABLE_LOGGING_AND_PROFILING
545
546static Sampler* active_sampler_ = NULL;
547
548static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
549 USE(info);
550 if (signal != SIGPROF) return;
551 if (active_sampler_ == NULL) return;
552
553 TickSample sample;
554
555 // We always sample the VM state.
ager@chromium.org357bf652010-04-12 11:30:10 +0000556 sample.state = VMState::current_state();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000557
558 active_sampler_->Tick(&sample);
559}
560
561
562class Sampler::PlatformData : public Malloced {
563 public:
564 PlatformData() {
565 signal_handler_installed_ = false;
566 }
567
568 bool signal_handler_installed_;
569 struct sigaction old_signal_handler_;
570 struct itimerval old_timer_value_;
571};
572
573
574Sampler::Sampler(int interval, bool profiling)
575 : interval_(interval), profiling_(profiling), active_(false) {
576 data_ = new PlatformData();
577}
578
579
580Sampler::~Sampler() {
581 delete data_;
582}
583
584
585void Sampler::Start() {
586 // There can only be one active sampler at the time on POSIX
587 // platforms.
588 if (active_sampler_ != NULL) return;
589
590 // Request profiling signals.
591 struct sigaction sa;
592 sa.sa_sigaction = ProfilerSignalHandler;
593 sigemptyset(&sa.sa_mask);
594 sa.sa_flags = SA_SIGINFO;
595 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
596 data_->signal_handler_installed_ = true;
597
598 // Set the itimer to generate a tick for each interval.
599 itimerval itimer;
600 itimer.it_interval.tv_sec = interval_ / 1000;
601 itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
602 itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
603 itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
604 setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
605
606 // Set this sampler as the active sampler.
607 active_sampler_ = this;
608 active_ = true;
609}
610
611
612void Sampler::Stop() {
613 // Restore old signal handler
614 if (data_->signal_handler_installed_) {
615 setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
616 sigaction(SIGPROF, &data_->old_signal_handler_, 0);
617 data_->signal_handler_installed_ = false;
618 }
619
620 // This sampler is no longer the active sampler.
621 active_sampler_ = NULL;
622 active_ = false;
623}
624
625#endif // ENABLE_LOGGING_AND_PROFILING
626
627} } // namespace v8::internal