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