blob: fe4c31f515b0a9d0cec1e9bf5a5c30571c149853 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000028// Platform specific code for Linux goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000030
31#include <pthread.h>
32#include <semaphore.h>
33#include <signal.h>
34#include <sys/time.h>
35#include <sys/resource.h>
ager@chromium.org381abbb2009-02-25 13:23:22 +000036#include <sys/types.h>
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037#include <stdlib.h>
38
39// Ubuntu Dapper requires memory pages to be marked as
40// executable. Otherwise, OS raises an exception when executing code
41// in that page.
42#include <sys/types.h> // mmap & munmap
ager@chromium.org236ad962008-09-25 09:45:57 +000043#include <sys/mman.h> // mmap & munmap
44#include <sys/stat.h> // open
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000045#include <fcntl.h> // open
46#include <unistd.h> // sysconf
47#ifdef __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000048#include <execinfo.h> // backtrace, backtrace_symbols
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000049#endif // def __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000050#include <strings.h> // index
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051#include <errno.h>
52#include <stdarg.h>
53
54#undef MAP_TYPE
55
56#include "v8.h"
57
58#include "platform.h"
ager@chromium.orga1645e22009-09-09 19:27:10 +000059#include "top.h"
60#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000061
62
kasperl@chromium.org71affb52009-05-26 05:44:31 +000063namespace v8 {
64namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000065
66// 0 is never a valid thread id on Linux since tids and pids share a
67// name space and pid 0 is reserved (see man 2 kill).
68static const pthread_t kNoThread = (pthread_t) 0;
69
70
71double ceiling(double x) {
72 return ceil(x);
73}
74
75
76void OS::Setup() {
77 // Seed the random number generator.
ager@chromium.org9258b6b2008-09-11 09:11:10 +000078 // 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));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000084}
85
86
ager@chromium.org236ad962008-09-25 09:45:57 +000087double OS::nan_value() {
88 return NAN;
89}
90
91
92int OS::ActivationFrameAlignment() {
ager@chromium.orge2902be2009-06-08 12:21:35 +000093#ifdef V8_TARGET_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +000094 // On EABI ARM targets this is required for fp correctness in the
95 // runtime system.
ager@chromium.org3a6061e2009-03-12 14:24:36 +000096 return 8;
ager@chromium.orge2902be2009-06-08 12:21:35 +000097#else
98 // With gcc 4.4 the tree vectorization optimiser can generate code
99 // that requires 16 byte alignment such as movdqa on x86.
100 return 16;
101#endif
ager@chromium.org236ad962008-09-25 09:45:57 +0000102}
103
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000104
105// We keep the lowest and highest addresses mapped as a quick way of
106// determining that pointers are outside the heap (used mostly in assertions
107// and verification). The estimate is conservative, ie, not all addresses in
108// 'allocated' space are actually allocated to our heap. The range is
109// [lowest, highest), inclusive on the low and and exclusive on the high end.
110static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
111static void* highest_ever_allocated = reinterpret_cast<void*>(0);
112
113
114static void UpdateAllocatedSpaceLimits(void* address, int size) {
115 lowest_ever_allocated = Min(lowest_ever_allocated, address);
116 highest_ever_allocated =
117 Max(highest_ever_allocated,
118 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
119}
120
121
122bool OS::IsOutsideAllocatedSpace(void* address) {
123 return address < lowest_ever_allocated || address >= highest_ever_allocated;
124}
125
126
127size_t OS::AllocateAlignment() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000128 return sysconf(_SC_PAGESIZE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000129}
130
131
kasper.lund7276f142008-07-30 08:49:36 +0000132void* OS::Allocate(const size_t requested,
133 size_t* allocated,
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000134 bool is_executable) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000135 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000136 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
kasper.lund7276f142008-07-30 08:49:36 +0000137 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000138 if (mbase == MAP_FAILED) {
139 LOG(StringEvent("OS::Allocate", "mmap failed"));
140 return NULL;
141 }
142 *allocated = msize;
143 UpdateAllocatedSpaceLimits(mbase, msize);
144 return mbase;
145}
146
147
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000148void OS::Free(void* address, const size_t size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000149 // TODO(1240712): munmap has a return value which is ignored here.
ager@chromium.orga1645e22009-09-09 19:27:10 +0000150 int result = munmap(address, size);
151 USE(result);
152 ASSERT(result == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000153}
154
155
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000156#ifdef ENABLE_HEAP_PROTECTION
157
158void OS::Protect(void* address, size_t size) {
159 // TODO(1240712): mprotect has a return value which is ignored here.
160 mprotect(address, size, PROT_READ);
161}
162
163
164void OS::Unprotect(void* address, size_t size, bool is_executable) {
165 // TODO(1240712): mprotect has a return value which is ignored here.
166 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
167 mprotect(address, size, prot);
168}
169
170#endif
171
172
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000173void OS::Sleep(int milliseconds) {
174 unsigned int ms = static_cast<unsigned int>(milliseconds);
175 usleep(1000 * ms);
176}
177
178
179void OS::Abort() {
180 // Redirect to std abort to signal abnormal program termination.
181 abort();
182}
183
184
kasper.lund7276f142008-07-30 08:49:36 +0000185void OS::DebugBreak() {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000186// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
187// which is the architecture of generated code).
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000188#if defined(__arm__) || defined(__thumb__)
kasper.lund7276f142008-07-30 08:49:36 +0000189 asm("bkpt 0");
190#else
191 asm("int $3");
192#endif
193}
194
195
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000196class PosixMemoryMappedFile : public OS::MemoryMappedFile {
197 public:
198 PosixMemoryMappedFile(FILE* file, void* memory, int size)
199 : file_(file), memory_(memory), size_(size) { }
200 virtual ~PosixMemoryMappedFile();
201 virtual void* memory() { return memory_; }
202 private:
203 FILE* file_;
204 void* memory_;
205 int size_;
206};
207
208
209OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
210 void* initial) {
211 FILE* file = fopen(name, "w+");
212 if (file == NULL) return NULL;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000213 int result = fwrite(initial, size, 1, file);
214 if (result < 1) {
215 fclose(file);
216 return NULL;
217 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000218 void* memory =
219 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
220 return new PosixMemoryMappedFile(file, memory, size);
221}
222
223
224PosixMemoryMappedFile::~PosixMemoryMappedFile() {
225 if (memory_) munmap(memory_, size_);
226 fclose(file_);
227}
228
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000229
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000230void OS::LogSharedLibraryAddresses() {
231#ifdef ENABLE_LOGGING_AND_PROFILING
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000232 // This function assumes that the layout of the file is as follows:
233 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
234 // If we encounter an unexpected situation we abort scanning further entries.
235 FILE *fp = fopen("/proc/self/maps", "r");
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000236 if (fp == NULL) return;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000237
238 // Allocate enough room to be able to store a full file name.
239 const int kLibNameLen = FILENAME_MAX + 1;
240 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
241
242 // This loop will terminate once the scanning hits an EOF.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000243 while (true) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000244 uintptr_t start, end;
245 char attr_r, attr_w, attr_x, attr_p;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000246 // Parse the addresses and permission bits at the beginning of the line.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000247 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
248 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000249
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000250 int c;
251 if (attr_r == 'r' && attr_x == 'x') {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000252 // Found a readable and executable entry. Skip characters until we reach
253 // the beginning of the filename or the end of the line.
254 do {
255 c = getc(fp);
256 } while ((c != EOF) && (c != '\n') && (c != '/'));
257 if (c == EOF) break; // EOF: Was unexpected, just exit.
258
259 // Process the filename if found.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000260 if (c == '/') {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000261 ungetc(c, fp); // Push the '/' back into the stream to be read below.
262
263 // Read to the end of the line. Exit if the read fails.
264 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
265
266 // Drop the newline character read by fgets. We do not need to check
267 // for a zero-length string because we know that we at least read the
268 // '/' character.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000269 lib_name[strlen(lib_name) - 1] = '\0';
270 } else {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000271 // No library name found, just record the raw address range.
272 snprintf(lib_name, kLibNameLen,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000273 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
274 }
275 LOG(SharedLibraryEvent(lib_name, start, end));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000276 } else {
277 // Entry not describing executable data. Skip to end of line to setup
278 // reading the next entry.
279 do {
280 c = getc(fp);
281 } while ((c != EOF) && (c != '\n'));
282 if (c == EOF) break;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000283 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284 }
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000285 free(lib_name);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000286 fclose(fp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000287#endif
288}
289
290
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000291int OS::StackWalk(Vector<OS::StackFrame> frames) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000292 // backtrace is a glibc extension.
293#ifdef __GLIBC__
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000294 int frames_size = frames.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295 void** addresses = NewArray<void*>(frames_size);
296
297 int frames_count = backtrace(addresses, frames_size);
298
299 char** symbols;
300 symbols = backtrace_symbols(addresses, frames_count);
301 if (symbols == NULL) {
302 DeleteArray(addresses);
303 return kStackWalkError;
304 }
305
306 for (int i = 0; i < frames_count; i++) {
307 frames[i].address = addresses[i];
308 // Format a text representation of the frame based on the information
309 // available.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000310 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
311 "%s",
312 symbols[i]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000313 // Make sure line termination is in place.
314 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
315 }
316
317 DeleteArray(addresses);
318 free(symbols);
319
320 return frames_count;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000321#else // ndef __GLIBC__
322 return 0;
323#endif // ndef __GLIBC__
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000324}
325
326
327// Constants used for mmap.
328static const int kMmapFd = -1;
329static const int kMmapFdOffset = 0;
330
331
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000332VirtualMemory::VirtualMemory(size_t size) {
333 address_ = mmap(NULL, size, PROT_NONE,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000334 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
335 kMmapFd, kMmapFdOffset);
336 size_ = size;
337}
338
339
340VirtualMemory::~VirtualMemory() {
341 if (IsReserved()) {
342 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
343 }
344}
345
346
347bool VirtualMemory::IsReserved() {
348 return address_ != MAP_FAILED;
349}
350
351
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000352bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
353 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
kasper.lund7276f142008-07-30 08:49:36 +0000354 if (MAP_FAILED == mmap(address, size, prot,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000355 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
356 kMmapFd, kMmapFdOffset)) {
357 return false;
358 }
359
360 UpdateAllocatedSpaceLimits(address, size);
361 return true;
362}
363
364
365bool VirtualMemory::Uncommit(void* address, size_t size) {
366 return mmap(address, size, PROT_NONE,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000367 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000368 kMmapFd, kMmapFdOffset) != MAP_FAILED;
369}
370
371
372class ThreadHandle::PlatformData : public Malloced {
373 public:
374 explicit PlatformData(ThreadHandle::Kind kind) {
375 Initialize(kind);
376 }
377
378 void Initialize(ThreadHandle::Kind kind) {
379 switch (kind) {
380 case ThreadHandle::SELF: thread_ = pthread_self(); break;
381 case ThreadHandle::INVALID: thread_ = kNoThread; break;
382 }
383 }
ager@chromium.org41826e72009-03-30 13:30:57 +0000384
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000385 pthread_t thread_; // Thread handle for pthread.
386};
387
388
389ThreadHandle::ThreadHandle(Kind kind) {
390 data_ = new PlatformData(kind);
391}
392
393
394void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
395 data_->Initialize(kind);
396}
397
398
399ThreadHandle::~ThreadHandle() {
400 delete data_;
401}
402
403
404bool ThreadHandle::IsSelf() const {
405 return pthread_equal(data_->thread_, pthread_self());
406}
407
408
409bool ThreadHandle::IsValid() const {
410 return data_->thread_ != kNoThread;
411}
412
413
414Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
415}
416
417
418Thread::~Thread() {
419}
420
421
422static void* ThreadEntry(void* arg) {
423 Thread* thread = reinterpret_cast<Thread*>(arg);
424 // This is also initialized by the first argument to pthread_create() but we
425 // don't know which thread will run first (the original thread or the new
426 // one) so we initialize it here too.
427 thread->thread_handle_data()->thread_ = pthread_self();
428 ASSERT(thread->IsValid());
429 thread->Run();
430 return NULL;
431}
432
433
434void Thread::Start() {
435 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
436 ASSERT(IsValid());
437}
438
439
440void Thread::Join() {
441 pthread_join(thread_handle_data()->thread_, NULL);
442}
443
444
445Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
446 pthread_key_t key;
447 int result = pthread_key_create(&key, NULL);
448 USE(result);
449 ASSERT(result == 0);
450 return static_cast<LocalStorageKey>(key);
451}
452
453
454void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
455 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
456 int result = pthread_key_delete(pthread_key);
457 USE(result);
458 ASSERT(result == 0);
459}
460
461
462void* Thread::GetThreadLocal(LocalStorageKey key) {
463 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
464 return pthread_getspecific(pthread_key);
465}
466
467
468void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
469 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
470 pthread_setspecific(pthread_key, value);
471}
472
473
474void Thread::YieldCPU() {
475 sched_yield();
476}
477
478
479class LinuxMutex : public Mutex {
480 public:
481
482 LinuxMutex() {
483 pthread_mutexattr_t attrs;
484 int result = pthread_mutexattr_init(&attrs);
485 ASSERT(result == 0);
486 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
487 ASSERT(result == 0);
488 result = pthread_mutex_init(&mutex_, &attrs);
489 ASSERT(result == 0);
490 }
491
492 virtual ~LinuxMutex() { pthread_mutex_destroy(&mutex_); }
493
494 virtual int Lock() {
495 int result = pthread_mutex_lock(&mutex_);
496 return result;
497 }
498
499 virtual int Unlock() {
500 int result = pthread_mutex_unlock(&mutex_);
501 return result;
502 }
503
504 private:
505 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
506};
507
508
509Mutex* OS::CreateMutex() {
510 return new LinuxMutex();
511}
512
513
514class LinuxSemaphore : public Semaphore {
515 public:
516 explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); }
517 virtual ~LinuxSemaphore() { sem_destroy(&sem_); }
518
kasper.lund7276f142008-07-30 08:49:36 +0000519 virtual void Wait();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000520 virtual bool Wait(int timeout);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000521 virtual void Signal() { sem_post(&sem_); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000522 private:
523 sem_t sem_;
524};
525
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000526
kasper.lund7276f142008-07-30 08:49:36 +0000527void LinuxSemaphore::Wait() {
528 while (true) {
529 int result = sem_wait(&sem_);
530 if (result == 0) return; // Successfully got semaphore.
531 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
532 }
533}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000534
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000535
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000536#ifndef TIMEVAL_TO_TIMESPEC
537#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
538 (ts)->tv_sec = (tv)->tv_sec; \
539 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
540} while (false)
541#endif
542
543
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000544bool LinuxSemaphore::Wait(int timeout) {
545 const long kOneSecondMicros = 1000000; // NOLINT
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000546
547 // Split timeout into second and nanosecond parts.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000548 struct timeval delta;
549 delta.tv_usec = timeout % kOneSecondMicros;
550 delta.tv_sec = timeout / kOneSecondMicros;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000551
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000552 struct timeval current_time;
553 // Get the current time.
554 if (gettimeofday(&current_time, NULL) == -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000555 return false;
556 }
557
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000558 // Calculate time for end of timeout.
559 struct timeval end_time;
560 timeradd(&current_time, &delta, &end_time);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000561
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000562 struct timespec ts;
563 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000564 // Wait for semaphore signalled or timeout.
565 while (true) {
566 int result = sem_timedwait(&sem_, &ts);
567 if (result == 0) return true; // Successfully got semaphore.
568 if (result > 0) {
569 // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
570 errno = result;
571 result = -1;
572 }
573 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
574 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
575 }
576}
577
578
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000579Semaphore* OS::CreateSemaphore(int count) {
580 return new LinuxSemaphore(count);
581}
582
ager@chromium.org381abbb2009-02-25 13:23:22 +0000583
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584#ifdef ENABLE_LOGGING_AND_PROFILING
585
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000586static Sampler* active_sampler_ = NULL;
ager@chromium.orga1645e22009-09-09 19:27:10 +0000587static pthread_t vm_thread_ = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000588
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000589
590#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
591// Android runs a fairly new Linux kernel, so signal info is there,
592// but the C library doesn't have the structs defined.
593
594struct sigcontext {
595 uint32_t trap_no;
596 uint32_t error_code;
597 uint32_t oldmask;
598 uint32_t gregs[16];
599 uint32_t arm_cpsr;
600 uint32_t fault_address;
601};
602typedef uint32_t __sigset_t;
603typedef struct sigcontext mcontext_t;
604typedef struct ucontext {
605 uint32_t uc_flags;
606 struct ucontext *uc_link;
607 stack_t uc_stack;
608 mcontext_t uc_mcontext;
609 __sigset_t uc_sigmask;
610} ucontext_t;
611enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
612
613#endif
614
615
ager@chromium.orga1645e22009-09-09 19:27:10 +0000616// A function that determines if a signal handler is called in the context
617// of a VM thread.
618//
619// The problem is that SIGPROF signal can be delivered to an arbitrary thread
620// (see http://code.google.com/p/google-perftools/issues/detail?id=106#c2)
621// So, if the signal is being handled in the context of a non-VM thread,
622// it means that the VM thread is running, and trying to sample its stack can
623// cause a crash.
624static inline bool IsVmThread() {
625 // In the case of a single VM thread, this check is enough.
626 if (pthread_equal(pthread_self(), vm_thread_)) return true;
627 // If there are multiple threads that use VM, they must have a thread id
628 // stored in TLS. To verify that the thread is really executing VM,
629 // we check Top's data. Having that ThreadManager::RestoreThread first
630 // restores ThreadLocalTop from TLS, and only then erases the TLS value,
631 // reading Top::thread_id() should not be affected by races.
632 if (ThreadManager::HasId() && !ThreadManager::IsArchived() &&
633 ThreadManager::CurrentId() == Top::thread_id()) {
634 return true;
635 }
636 return false;
637}
638
639
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000640static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
641 USE(info);
642 if (signal != SIGPROF) return;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000643 if (active_sampler_ == NULL) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000644
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000645 TickSample sample;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000646
647 // If profiling, we extract the current pc and sp.
648 if (active_sampler_->IsProfiling()) {
649 // Extracting the sample from the context is extremely machine dependent.
650 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
651 mcontext_t& mcontext = ucontext->uc_mcontext;
ager@chromium.org9085a012009-05-11 19:22:57 +0000652#if V8_HOST_ARCH_IA32
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000653 sample.pc = mcontext.gregs[REG_EIP];
654 sample.sp = mcontext.gregs[REG_ESP];
ager@chromium.org381abbb2009-02-25 13:23:22 +0000655 sample.fp = mcontext.gregs[REG_EBP];
ager@chromium.org9085a012009-05-11 19:22:57 +0000656#elif V8_HOST_ARCH_X64
657 sample.pc = mcontext.gregs[REG_RIP];
658 sample.sp = mcontext.gregs[REG_RSP];
659 sample.fp = mcontext.gregs[REG_RBP];
660#elif V8_HOST_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000661// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
662#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
ager@chromium.org9085a012009-05-11 19:22:57 +0000663 sample.pc = mcontext.gregs[R15];
664 sample.sp = mcontext.gregs[R13];
665 sample.fp = mcontext.gregs[R11];
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000666#else
667 sample.pc = mcontext.arm_pc;
668 sample.sp = mcontext.arm_sp;
669 sample.fp = mcontext.arm_fp;
670#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000671#endif
ager@chromium.orga1645e22009-09-09 19:27:10 +0000672 if (IsVmThread())
673 active_sampler_->SampleStack(&sample);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000674 }
675
676 // We always sample the VM state.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000677 sample.state = Logger::state();
678
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000679 active_sampler_->Tick(&sample);
680}
681
682
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000683class Sampler::PlatformData : public Malloced {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000684 public:
685 PlatformData() {
686 signal_handler_installed_ = false;
687 }
688
689 bool signal_handler_installed_;
690 struct sigaction old_signal_handler_;
691 struct itimerval old_timer_value_;
692};
693
694
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000695Sampler::Sampler(int interval, bool profiling)
696 : interval_(interval), profiling_(profiling), active_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697 data_ = new PlatformData();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000698}
699
700
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000701Sampler::~Sampler() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000702 delete data_;
703}
704
705
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000706void Sampler::Start() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000707 // There can only be one active sampler at the time on POSIX
708 // platforms.
709 if (active_sampler_ != NULL) return;
710
ager@chromium.orga1645e22009-09-09 19:27:10 +0000711 vm_thread_ = pthread_self();
712
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000713 // Request profiling signals.
714 struct sigaction sa;
715 sa.sa_sigaction = ProfilerSignalHandler;
716 sigemptyset(&sa.sa_mask);
717 sa.sa_flags = SA_SIGINFO;
718 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
719 data_->signal_handler_installed_ = true;
720
721 // Set the itimer to generate a tick for each interval.
722 itimerval itimer;
723 itimer.it_interval.tv_sec = interval_ / 1000;
724 itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
725 itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
726 itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
727 setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
728
729 // Set this sampler as the active sampler.
730 active_sampler_ = this;
731 active_ = true;
732}
733
734
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000735void Sampler::Stop() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736 // Restore old signal handler
737 if (data_->signal_handler_installed_) {
738 setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
739 sigaction(SIGPROF, &data_->old_signal_handler_, 0);
740 data_->signal_handler_installed_ = false;
741 }
742
743 // This sampler is no longer the active sampler.
744 active_sampler_ = NULL;
745 active_ = false;
746}
747
ager@chromium.orga1645e22009-09-09 19:27:10 +0000748
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000749#endif // ENABLE_LOGGING_AND_PROFILING
750
751} } // namespace v8::internal