1 // 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
58 namespace v8 {
59 namespace 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).
63 static const pthread_t kNoThread = (pthread_t) 0;
64
65
ceiling(double x)66 double 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
Setup()76 void 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
CpuFeaturesImpliedByPlatform()87 uint64_t OS::CpuFeaturesImpliedByPlatform() {
88 return 0; // FreeBSD runs on anything.
89 }
90
91
ActivationFrameAlignment()92 int OS::ActivationFrameAlignment() {
93 // 16 byte alignment on FreeBSD
94 return 16;
95 }
96
97
LocalTimezone(double time)98 const 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
LocalTimeOffset()107 double 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
116 // 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.
121 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
122 static void* highest_ever_allocated = reinterpret_cast<void*>(0);
123
124
UpdateAllocatedSpaceLimits(void * address,int size)125 static 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
IsOutsideAllocatedSpace(void * address)133 bool OS::IsOutsideAllocatedSpace(void* address) {
134 return address < lowest_ever_allocated || address >= highest_ever_allocated;
135 }
136
137
AllocateAlignment()138 size_t OS::AllocateAlignment() {
139 return getpagesize();
140 }
141
142
Allocate(const size_t requested,size_t * allocated,bool executable)143 void* 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
Free(void * buf,const size_t length)160 void 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
Protect(void * address,size_t size)170 void OS::Protect(void* address, size_t size) {
171 UNIMPLEMENTED();
172 }
173
174
Unprotect(void * address,size_t size,bool is_executable)175 void OS::Unprotect(void* address, size_t size, bool is_executable) {
176 UNIMPLEMENTED();
177 }
178
179 #endif
180
181
Sleep(int milliseconds)182 void OS::Sleep(int milliseconds) {
183 unsigned int ms = static_cast<unsigned int>(milliseconds);
184 usleep(1000 * ms);
185 }
186
187
Abort()188 void OS::Abort() {
189 // Redirect to std abort to signal abnormal program termination.
190 abort();
191 }
192
193
DebugBreak()194 void OS::DebugBreak() {
195 #if (defined(__arm__) || defined(__thumb__)) && \
196 defined(CAN_USE_ARMV5_INSTRUCTIONS)
197 asm("bkpt 0");
198 #else
199 asm("int $3");
200 #endif
201 }
202
203
204 class PosixMemoryMappedFile : public OS::MemoryMappedFile {
205 public:
PosixMemoryMappedFile(FILE * file,void * memory,int size)206 PosixMemoryMappedFile(FILE* file, void* memory, int size)
207 : file_(file), memory_(memory), size_(size) { }
208 virtual ~PosixMemoryMappedFile();
memory()209 virtual void* memory() { return memory_; }
210 private:
211 FILE* file_;
212 void* memory_;
213 int size_;
214 };
215
216
create(const char * name,int size,void * initial)217 OS::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
~PosixMemoryMappedFile()232 PosixMemoryMappedFile::~PosixMemoryMappedFile() {
233 if (memory_) munmap(memory_, size_);
234 fclose(file_);
235 }
236
237
238 #ifdef ENABLE_LOGGING_AND_PROFILING
StringToLong(char * buffer)239 static unsigned StringToLong(char* buffer) {
240 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
241 }
242 #endif
243
244
LogSharedLibraryAddresses()245 void 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
StackWalk(Vector<OS::StackFrame> frames)287 int 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.
319 static const int kMmapFd = -1;
320 static const int kMmapFdOffset = 0;
321
322
VirtualMemory(size_t size)323 VirtualMemory::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
~VirtualMemory()331 VirtualMemory::~VirtualMemory() {
332 if (IsReserved()) {
333 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
334 }
335 }
336
337
IsReserved()338 bool VirtualMemory::IsReserved() {
339 return address_ != MAP_FAILED;
340 }
341
342
Commit(void * address,size_t size,bool executable)343 bool 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
Uncommit(void * address,size_t size)356 bool 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
363 class ThreadHandle::PlatformData : public Malloced {
364 public:
PlatformData(ThreadHandle::Kind kind)365 explicit PlatformData(ThreadHandle::Kind kind) {
366 Initialize(kind);
367 }
368
Initialize(ThreadHandle::Kind kind)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
ThreadHandle(Kind kind)379 ThreadHandle::ThreadHandle(Kind kind) {
380 data_ = new PlatformData(kind);
381 }
382
383
Initialize(ThreadHandle::Kind kind)384 void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
385 data_->Initialize(kind);
386 }
387
388
~ThreadHandle()389 ThreadHandle::~ThreadHandle() {
390 delete data_;
391 }
392
393
IsSelf() const394 bool ThreadHandle::IsSelf() const {
395 return pthread_equal(data_->thread_, pthread_self());
396 }
397
398
IsValid() const399 bool ThreadHandle::IsValid() const {
400 return data_->thread_ != kNoThread;
401 }
402
403
Thread()404 Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
405 }
406
407
~Thread()408 Thread::~Thread() {
409 }
410
411
ThreadEntry(void * arg)412 static 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
Start()424 void Thread::Start() {
425 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
426 ASSERT(IsValid());
427 }
428
429
Join()430 void Thread::Join() {
431 pthread_join(thread_handle_data()->thread_, NULL);
432 }
433
434
CreateThreadLocalKey()435 Thread::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
DeleteThreadLocalKey(LocalStorageKey key)444 void 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
GetThreadLocal(LocalStorageKey key)452 void* 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
SetThreadLocal(LocalStorageKey key,void * value)458 void 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
YieldCPU()464 void Thread::YieldCPU() {
465 sched_yield();
466 }
467
468
469 class FreeBSDMutex : public Mutex {
470 public:
471
FreeBSDMutex()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
~FreeBSDMutex()482 virtual ~FreeBSDMutex() { pthread_mutex_destroy(&mutex_); }
483
Lock()484 virtual int Lock() {
485 int result = pthread_mutex_lock(&mutex_);
486 return result;
487 }
488
Unlock()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
CreateMutex()499 Mutex* OS::CreateMutex() {
500 return new FreeBSDMutex();
501 }
502
503
504 class FreeBSDSemaphore : public Semaphore {
505 public:
FreeBSDSemaphore(int count)506 explicit FreeBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
~FreeBSDSemaphore()507 virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); }
508
509 virtual void Wait();
510 virtual bool Wait(int timeout);
Signal()511 virtual void Signal() { sem_post(&sem_); }
512 private:
513 sem_t sem_;
514 };
515
516
Wait()517 void 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
Wait(int timeout)526 bool 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(¤t_time, NULL) == -1) {
537 return false;
538 }
539
540 // Calculate time for end of timeout.
541 struct timeval end_time;
542 timeradd(¤t_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
CreateSemaphore(int count)555 Semaphore* OS::CreateSemaphore(int count) {
556 return new FreeBSDSemaphore(count);
557 }
558
559
560 #ifdef ENABLE_LOGGING_AND_PROFILING
561
562 static Sampler* active_sampler_ = NULL;
563
ProfilerSignalHandler(int signal,siginfo_t * info,void * context)564 static 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
577 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);
580 #elif V8_HOST_ARCH_X64
581 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);
584 #elif V8_HOST_ARCH_ARM
585 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);
588 #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
599 class Sampler::PlatformData : public Malloced {
600 public:
PlatformData()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
Sampler(int interval,bool profiling)611 Sampler::Sampler(int interval, bool profiling)
612 : interval_(interval), profiling_(profiling), active_(false) {
613 data_ = new PlatformData();
614 }
615
616
~Sampler()617 Sampler::~Sampler() {
618 delete data_;
619 }
620
621
Start()622 void 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
Stop()649 void 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
665