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