blob: 4248ea214faa9b7d2c23ec4527d668d4ce542e2c [file] [log] [blame]
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001// Copyright 2012 the V8 project authors. All rights reserved.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +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 Solaris 10 goes here. For the POSIX comaptible
29// parts the implementation is in platform-posix.cc.
30
31#ifdef __sparc
32# error "V8 does not support the SPARC CPU architecture."
33#endif
34
35#include <sys/stack.h> // for stack alignment
36#include <unistd.h> // getpagesize(), usleep()
37#include <sys/mman.h> // mmap()
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000038#include <ucontext.h> // walkstack(), getcontext()
39#include <dlfcn.h> // dladdr
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000040#include <pthread.h>
41#include <sched.h> // for sched_yield
42#include <semaphore.h>
43#include <time.h>
44#include <sys/time.h> // gettimeofday(), timeradd()
45#include <errno.h>
46#include <ieeefp.h> // finite()
47#include <signal.h> // sigemptyset(), etc
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000048#include <sys/regset.h>
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000049
50
51#undef MAP_TYPE
52
53#include "v8.h"
54
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000055#include "platform-posix.h"
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000056#include "platform.h"
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +000057#include "v8threads.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000058#include "vm-state-inl.h"
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000059
60
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000061// It seems there is a bug in some Solaris distributions (experienced in
62// SunOS 5.10 Generic_141445-09) which make it difficult or impossible to
63// access signbit() despite the availability of other C99 math functions.
64#ifndef signbit
65// Test sign - usually defined in math.h
66int signbit(double x) {
67 // We need to take care of the special case of both positive and negative
68 // versions of zero.
69 if (x == 0) {
70 return fpclass(x) & FP_NZERO;
71 } else {
72 // This won't detect negative NaN but that should be okay since we don't
73 // assume that behavior.
74 return x < 0;
75 }
76}
77#endif // signbit
78
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000079namespace v8 {
80namespace internal {
81
82
83// 0 is never a valid thread id on Solaris since the main thread is 1 and
84// subsequent have their ids incremented from there
85static const pthread_t kNoThread = (pthread_t) 0;
86
87
88double ceiling(double x) {
89 return ceil(x);
90}
91
92
ricow@chromium.org4f693d62011-07-04 14:01:31 +000093static Mutex* limit_mutex = NULL;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000094
95
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000096void OS::PostSetUp() {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +000097 POSIXPostSetUp();
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000098}
99
100
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000101uint64_t OS::CpuFeaturesImpliedByPlatform() {
102 return 0; // Solaris runs on a lot of things.
103}
104
105
106int OS::ActivationFrameAlignment() {
vegorov@chromium.org3cf47312011-06-29 13:20:01 +0000107 // GCC generates code that requires 16 byte alignment such as movdqa.
108 return Max(STACK_ALIGN, 16);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000109}
110
111
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000112void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
113 __asm__ __volatile__("" : : : "memory");
114 *ptr = value;
115}
116
117
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000118const char* OS::LocalTimezone(double time) {
119 if (isnan(time)) return "";
120 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
121 struct tm* t = localtime(&tv);
122 if (NULL == t) return "";
123 return tzname[0]; // The location of the timezone string on Solaris.
124}
125
126
127double OS::LocalTimeOffset() {
128 // On Solaris, struct tm does not contain a tm_gmtoff field.
129 time_t utc = time(NULL);
130 ASSERT(utc != -1);
131 struct tm* loc = localtime(&utc);
132 ASSERT(loc != NULL);
133 return static_cast<double>((mktime(loc) - utc) * msPerSecond);
134}
135
136
137// We keep the lowest and highest addresses mapped as a quick way of
138// determining that pointers are outside the heap (used mostly in assertions
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000139// and verification). The estimate is conservative, i.e., not all addresses in
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000140// 'allocated' space are actually allocated to our heap. The range is
141// [lowest, highest), inclusive on the low and and exclusive on the high end.
142static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
143static void* highest_ever_allocated = reinterpret_cast<void*>(0);
144
145
146static void UpdateAllocatedSpaceLimits(void* address, int size) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000147 ASSERT(limit_mutex != NULL);
148 ScopedLock lock(limit_mutex);
149
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000150 lowest_ever_allocated = Min(lowest_ever_allocated, address);
151 highest_ever_allocated =
152 Max(highest_ever_allocated,
153 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
154}
155
156
157bool OS::IsOutsideAllocatedSpace(void* address) {
158 return address < lowest_ever_allocated || address >= highest_ever_allocated;
159}
160
161
162size_t OS::AllocateAlignment() {
163 return static_cast<size_t>(getpagesize());
164}
165
166
167void* OS::Allocate(const size_t requested,
168 size_t* allocated,
169 bool is_executable) {
170 const size_t msize = RoundUp(requested, getpagesize());
171 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
172 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
173
174 if (mbase == MAP_FAILED) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000175 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000176 return NULL;
177 }
178 *allocated = msize;
179 UpdateAllocatedSpaceLimits(mbase, msize);
180 return mbase;
181}
182
183
184void OS::Free(void* address, const size_t size) {
185 // TODO(1240712): munmap has a return value which is ignored here.
186 int result = munmap(address, size);
187 USE(result);
188 ASSERT(result == 0);
189}
190
191
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000192void OS::Sleep(int milliseconds) {
193 useconds_t ms = static_cast<useconds_t>(milliseconds);
194 usleep(1000 * ms);
195}
196
197
198void OS::Abort() {
199 // Redirect to std abort to signal abnormal program termination.
200 abort();
201}
202
203
204void OS::DebugBreak() {
205 asm("int $3");
206}
207
208
209class PosixMemoryMappedFile : public OS::MemoryMappedFile {
210 public:
211 PosixMemoryMappedFile(FILE* file, void* memory, int size)
212 : file_(file), memory_(memory), size_(size) { }
213 virtual ~PosixMemoryMappedFile();
214 virtual void* memory() { return memory_; }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000215 virtual int size() { return size_; }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000216 private:
217 FILE* file_;
218 void* memory_;
219 int size_;
220};
221
222
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000223OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000224 FILE* file = fopen(name, "r+");
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000225 if (file == NULL) return NULL;
226
227 fseek(file, 0, SEEK_END);
228 int size = ftell(file);
229
230 void* memory =
231 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
232 return new PosixMemoryMappedFile(file, memory, size);
233}
234
235
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000236OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
237 void* initial) {
238 FILE* file = fopen(name, "w+");
239 if (file == NULL) return NULL;
240 int result = fwrite(initial, size, 1, file);
241 if (result < 1) {
242 fclose(file);
243 return NULL;
244 }
245 void* memory =
246 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
247 return new PosixMemoryMappedFile(file, memory, size);
248}
249
250
251PosixMemoryMappedFile::~PosixMemoryMappedFile() {
252 if (memory_) munmap(memory_, size_);
253 fclose(file_);
254}
255
256
257void OS::LogSharedLibraryAddresses() {
258}
259
260
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000261void OS::SignalCodeMovingGC() {
262}
263
264
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000265struct StackWalker {
266 Vector<OS::StackFrame>& frames;
267 int index;
268};
269
270
271static int StackWalkCallback(uintptr_t pc, int signo, void* data) {
272 struct StackWalker* walker = static_cast<struct StackWalker*>(data);
273 Dl_info info;
274
275 int i = walker->index;
276
277 walker->frames[i].address = reinterpret_cast<void*>(pc);
278
279 // Make sure line termination is in place.
280 walker->frames[i].text[OS::kStackWalkMaxTextLen - 1] = '\0';
281
282 Vector<char> text = MutableCStrVector(walker->frames[i].text,
283 OS::kStackWalkMaxTextLen);
284
285 if (dladdr(reinterpret_cast<void*>(pc), &info) == 0) {
286 OS::SNPrintF(text, "[0x%p]", pc);
287 } else if ((info.dli_fname != NULL && info.dli_sname != NULL)) {
288 // We have symbol info.
289 OS::SNPrintF(text, "%s'%s+0x%x", info.dli_fname, info.dli_sname, pc);
290 } else {
291 // No local symbol info.
292 OS::SNPrintF(text,
293 "%s'0x%p [0x%p]",
294 info.dli_fname,
295 pc - reinterpret_cast<uintptr_t>(info.dli_fbase),
296 pc);
297 }
298 walker->index++;
299 return 0;
300}
301
302
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000303int OS::StackWalk(Vector<OS::StackFrame> frames) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000304 ucontext_t ctx;
305 struct StackWalker walker = { frames, 0 };
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000306
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000307 if (getcontext(&ctx) < 0) return kStackWalkError;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000308
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000309 if (!walkcontext(&ctx, StackWalkCallback, &walker)) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000310 return kStackWalkError;
311 }
312
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000313 return walker.index;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000314}
315
316
317// Constants used for mmap.
318static const int kMmapFd = -1;
319static const int kMmapFdOffset = 0;
320
321
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000322VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
323
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000324VirtualMemory::VirtualMemory(size_t size) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000325 address_ = ReserveRegion(size);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000326 size_ = size;
327}
328
329
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000330VirtualMemory::VirtualMemory(size_t size, size_t alignment)
331 : address_(NULL), size_(0) {
332 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
333 size_t request_size = RoundUp(size + alignment,
334 static_cast<intptr_t>(OS::AllocateAlignment()));
335 void* reservation = mmap(OS::GetRandomMmapAddr(),
336 request_size,
337 PROT_NONE,
338 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
339 kMmapFd,
340 kMmapFdOffset);
341 if (reservation == MAP_FAILED) return;
342
343 Address base = static_cast<Address>(reservation);
344 Address aligned_base = RoundUp(base, alignment);
345 ASSERT_LE(base, aligned_base);
346
347 // Unmap extra memory reserved before and after the desired block.
348 if (aligned_base != base) {
349 size_t prefix_size = static_cast<size_t>(aligned_base - base);
350 OS::Free(base, prefix_size);
351 request_size -= prefix_size;
352 }
353
354 size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
355 ASSERT_LE(aligned_size, request_size);
356
357 if (aligned_size != request_size) {
358 size_t suffix_size = request_size - aligned_size;
359 OS::Free(aligned_base + aligned_size, suffix_size);
360 request_size -= suffix_size;
361 }
362
363 ASSERT(aligned_size == request_size);
364
365 address_ = static_cast<void*>(aligned_base);
366 size_ = aligned_size;
367}
368
369
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000370VirtualMemory::~VirtualMemory() {
371 if (IsReserved()) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000372 bool result = ReleaseRegion(address(), size());
373 ASSERT(result);
374 USE(result);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000375 }
376}
377
378
379bool VirtualMemory::IsReserved() {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000380 return address_ != NULL;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000381}
382
383
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000384void VirtualMemory::Reset() {
385 address_ = NULL;
386 size_ = 0;
387}
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000388
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000389
390bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
391 return CommitRegion(address, size, is_executable);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000392}
393
394
395bool VirtualMemory::Uncommit(void* address, size_t size) {
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000396 return UncommitRegion(address, size);
397}
398
399
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000400bool VirtualMemory::Guard(void* address) {
401 OS::Guard(address, OS::CommitPageSize());
402 return true;
403}
404
405
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000406void* VirtualMemory::ReserveRegion(size_t size) {
407 void* result = mmap(OS::GetRandomMmapAddr(),
408 size,
409 PROT_NONE,
410 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
411 kMmapFd,
412 kMmapFdOffset);
413
414 if (result == MAP_FAILED) return NULL;
415
416 return result;
417}
418
419
420bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
421 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
422 if (MAP_FAILED == mmap(base,
423 size,
424 prot,
425 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
426 kMmapFd,
427 kMmapFdOffset)) {
428 return false;
429 }
430
431 UpdateAllocatedSpaceLimits(base, size);
432 return true;
433}
434
435
436bool VirtualMemory::UncommitRegion(void* base, size_t size) {
437 return mmap(base,
438 size,
439 PROT_NONE,
440 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
441 kMmapFd,
442 kMmapFdOffset) != MAP_FAILED;
443}
444
445
446bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
447 return munmap(base, size) == 0;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000448}
449
450
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000451class Thread::PlatformData : public Malloced {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000452 public:
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000453 PlatformData() : thread_(kNoThread) { }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000454
455 pthread_t thread_; // Thread handle for pthread.
456};
457
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000458
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000459Thread::Thread(const Options& options)
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000460 : data_(new PlatformData()),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000461 stack_size_(options.stack_size()) {
462 set_name(options.name());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000463}
464
465
466Thread::~Thread() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000467 delete data_;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000468}
469
470
471static void* ThreadEntry(void* arg) {
472 Thread* thread = reinterpret_cast<Thread*>(arg);
473 // This is also initialized by the first argument to pthread_create() but we
474 // don't know which thread will run first (the original thread or the new
475 // one) so we initialize it here too.
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000476 thread->data()->thread_ = pthread_self();
477 ASSERT(thread->data()->thread_ != kNoThread);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000478 thread->Run();
479 return NULL;
480}
481
482
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000483void Thread::set_name(const char* name) {
484 strncpy(name_, name, sizeof(name_));
485 name_[sizeof(name_) - 1] = '\0';
486}
487
488
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000489void Thread::Start() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000490 pthread_attr_t attr;
491 if (stack_size_ > 0) {
492 pthread_attr_init(&attr);
493 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000494 }
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000495 pthread_create(&data_->thread_, NULL, ThreadEntry, this);
496 ASSERT(data_->thread_ != kNoThread);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000497}
498
499
500void Thread::Join() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000501 pthread_join(data_->thread_, NULL);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000502}
503
504
505Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
506 pthread_key_t key;
507 int result = pthread_key_create(&key, NULL);
508 USE(result);
509 ASSERT(result == 0);
510 return static_cast<LocalStorageKey>(key);
511}
512
513
514void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
515 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
516 int result = pthread_key_delete(pthread_key);
517 USE(result);
518 ASSERT(result == 0);
519}
520
521
522void* Thread::GetThreadLocal(LocalStorageKey key) {
523 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
524 return pthread_getspecific(pthread_key);
525}
526
527
528void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
529 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
530 pthread_setspecific(pthread_key, value);
531}
532
533
534void Thread::YieldCPU() {
535 sched_yield();
536}
537
538
539class SolarisMutex : public Mutex {
540 public:
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000541 SolarisMutex() {
542 pthread_mutexattr_t attr;
543 pthread_mutexattr_init(&attr);
544 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
545 pthread_mutex_init(&mutex_, &attr);
546 }
547
548 ~SolarisMutex() { pthread_mutex_destroy(&mutex_); }
549
550 int Lock() { return pthread_mutex_lock(&mutex_); }
551
552 int Unlock() { return pthread_mutex_unlock(&mutex_); }
553
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000554 virtual bool TryLock() {
555 int result = pthread_mutex_trylock(&mutex_);
556 // Return false if the lock is busy and locking failed.
557 if (result == EBUSY) {
558 return false;
559 }
560 ASSERT(result == 0); // Verify no other errors.
561 return true;
562 }
563
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000564 private:
565 pthread_mutex_t mutex_;
566};
567
568
569Mutex* OS::CreateMutex() {
570 return new SolarisMutex();
571}
572
573
574class SolarisSemaphore : public Semaphore {
575 public:
576 explicit SolarisSemaphore(int count) { sem_init(&sem_, 0, count); }
577 virtual ~SolarisSemaphore() { sem_destroy(&sem_); }
578
579 virtual void Wait();
580 virtual bool Wait(int timeout);
581 virtual void Signal() { sem_post(&sem_); }
582 private:
583 sem_t sem_;
584};
585
586
587void SolarisSemaphore::Wait() {
588 while (true) {
589 int result = sem_wait(&sem_);
590 if (result == 0) return; // Successfully got semaphore.
591 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
592 }
593}
594
595
596#ifndef TIMEVAL_TO_TIMESPEC
597#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
598 (ts)->tv_sec = (tv)->tv_sec; \
599 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
600} while (false)
601#endif
602
603
604#ifndef timeradd
605#define timeradd(a, b, result) \
606 do { \
607 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
608 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
609 if ((result)->tv_usec >= 1000000) { \
610 ++(result)->tv_sec; \
611 (result)->tv_usec -= 1000000; \
612 } \
613 } while (0)
614#endif
615
616
617bool SolarisSemaphore::Wait(int timeout) {
618 const long kOneSecondMicros = 1000000; // NOLINT
619
620 // Split timeout into second and nanosecond parts.
621 struct timeval delta;
622 delta.tv_usec = timeout % kOneSecondMicros;
623 delta.tv_sec = timeout / kOneSecondMicros;
624
625 struct timeval current_time;
626 // Get the current time.
627 if (gettimeofday(&current_time, NULL) == -1) {
628 return false;
629 }
630
631 // Calculate time for end of timeout.
632 struct timeval end_time;
633 timeradd(&current_time, &delta, &end_time);
634
635 struct timespec ts;
636 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
637 // Wait for semaphore signalled or timeout.
638 while (true) {
639 int result = sem_timedwait(&sem_, &ts);
640 if (result == 0) return true; // Successfully got semaphore.
641 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
642 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
643 }
644}
645
646
647Semaphore* OS::CreateSemaphore(int count) {
648 return new SolarisSemaphore(count);
649}
650
651
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000652static pthread_t GetThreadID() {
653 return pthread_self();
654}
655
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000656static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
657 USE(info);
658 if (signal != SIGPROF) return;
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000659 Isolate* isolate = Isolate::UncheckedCurrent();
660 if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
661 // We require a fully initialized and entered isolate.
662 return;
663 }
664 if (v8::Locker::IsActive() &&
665 !isolate->thread_manager()->IsLockedByCurrentThread()) {
666 return;
667 }
668
669 Sampler* sampler = isolate->logger()->sampler();
670 if (sampler == NULL || !sampler->IsActive()) return;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000671
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000672 TickSample sample_obj;
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000673 TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000674 if (sample == NULL) sample = &sample_obj;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000675
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000676 // Extracting the sample from the context is extremely machine dependent.
677 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
678 mcontext_t& mcontext = ucontext->uc_mcontext;
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000679 sample->state = isolate->current_vm_state();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000680
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000681 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
682 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
683 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
684
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000685 sampler->SampleStack(sample);
686 sampler->Tick(sample);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000687}
688
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000689class Sampler::PlatformData : public Malloced {
690 public:
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000691 PlatformData() : vm_tid_(GetThreadID()) {}
692
693 pthread_t vm_tid() const { return vm_tid_; }
694
695 private:
696 pthread_t vm_tid_;
697};
698
699
700class SignalSender : public Thread {
701 public:
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000702 enum SleepInterval {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000703 HALF_INTERVAL,
704 FULL_INTERVAL
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000705 };
706
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +0000707 static const int kSignalSenderStackSize = 64 * KB;
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000708
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000709 explicit SignalSender(int interval)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000710 : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000711 interval_(interval) {}
712
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000713 static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
714 static void TearDown() { delete mutex_; }
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000715
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000716 static void InstallSignalHandler() {
717 struct sigaction sa;
718 sa.sa_sigaction = ProfilerSignalHandler;
719 sigemptyset(&sa.sa_mask);
720 sa.sa_flags = SA_RESTART | SA_SIGINFO;
721 signal_handler_installed_ =
722 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000723 }
724
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000725 static void RestoreSignalHandler() {
726 if (signal_handler_installed_) {
727 sigaction(SIGPROF, &old_signal_handler_, 0);
728 signal_handler_installed_ = false;
729 }
730 }
731
732 static void AddActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000733 ScopedLock lock(mutex_);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000734 SamplerRegistry::AddActiveSampler(sampler);
735 if (instance_ == NULL) {
736 // Start a thread that will send SIGPROF signal to VM threads,
737 // when CPU profiling will be enabled.
738 instance_ = new SignalSender(sampler->interval());
739 instance_->Start();
740 } else {
741 ASSERT(instance_->interval_ == sampler->interval());
742 }
743 }
744
745 static void RemoveActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000746 ScopedLock lock(mutex_);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000747 SamplerRegistry::RemoveActiveSampler(sampler);
748 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000749 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000750 delete instance_;
751 instance_ = NULL;
752 RestoreSignalHandler();
753 }
754 }
755
756 // Implement Thread::Run().
757 virtual void Run() {
758 SamplerRegistry::State state;
759 while ((state = SamplerRegistry::GetState()) !=
760 SamplerRegistry::HAS_NO_SAMPLERS) {
761 bool cpu_profiling_enabled =
762 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
763 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
764 if (cpu_profiling_enabled && !signal_handler_installed_) {
765 InstallSignalHandler();
766 } else if (!cpu_profiling_enabled && signal_handler_installed_) {
767 RestoreSignalHandler();
768 }
769
770 // When CPU profiling is enabled both JavaScript and C++ code is
771 // profiled. We must not suspend.
772 if (!cpu_profiling_enabled) {
773 if (rate_limiter_.SuspendIfNecessary()) continue;
774 }
775 if (cpu_profiling_enabled && runtime_profiler_enabled) {
776 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
777 return;
778 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000779 Sleep(HALF_INTERVAL);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000780 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
781 return;
782 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000783 Sleep(HALF_INTERVAL);
784 } else {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000785 if (cpu_profiling_enabled) {
786 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
787 this)) {
788 return;
789 }
790 }
791 if (runtime_profiler_enabled) {
792 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
793 NULL)) {
794 return;
795 }
796 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000797 Sleep(FULL_INTERVAL);
798 }
799 }
800 }
801
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000802 static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
803 if (!sampler->IsProfiling()) return;
804 SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
805 sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
806 }
807
808 static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
809 if (!sampler->isolate()->IsInitialized()) return;
810 sampler->isolate()->runtime_profiler()->NotifyTick();
811 }
812
813 void SendProfilingSignal(pthread_t tid) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000814 if (!signal_handler_installed_) return;
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000815 pthread_kill(tid, SIGPROF);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000816 }
817
818 void Sleep(SleepInterval full_or_half) {
819 // Convert ms to us and subtract 100 us to compensate delays
820 // occuring during signal delivery.
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000821 useconds_t interval = interval_ * 1000 - 100;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000822 if (full_or_half == HALF_INTERVAL) interval /= 2;
823 int result = usleep(interval);
824#ifdef DEBUG
825 if (result != 0 && errno != EINTR) {
826 fprintf(stderr,
827 "SignalSender usleep error; interval = %u, errno = %d\n",
828 interval,
829 errno);
830 ASSERT(result == 0 || errno == EINTR);
831 }
832#endif
833 USE(result);
834 }
835
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000836 const int interval_;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000837 RuntimeProfilerRateLimiter rate_limiter_;
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000838
839 // Protects the process wide state below.
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000840 static Mutex* mutex_;
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000841 static SignalSender* instance_;
842 static bool signal_handler_installed_;
843 static struct sigaction old_signal_handler_;
844
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000845 private:
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000846 DISALLOW_COPY_AND_ASSIGN(SignalSender);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000847};
848
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000849Mutex* SignalSender::mutex_ = NULL;
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000850SignalSender* SignalSender::instance_ = NULL;
851struct sigaction SignalSender::old_signal_handler_;
852bool SignalSender::signal_handler_installed_ = false;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000853
854
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000855void OS::SetUp() {
856 // Seed the random number generator.
857 // Convert the current time to a 64-bit integer first, before converting it
858 // to an unsigned. Going directly will cause an overflow and the seed to be
859 // set to all ones. The seed will be identical for different instances that
860 // call this setup code within the same millisecond.
861 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
862 srandom(static_cast<unsigned int>(seed));
863 limit_mutex = CreateMutex();
864 SignalSender::SetUp();
865}
866
867
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000868void OS::TearDown() {
869 SignalSender::TearDown();
870 delete limit_mutex;
871}
872
873
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000874Sampler::Sampler(Isolate* isolate, int interval)
875 : isolate_(isolate),
876 interval_(interval),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000877 profiling_(false),
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000878 active_(false),
879 samples_taken_(0) {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000880 data_ = new PlatformData;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000881}
882
883
884Sampler::~Sampler() {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000885 ASSERT(!IsActive());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000886 delete data_;
887}
888
889
890void Sampler::Start() {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000891 ASSERT(!IsActive());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000892 SetActive(true);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000893 SignalSender::AddActiveSampler(this);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000894}
895
896
897void Sampler::Stop() {
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000898 ASSERT(IsActive());
899 SignalSender::RemoveActiveSampler(this);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000900 SetActive(false);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000901}
902
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000903} } // namespace v8::internal