blob: f7e7d5e938ecdec842768585ef10c2a869318780 [file] [log] [blame]
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001// Copyright 2012 the V8 project authors. All rights reserved.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +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
28// Platform specific code for Cygwin goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
30
31#include <errno.h>
32#include <pthread.h>
33#include <semaphore.h>
34#include <stdarg.h>
35#include <strings.h> // index
36#include <sys/time.h>
37#include <sys/mman.h> // mmap & munmap
38#include <unistd.h> // sysconf
39
40#undef MAP_TYPE
41
42#include "v8.h"
43
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000044#include "platform-posix.h"
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000045#include "platform.h"
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000046#include "v8threads.h"
47#include "vm-state-inl.h"
48#include "win32-headers.h"
49
50namespace v8 {
51namespace internal {
52
53// 0 is never a valid thread id
54static const pthread_t kNoThread = (pthread_t) 0;
55
56
57double ceiling(double x) {
58 return ceil(x);
59}
60
61
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000062static Mutex* limit_mutex = NULL;
63
64
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000065void OS::PostSetUp() {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +000066 POSIXPostSetUp();
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000067}
68
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000069uint64_t OS::CpuFeaturesImpliedByPlatform() {
70 return 0; // Nothing special about Cygwin.
71}
72
73
74int OS::ActivationFrameAlignment() {
75 // With gcc 4.4 the tree vectorization optimizer can generate code
76 // that requires 16 byte alignment such as movdqa on x86.
77 return 16;
78}
79
80
81void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
82 __asm__ __volatile__("" : : : "memory");
83 // An x86 store acts as a release barrier.
84 *ptr = value;
85}
86
87const char* OS::LocalTimezone(double time) {
88 if (isnan(time)) return "";
89 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
90 struct tm* t = localtime(&tv);
91 if (NULL == t) return "";
92 return tzname[0]; // The location of the timezone string on Cygwin.
93}
94
95
96double OS::LocalTimeOffset() {
97 // On Cygwin, struct tm does not contain a tm_gmtoff field.
98 time_t utc = time(NULL);
99 ASSERT(utc != -1);
100 struct tm* loc = localtime(&utc);
101 ASSERT(loc != NULL);
102 // time - localtime includes any daylight savings offset, so subtract it.
103 return static_cast<double>((mktime(loc) - utc) * msPerSecond -
104 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0));
105}
106
107
108// We keep the lowest and highest addresses mapped as a quick way of
109// determining that pointers are outside the heap (used mostly in assertions
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000110// and verification). The estimate is conservative, i.e., not all addresses in
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000111// 'allocated' space are actually allocated to our heap. The range is
112// [lowest, highest), inclusive on the low and and exclusive on the high end.
113static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
114static void* highest_ever_allocated = reinterpret_cast<void*>(0);
115
116
117static void UpdateAllocatedSpaceLimits(void* address, int size) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000118 ASSERT(limit_mutex != NULL);
119 ScopedLock lock(limit_mutex);
120
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000121 lowest_ever_allocated = Min(lowest_ever_allocated, address);
122 highest_ever_allocated =
123 Max(highest_ever_allocated,
124 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
125}
126
127
128bool OS::IsOutsideAllocatedSpace(void* address) {
129 return address < lowest_ever_allocated || address >= highest_ever_allocated;
130}
131
132
133size_t OS::AllocateAlignment() {
134 return sysconf(_SC_PAGESIZE);
135}
136
137
138void* OS::Allocate(const size_t requested,
139 size_t* allocated,
140 bool is_executable) {
141 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
142 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
143 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
144 if (mbase == MAP_FAILED) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000145 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000146 return NULL;
147 }
148 *allocated = msize;
149 UpdateAllocatedSpaceLimits(mbase, msize);
150 return mbase;
151}
152
153
154void OS::Free(void* address, const size_t size) {
155 // TODO(1240712): munmap has a return value which is ignored here.
156 int result = munmap(address, size);
157 USE(result);
158 ASSERT(result == 0);
159}
160
161
lrn@chromium.orgd4e9e222011-08-03 12:01:58 +0000162void OS::ProtectCode(void* address, const size_t size) {
163 DWORD old_protect;
164 VirtualProtect(address, size, PAGE_EXECUTE_READ, &old_protect);
165}
166
167
168void OS::Guard(void* address, const size_t size) {
169 DWORD oldprotect;
170 VirtualProtect(address, size, PAGE_READONLY | PAGE_GUARD, &oldprotect);
171}
172
173
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000174void OS::Sleep(int milliseconds) {
175 unsigned int ms = static_cast<unsigned int>(milliseconds);
176 usleep(1000 * ms);
177}
178
179
yangguo@chromium.orgc03a1922013-02-19 13:55:47 +0000180int OS::NumberOfCores() {
181 return sysconf(_SC_NPROCESSORS_ONLN);
182}
183
184
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000185void OS::Abort() {
186 // Redirect to std abort to signal abnormal program termination.
187 abort();
188}
189
190
191void OS::DebugBreak() {
192 asm("int $3");
193}
194
195
196class 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 virtual int size() { return size_; }
203 private:
204 FILE* file_;
205 void* memory_;
206 int size_;
207};
208
209
210OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
211 FILE* file = fopen(name, "r+");
212 if (file == NULL) return NULL;
213
214 fseek(file, 0, SEEK_END);
215 int size = ftell(file);
216
217 void* memory =
218 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
219 return new PosixMemoryMappedFile(file, memory, size);
220}
221
222
223OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
224 void* initial) {
225 FILE* file = fopen(name, "w+");
226 if (file == NULL) return NULL;
227 int result = fwrite(initial, size, 1, file);
228 if (result < 1) {
229 fclose(file);
230 return NULL;
231 }
232 void* memory =
233 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
234 return new PosixMemoryMappedFile(file, memory, size);
235}
236
237
238PosixMemoryMappedFile::~PosixMemoryMappedFile() {
239 if (memory_) munmap(memory_, size_);
240 fclose(file_);
241}
242
243
244void OS::LogSharedLibraryAddresses() {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000245 // This function assumes that the layout of the file is as follows:
246 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
247 // If we encounter an unexpected situation we abort scanning further entries.
248 FILE* fp = fopen("/proc/self/maps", "r");
249 if (fp == NULL) return;
250
251 // Allocate enough room to be able to store a full file name.
252 const int kLibNameLen = FILENAME_MAX + 1;
253 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
254
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000255 i::Isolate* isolate = ISOLATE;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000256 // This loop will terminate once the scanning hits an EOF.
257 while (true) {
258 uintptr_t start, end;
259 char attr_r, attr_w, attr_x, attr_p;
260 // Parse the addresses and permission bits at the beginning of the line.
261 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
262 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
263
264 int c;
265 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
266 // Found a read-only executable entry. Skip characters until we reach
267 // the beginning of the filename or the end of the line.
268 do {
269 c = getc(fp);
270 } while ((c != EOF) && (c != '\n') && (c != '/'));
271 if (c == EOF) break; // EOF: Was unexpected, just exit.
272
273 // Process the filename if found.
274 if (c == '/') {
275 ungetc(c, fp); // Push the '/' back into the stream to be read below.
276
277 // Read to the end of the line. Exit if the read fails.
278 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
279
280 // Drop the newline character read by fgets. We do not need to check
281 // for a zero-length string because we know that we at least read the
282 // '/' character.
283 lib_name[strlen(lib_name) - 1] = '\0';
284 } else {
285 // No library name found, just record the raw address range.
286 snprintf(lib_name, kLibNameLen,
287 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
288 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000289 LOG(isolate, SharedLibraryEvent(lib_name, start, end));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000290 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000291 // Entry not describing executable data. Skip to end of line to set up
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000292 // reading the next entry.
293 do {
294 c = getc(fp);
295 } while ((c != EOF) && (c != '\n'));
296 if (c == EOF) break;
297 }
298 }
299 free(lib_name);
300 fclose(fp);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000301}
302
303
304void OS::SignalCodeMovingGC() {
305 // Nothing to do on Cygwin.
306}
307
308
309int OS::StackWalk(Vector<OS::StackFrame> frames) {
310 // Not supported on Cygwin.
311 return 0;
312}
313
314
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000315// The VirtualMemory implementation is taken from platform-win32.cc.
316// The mmap-based virtual memory implementation as it is used on most posix
317// platforms does not work well because Cygwin does not support MAP_FIXED.
318// This causes VirtualMemory::Commit to not always commit the memory region
319// specified.
320
321bool VirtualMemory::IsReserved() {
322 return address_ != NULL;
323}
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000324
325
326VirtualMemory::VirtualMemory(size_t size) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000327 address_ = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000328 size_ = size;
329}
330
331
332VirtualMemory::~VirtualMemory() {
333 if (IsReserved()) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000334 if (0 == VirtualFree(address(), 0, MEM_RELEASE)) address_ = NULL;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000335 }
336}
337
338
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000339bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000340 int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
341 if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000342 return false;
343 }
344
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000345 UpdateAllocatedSpaceLimits(address, static_cast<int>(size));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000346 return true;
347}
348
349
350bool VirtualMemory::Uncommit(void* address, size_t size) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000351 ASSERT(IsReserved());
352 return VirtualFree(address, size, MEM_DECOMMIT) != false;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000353}
354
355
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000356bool VirtualMemory::Guard(void* address) {
357 if (NULL == VirtualAlloc(address,
358 OS::CommitPageSize(),
359 MEM_COMMIT,
360 PAGE_READONLY | PAGE_GUARD)) {
361 return false;
362 }
363 return true;
364}
365
366
danno@chromium.org72204d52012-10-31 10:02:10 +0000367bool VirtualMemory::HasLazyCommits() {
368 // TODO(alph): implement for the platform.
369 return false;
370}
371
372
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000373class Thread::PlatformData : public Malloced {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000374 public:
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000375 PlatformData() : thread_(kNoThread) {}
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000376 pthread_t thread_; // Thread handle for pthread.
377};
378
379
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000380
381
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000382Thread::Thread(const Options& options)
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000383 : data_(new PlatformData()),
384 stack_size_(options.stack_size()) {
385 set_name(options.name());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000386}
387
388
389Thread::~Thread() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000390 delete data_;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000391}
392
393
394static void* ThreadEntry(void* arg) {
395 Thread* thread = reinterpret_cast<Thread*>(arg);
396 // This is also initialized by the first argument to pthread_create() but we
397 // don't know which thread will run first (the original thread or the new
398 // one) so we initialize it here too.
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000399 thread->data()->thread_ = pthread_self();
400 ASSERT(thread->data()->thread_ != kNoThread);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000401 thread->Run();
402 return NULL;
403}
404
405
406void Thread::set_name(const char* name) {
407 strncpy(name_, name, sizeof(name_));
408 name_[sizeof(name_) - 1] = '\0';
409}
410
411
412void Thread::Start() {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000413 pthread_attr_t* attr_ptr = NULL;
414 pthread_attr_t attr;
415 if (stack_size_ > 0) {
416 pthread_attr_init(&attr);
417 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
418 attr_ptr = &attr;
419 }
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000420 pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
421 ASSERT(data_->thread_ != kNoThread);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000422}
423
424
425void Thread::Join() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000426 pthread_join(data_->thread_, NULL);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000427}
428
429
430static inline Thread::LocalStorageKey PthreadKeyToLocalKey(
431 pthread_key_t pthread_key) {
432 // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps
433 // because pthread_key_t is a pointer type on Cygwin. This will probably not
434 // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway.
435 STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
436 intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key);
437 return static_cast<Thread::LocalStorageKey>(ptr_key);
438}
439
440
441static inline pthread_key_t LocalKeyToPthreadKey(
442 Thread::LocalStorageKey local_key) {
443 STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t));
444 intptr_t ptr_key = static_cast<intptr_t>(local_key);
445 return reinterpret_cast<pthread_key_t>(ptr_key);
446}
447
448
449Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
450 pthread_key_t key;
451 int result = pthread_key_create(&key, NULL);
452 USE(result);
453 ASSERT(result == 0);
454 return PthreadKeyToLocalKey(key);
455}
456
457
458void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
459 pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
460 int result = pthread_key_delete(pthread_key);
461 USE(result);
462 ASSERT(result == 0);
463}
464
465
466void* Thread::GetThreadLocal(LocalStorageKey key) {
467 pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
468 return pthread_getspecific(pthread_key);
469}
470
471
472void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
473 pthread_key_t pthread_key = LocalKeyToPthreadKey(key);
474 pthread_setspecific(pthread_key, value);
475}
476
477
478void Thread::YieldCPU() {
479 sched_yield();
480}
481
482
483class CygwinMutex : public Mutex {
484 public:
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000485 CygwinMutex() {
486 pthread_mutexattr_t attrs;
487 memset(&attrs, 0, sizeof(attrs));
488
489 int result = pthread_mutexattr_init(&attrs);
490 ASSERT(result == 0);
491 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
492 ASSERT(result == 0);
493 result = pthread_mutex_init(&mutex_, &attrs);
494 ASSERT(result == 0);
495 }
496
497 virtual ~CygwinMutex() { pthread_mutex_destroy(&mutex_); }
498
499 virtual int Lock() {
500 int result = pthread_mutex_lock(&mutex_);
501 return result;
502 }
503
504 virtual int Unlock() {
505 int result = pthread_mutex_unlock(&mutex_);
506 return result;
507 }
508
509 virtual bool TryLock() {
510 int result = pthread_mutex_trylock(&mutex_);
511 // Return false if the lock is busy and locking failed.
512 if (result == EBUSY) {
513 return false;
514 }
515 ASSERT(result == 0); // Verify no other errors.
516 return true;
517 }
518
519 private:
520 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
521};
522
523
524Mutex* OS::CreateMutex() {
525 return new CygwinMutex();
526}
527
528
529class CygwinSemaphore : public Semaphore {
530 public:
531 explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); }
532 virtual ~CygwinSemaphore() { sem_destroy(&sem_); }
533
534 virtual void Wait();
535 virtual bool Wait(int timeout);
536 virtual void Signal() { sem_post(&sem_); }
537 private:
538 sem_t sem_;
539};
540
541
542void CygwinSemaphore::Wait() {
543 while (true) {
544 int result = sem_wait(&sem_);
545 if (result == 0) return; // Successfully got semaphore.
546 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
547 }
548}
549
550
551#ifndef TIMEVAL_TO_TIMESPEC
552#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
553 (ts)->tv_sec = (tv)->tv_sec; \
554 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
555} while (false)
556#endif
557
558
559bool CygwinSemaphore::Wait(int timeout) {
560 const long kOneSecondMicros = 1000000; // NOLINT
561
562 // Split timeout into second and nanosecond parts.
563 struct timeval delta;
564 delta.tv_usec = timeout % kOneSecondMicros;
565 delta.tv_sec = timeout / kOneSecondMicros;
566
567 struct timeval current_time;
568 // Get the current time.
569 if (gettimeofday(&current_time, NULL) == -1) {
570 return false;
571 }
572
573 // Calculate time for end of timeout.
574 struct timeval end_time;
575 timeradd(&current_time, &delta, &end_time);
576
577 struct timespec ts;
578 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
579 // Wait for semaphore signalled or timeout.
580 while (true) {
581 int result = sem_timedwait(&sem_, &ts);
582 if (result == 0) return true; // Successfully got semaphore.
583 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
584 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
585 }
586}
587
588
589Semaphore* OS::CreateSemaphore(int count) {
590 return new CygwinSemaphore(count);
591}
592
593
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000594// ----------------------------------------------------------------------------
595// Cygwin profiler support.
596//
597// On Cygwin we use the same sampler implementation as on win32.
598
599class Sampler::PlatformData : public Malloced {
600 public:
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000601 // Get a handle to the calling thread. This is the thread that we are
602 // going to profile. We need to make a copy of the handle because we are
603 // going to use it in the sampler thread. Using GetThreadHandle() will
604 // not work in this case. We're using OpenThread because DuplicateHandle
605 // for some reason doesn't work in Chrome's sandbox.
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000606 PlatformData() : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
607 THREAD_SUSPEND_RESUME |
608 THREAD_QUERY_INFORMATION,
609 false,
610 GetCurrentThreadId())) {}
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000611
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000612 ~PlatformData() {
613 if (profiled_thread_ != NULL) {
614 CloseHandle(profiled_thread_);
615 profiled_thread_ = NULL;
616 }
617 }
618
619 HANDLE profiled_thread() { return profiled_thread_; }
620
621 private:
622 HANDLE profiled_thread_;
623};
624
625
626class SamplerThread : public Thread {
627 public:
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000628 static const int kSamplerThreadStackSize = 64 * KB;
629
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000630 explicit SamplerThread(int interval)
jkummerow@chromium.orgf7a58842012-02-21 10:08:21 +0000631 : Thread(Thread::Options("SamplerThread", kSamplerThreadStackSize)),
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000632 interval_(interval) {}
633
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000634 static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
635 static void TearDown() { delete mutex_; }
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000636
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000637 static void AddActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000638 ScopedLock lock(mutex_);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000639 SamplerRegistry::AddActiveSampler(sampler);
640 if (instance_ == NULL) {
641 instance_ = new SamplerThread(sampler->interval());
642 instance_->Start();
643 } else {
644 ASSERT(instance_->interval_ == sampler->interval());
645 }
646 }
647
648 static void RemoveActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000649 ScopedLock lock(mutex_);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000650 SamplerRegistry::RemoveActiveSampler(sampler);
651 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000652 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000653 delete instance_;
654 instance_ = NULL;
655 }
656 }
657
658 // Implement Thread::Run().
659 virtual void Run() {
660 SamplerRegistry::State state;
661 while ((state = SamplerRegistry::GetState()) !=
662 SamplerRegistry::HAS_NO_SAMPLERS) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000663 // When CPU profiling is enabled both JavaScript and C++ code is
664 // profiled. We must not suspend.
rossberg@chromium.orgcddc71f2012-12-07 12:40:13 +0000665 if (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS) {
666 SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this);
667 } else {
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +0000668 if (RuntimeProfiler::WaitForSomeIsolateToEnterJS()) continue;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000669 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000670 OS::Sleep(interval_);
671 }
672 }
673
674 static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) {
675 if (!sampler->isolate()->IsInitialized()) return;
676 if (!sampler->IsProfiling()) return;
677 SamplerThread* sampler_thread =
678 reinterpret_cast<SamplerThread*>(raw_sampler_thread);
679 sampler_thread->SampleContext(sampler);
680 }
681
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000682 void SampleContext(Sampler* sampler) {
683 HANDLE profiled_thread = sampler->platform_data()->profiled_thread();
684 if (profiled_thread == NULL) return;
685
686 // Context used for sampling the register state of the profiled thread.
687 CONTEXT context;
688 memset(&context, 0, sizeof(context));
689
rossberg@chromium.org89e18f52012-10-22 13:09:53 +0000690 TickSample sample_obj;
mmassi@chromium.org49a44672012-12-04 13:52:03 +0000691 TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate());
rossberg@chromium.org89e18f52012-10-22 13:09:53 +0000692 if (sample == NULL) sample = &sample_obj;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000693
694 static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
695 if (SuspendThread(profiled_thread) == kSuspendFailed) return;
696 sample->state = sampler->isolate()->current_vm_state();
697
698 context.ContextFlags = CONTEXT_FULL;
699 if (GetThreadContext(profiled_thread, &context) != 0) {
700#if V8_HOST_ARCH_X64
701 sample->pc = reinterpret_cast<Address>(context.Rip);
702 sample->sp = reinterpret_cast<Address>(context.Rsp);
703 sample->fp = reinterpret_cast<Address>(context.Rbp);
704#else
705 sample->pc = reinterpret_cast<Address>(context.Eip);
706 sample->sp = reinterpret_cast<Address>(context.Esp);
707 sample->fp = reinterpret_cast<Address>(context.Ebp);
708#endif
709 sampler->SampleStack(sample);
710 sampler->Tick(sample);
711 }
712 ResumeThread(profiled_thread);
713 }
714
715 const int interval_;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000716
717 // Protects the process wide state below.
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000718 static Mutex* mutex_;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000719 static SamplerThread* instance_;
720
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000721 private:
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000722 DISALLOW_COPY_AND_ASSIGN(SamplerThread);
723};
724
725
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000726Mutex* SamplerThread::mutex_ = NULL;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000727SamplerThread* SamplerThread::instance_ = NULL;
728
729
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000730void OS::SetUp() {
731 // Seed the random number generator.
732 // Convert the current time to a 64-bit integer first, before converting it
733 // to an unsigned. Going directly can cause an overflow and the seed to be
734 // set to all ones. The seed will be identical for different instances that
735 // call this setup code within the same millisecond.
736 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
737 srandom(static_cast<unsigned int>(seed));
738 limit_mutex = CreateMutex();
739 SamplerThread::SetUp();
740}
741
742
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000743void OS::TearDown() {
744 SamplerThread::TearDown();
745 delete limit_mutex;
746}
747
748
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000749Sampler::Sampler(Isolate* isolate, int interval)
750 : isolate_(isolate),
751 interval_(interval),
752 profiling_(false),
753 active_(false),
754 samples_taken_(0) {
755 data_ = new PlatformData;
756}
757
758
759Sampler::~Sampler() {
760 ASSERT(!IsActive());
761 delete data_;
762}
763
764
765void Sampler::Start() {
766 ASSERT(!IsActive());
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000767 SetActive(true);
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000768 SamplerThread::AddActiveSampler(this);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000769}
770
771
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000772void Sampler::Stop() {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000773 ASSERT(IsActive());
774 SamplerThread::RemoveActiveSampler(this);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000775 SetActive(false);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000776}
777
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000778
mstarzinger@chromium.org71fc3462013-02-27 09:34:27 +0000779bool Sampler::CanSampleOnProfilerEventsProcessorThread() {
780 return false;
781}
782
783
784void Sampler::DoSample() {
785}
786
787
788void Sampler::StartProfiling() {
789}
790
791
792void Sampler::StopProfiling() {
793}
794
795
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000796} } // namespace v8::internal