blob: 973329b9b109b3b145444e6a3a9dc650ba659d1b [file] [log] [blame]
Ben Murdoch3fb3ca82011-12-02 17:19:32 +00001// Copyright 2006-2011 the V8 project authors. All rights reserved.
Steve Blockd0582a62009-12-15 09:54:21 +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 OpenBSD goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
30
31#include <pthread.h>
32#include <semaphore.h>
33#include <signal.h>
34#include <sys/time.h>
35#include <sys/resource.h>
36#include <sys/types.h>
37#include <stdlib.h>
38
39#include <sys/types.h> // mmap & munmap
40#include <sys/mman.h> // mmap & munmap
41#include <sys/stat.h> // open
42#include <sys/fcntl.h> // open
43#include <unistd.h> // getpagesize
44#include <execinfo.h> // backtrace, backtrace_symbols
45#include <strings.h> // index
46#include <errno.h>
47#include <stdarg.h>
48#include <limits.h>
49
50#undef MAP_TYPE
51
52#include "v8.h"
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000053#include "v8threads.h"
Steve Blockd0582a62009-12-15 09:54:21 +000054
55#include "platform.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010056#include "vm-state-inl.h"
Steve Blockd0582a62009-12-15 09:54:21 +000057
58
59namespace v8 {
60namespace internal {
61
62// 0 is never a valid thread id on OpenBSD since tids and pids share a
63// name space and pid 0 is used to kill the group (see man 2 kill).
64static const pthread_t kNoThread = (pthread_t) 0;
65
66
67double ceiling(double x) {
68 // Correct as on OS X
69 if (-1.0 < x && x < 0.0) {
70 return -0.0;
71 } else {
72 return ceil(x);
73 }
74}
75
76
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000077static Mutex* limit_mutex = NULL;
78
79
Steve Blockd0582a62009-12-15 09:54:21 +000080void OS::Setup() {
81 // Seed the random number generator.
82 // Convert the current time to a 64-bit integer first, before converting it
83 // to an unsigned. Going directly can cause an overflow and the seed to be
84 // set to all ones. The seed will be identical for different instances that
85 // call this setup code within the same millisecond.
86 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
87 srandom(static_cast<unsigned int>(seed));
Ben Murdoch3fb3ca82011-12-02 17:19:32 +000088 limit_mutex = CreateMutex();
Steve Blockd0582a62009-12-15 09:54:21 +000089}
90
91
Ben Murdoch3bec4d22010-07-22 14:51:16 +010092void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
93 __asm__ __volatile__("" : : : "memory");
94 *ptr = value;
95}
96
97
Steve Blockd0582a62009-12-15 09:54:21 +000098uint64_t OS::CpuFeaturesImpliedByPlatform() {
99 return 0; // OpenBSD runs on anything.
100}
101
102
103int OS::ActivationFrameAlignment() {
104 // 16 byte alignment on OpenBSD
105 return 16;
106}
107
108
Leon Clarked91b9f72010-01-27 17:25:45 +0000109const char* OS::LocalTimezone(double time) {
110 if (isnan(time)) return "";
111 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
112 struct tm* t = localtime(&tv);
113 if (NULL == t) return "";
114 return t->tm_zone;
115}
116
117
118double OS::LocalTimeOffset() {
119 time_t tv = time(NULL);
120 struct tm* t = localtime(&tv);
121 // tm_gmtoff includes any daylight savings offset, so subtract it.
122 return static_cast<double>(t->tm_gmtoff * msPerSecond -
123 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
124}
125
126
Steve Blockd0582a62009-12-15 09:54:21 +0000127// We keep the lowest and highest addresses mapped as a quick way of
128// determining that pointers are outside the heap (used mostly in assertions
129// and verification). The estimate is conservative, ie, not all addresses in
130// 'allocated' space are actually allocated to our heap. The range is
131// [lowest, highest), inclusive on the low and and exclusive on the high end.
132static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
133static void* highest_ever_allocated = reinterpret_cast<void*>(0);
134
135
136static void UpdateAllocatedSpaceLimits(void* address, int size) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000137 ASSERT(limit_mutex != NULL);
138 ScopedLock lock(limit_mutex);
139
Steve Blockd0582a62009-12-15 09:54:21 +0000140 lowest_ever_allocated = Min(lowest_ever_allocated, address);
141 highest_ever_allocated =
142 Max(highest_ever_allocated,
143 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
144}
145
146
147bool OS::IsOutsideAllocatedSpace(void* address) {
148 return address < lowest_ever_allocated || address >= highest_ever_allocated;
149}
150
151
152size_t OS::AllocateAlignment() {
153 return getpagesize();
154}
155
156
157void* OS::Allocate(const size_t requested,
158 size_t* allocated,
159 bool executable) {
160 const size_t msize = RoundUp(requested, getpagesize());
161 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
162 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
163
164 if (mbase == MAP_FAILED) {
Steve Block44f0eee2011-05-26 01:26:41 +0100165 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
Steve Blockd0582a62009-12-15 09:54:21 +0000166 return NULL;
167 }
168 *allocated = msize;
169 UpdateAllocatedSpaceLimits(mbase, msize);
170 return mbase;
171}
172
173
174void OS::Free(void* buf, const size_t length) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000175 // TODO(1240712): munmap has a return value which is ignored here.
Steve Blockd0582a62009-12-15 09:54:21 +0000176 int result = munmap(buf, length);
177 USE(result);
178 ASSERT(result == 0);
179}
180
181
Steve Blockd0582a62009-12-15 09:54:21 +0000182void OS::Sleep(int milliseconds) {
183 unsigned int ms = static_cast<unsigned int>(milliseconds);
184 usleep(1000 * ms);
185}
186
187
188void OS::Abort() {
189 // Redirect to std abort to signal abnormal program termination.
190 abort();
191}
192
193
194void OS::DebugBreak() {
Iain Merrick75681382010-08-19 15:07:18 +0100195#if (defined(__arm__) || defined(__thumb__))
196# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
Steve Blockd0582a62009-12-15 09:54:21 +0000197 asm("bkpt 0");
Iain Merrick75681382010-08-19 15:07:18 +0100198# endif
Steve Blockd0582a62009-12-15 09:54:21 +0000199#else
200 asm("int $3");
201#endif
202}
203
204
205class PosixMemoryMappedFile : public OS::MemoryMappedFile {
206 public:
207 PosixMemoryMappedFile(FILE* file, void* memory, int size)
208 : file_(file), memory_(memory), size_(size) { }
209 virtual ~PosixMemoryMappedFile();
210 virtual void* memory() { return memory_; }
Steve Block1e0659c2011-05-24 12:43:12 +0100211 virtual int size() { return size_; }
Steve Blockd0582a62009-12-15 09:54:21 +0000212 private:
213 FILE* file_;
214 void* memory_;
215 int size_;
216};
217
218
Steve Block1e0659c2011-05-24 12:43:12 +0100219OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100220 FILE* file = fopen(name, "r+");
Steve Block1e0659c2011-05-24 12:43:12 +0100221 if (file == NULL) return NULL;
222
223 fseek(file, 0, SEEK_END);
224 int size = ftell(file);
225
226 void* memory =
227 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
228 return new PosixMemoryMappedFile(file, memory, size);
229}
230
231
Steve Blockd0582a62009-12-15 09:54:21 +0000232OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
233 void* initial) {
234 FILE* file = fopen(name, "w+");
235 if (file == NULL) return NULL;
236 int result = fwrite(initial, size, 1, file);
237 if (result < 1) {
238 fclose(file);
239 return NULL;
240 }
241 void* memory =
242 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
243 return new PosixMemoryMappedFile(file, memory, size);
244}
245
246
247PosixMemoryMappedFile::~PosixMemoryMappedFile() {
248 if (memory_) munmap(memory_, size_);
249 fclose(file_);
250}
251
252
Steve Blockd0582a62009-12-15 09:54:21 +0000253static unsigned StringToLong(char* buffer) {
254 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
255}
Steve Blockd0582a62009-12-15 09:54:21 +0000256
257
258void OS::LogSharedLibraryAddresses() {
Steve Blockd0582a62009-12-15 09:54:21 +0000259 static const int MAP_LENGTH = 1024;
260 int fd = open("/proc/self/maps", O_RDONLY);
261 if (fd < 0) return;
262 while (true) {
263 char addr_buffer[11];
264 addr_buffer[0] = '0';
265 addr_buffer[1] = 'x';
266 addr_buffer[10] = 0;
267 int result = read(fd, addr_buffer + 2, 8);
268 if (result < 8) break;
269 unsigned start = StringToLong(addr_buffer);
270 result = read(fd, addr_buffer + 2, 1);
271 if (result < 1) break;
272 if (addr_buffer[2] != '-') break;
273 result = read(fd, addr_buffer + 2, 8);
274 if (result < 8) break;
275 unsigned end = StringToLong(addr_buffer);
276 char buffer[MAP_LENGTH];
277 int bytes_read = -1;
278 do {
279 bytes_read++;
280 if (bytes_read >= MAP_LENGTH - 1)
281 break;
282 result = read(fd, buffer + bytes_read, 1);
283 if (result < 1) break;
284 } while (buffer[bytes_read] != '\n');
285 buffer[bytes_read] = 0;
286 // Ignore mappings that are not executable.
287 if (buffer[3] != 'x') continue;
288 char* start_of_path = index(buffer, '/');
289 // There may be no filename in this line. Skip to next.
290 if (start_of_path == NULL) continue;
291 buffer[bytes_read] = 0;
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000292 LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end));
Steve Blockd0582a62009-12-15 09:54:21 +0000293 }
294 close(fd);
Steve Blockd0582a62009-12-15 09:54:21 +0000295}
296
297
Ben Murdochf87a2032010-10-22 12:50:53 +0100298void OS::SignalCodeMovingGC() {
299}
300
301
Steve Blockd0582a62009-12-15 09:54:21 +0000302int OS::StackWalk(Vector<OS::StackFrame> frames) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000303 int frames_size = frames.length();
304 ScopedVector<void*> addresses(frames_size);
305
306 int frames_count = backtrace(addresses.start(), frames_size);
307
308 char** symbols = backtrace_symbols(addresses.start(), frames_count);
309 if (symbols == NULL) {
310 return kStackWalkError;
311 }
312
313 for (int i = 0; i < frames_count; i++) {
314 frames[i].address = addresses[i];
315 // Format a text representation of the frame based on the information
316 // available.
317 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
318 "%s",
319 symbols[i]);
320 // Make sure line termination is in place.
321 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
322 }
323
324 free(symbols);
325
326 return frames_count;
Steve Blockd0582a62009-12-15 09:54:21 +0000327}
328
329
330// Constants used for mmap.
331static const int kMmapFd = -1;
332static const int kMmapFdOffset = 0;
333
334
335VirtualMemory::VirtualMemory(size_t size) {
336 address_ = mmap(NULL, size, PROT_NONE,
337 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
338 kMmapFd, kMmapFdOffset);
339 size_ = size;
340}
341
342
343VirtualMemory::~VirtualMemory() {
344 if (IsReserved()) {
345 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
346 }
347}
348
349
350bool VirtualMemory::IsReserved() {
351 return address_ != MAP_FAILED;
352}
353
354
355bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
356 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
357 if (MAP_FAILED == mmap(address, size, prot,
358 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
359 kMmapFd, kMmapFdOffset)) {
360 return false;
361 }
362
363 UpdateAllocatedSpaceLimits(address, size);
364 return true;
365}
366
367
368bool VirtualMemory::Uncommit(void* address, size_t size) {
369 return mmap(address, size, PROT_NONE,
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000370 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
Steve Blockd0582a62009-12-15 09:54:21 +0000371 kMmapFd, kMmapFdOffset) != MAP_FAILED;
372}
373
374
Ben Murdoch8b112d22011-06-08 16:22:53 +0100375class Thread::PlatformData : public Malloced {
Steve Blockd0582a62009-12-15 09:54:21 +0000376 public:
Steve Blockd0582a62009-12-15 09:54:21 +0000377 pthread_t thread_; // Thread handle for pthread.
378};
379
380
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000381Thread::Thread(const Options& options)
382 : data_(new PlatformData),
Steve Block44f0eee2011-05-26 01:26:41 +0100383 stack_size_(options.stack_size) {
384 set_name(options.name);
Steve Block9fac8402011-05-12 15:51:54 +0100385}
386
387
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000388Thread::Thread(const char* name)
389 : data_(new PlatformData),
Steve Block44f0eee2011-05-26 01:26:41 +0100390 stack_size_(0) {
Steve Block9fac8402011-05-12 15:51:54 +0100391 set_name(name);
Steve Blockd0582a62009-12-15 09:54:21 +0000392}
393
394
395Thread::~Thread() {
Ben Murdoch8b112d22011-06-08 16:22:53 +0100396 delete data_;
Steve Blockd0582a62009-12-15 09:54:21 +0000397}
398
399
400static void* ThreadEntry(void* arg) {
401 Thread* thread = reinterpret_cast<Thread*>(arg);
402 // This is also initialized by the first argument to pthread_create() but we
403 // don't know which thread will run first (the original thread or the new
404 // one) so we initialize it here too.
Ben Murdoch8b112d22011-06-08 16:22:53 +0100405 thread->data()->thread_ = pthread_self();
406 ASSERT(thread->data()->thread_ != kNoThread);
Steve Blockd0582a62009-12-15 09:54:21 +0000407 thread->Run();
408 return NULL;
409}
410
411
Steve Block9fac8402011-05-12 15:51:54 +0100412void Thread::set_name(const char* name) {
413 strncpy(name_, name, sizeof(name_));
414 name_[sizeof(name_) - 1] = '\0';
415}
416
417
Steve Blockd0582a62009-12-15 09:54:21 +0000418void Thread::Start() {
Steve Block44f0eee2011-05-26 01:26:41 +0100419 pthread_attr_t* attr_ptr = NULL;
420 pthread_attr_t attr;
421 if (stack_size_ > 0) {
422 pthread_attr_init(&attr);
423 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
424 attr_ptr = &attr;
425 }
Ben Murdoch8b112d22011-06-08 16:22:53 +0100426 pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000427 ASSERT(data_->thread_ != kNoThread);
Steve Blockd0582a62009-12-15 09:54:21 +0000428}
429
430
431void Thread::Join() {
Ben Murdoch8b112d22011-06-08 16:22:53 +0100432 pthread_join(data_->thread_, NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000433}
434
435
436Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
437 pthread_key_t key;
438 int result = pthread_key_create(&key, NULL);
439 USE(result);
440 ASSERT(result == 0);
441 return static_cast<LocalStorageKey>(key);
442}
443
444
445void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
446 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
447 int result = pthread_key_delete(pthread_key);
448 USE(result);
449 ASSERT(result == 0);
450}
451
452
453void* Thread::GetThreadLocal(LocalStorageKey key) {
454 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
455 return pthread_getspecific(pthread_key);
456}
457
458
459void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
460 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
461 pthread_setspecific(pthread_key, value);
462}
463
464
465void Thread::YieldCPU() {
466 sched_yield();
467}
468
469
470class OpenBSDMutex : public Mutex {
471 public:
Steve Blockd0582a62009-12-15 09:54:21 +0000472 OpenBSDMutex() {
473 pthread_mutexattr_t attrs;
474 int result = pthread_mutexattr_init(&attrs);
475 ASSERT(result == 0);
476 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
477 ASSERT(result == 0);
478 result = pthread_mutex_init(&mutex_, &attrs);
479 ASSERT(result == 0);
480 }
481
482 virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); }
483
484 virtual int Lock() {
485 int result = pthread_mutex_lock(&mutex_);
486 return result;
487 }
488
489 virtual int Unlock() {
490 int result = pthread_mutex_unlock(&mutex_);
491 return result;
492 }
493
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000494 virtual bool TryLock() {
495 int result = pthread_mutex_trylock(&mutex_);
496 // Return false if the lock is busy and locking failed.
497 if (result == EBUSY) {
498 return false;
499 }
500 ASSERT(result == 0); // Verify no other errors.
501 return true;
502 }
503
Steve Blockd0582a62009-12-15 09:54:21 +0000504 private:
505 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
506};
507
508
509Mutex* OS::CreateMutex() {
510 return new OpenBSDMutex();
511}
512
513
514class OpenBSDSemaphore : public Semaphore {
515 public:
516 explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
517 virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
518
519 virtual void Wait();
520 virtual bool Wait(int timeout);
521 virtual void Signal() { sem_post(&sem_); }
522 private:
523 sem_t sem_;
524};
525
526
527void OpenBSDSemaphore::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}
534
535
536bool OpenBSDSemaphore::Wait(int timeout) {
537 const long kOneSecondMicros = 1000000; // NOLINT
538
539 // Split timeout into second and nanosecond parts.
540 struct timeval delta;
541 delta.tv_usec = timeout % kOneSecondMicros;
542 delta.tv_sec = timeout / kOneSecondMicros;
543
544 struct timeval current_time;
545 // Get the current time.
546 if (gettimeofday(&current_time, NULL) == -1) {
547 return false;
548 }
549
550 // Calculate time for end of timeout.
551 struct timeval end_time;
552 timeradd(&current_time, &delta, &end_time);
553
554 struct timespec ts;
555 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000556
557 int to = ts.tv_sec;
558
Steve Blockd0582a62009-12-15 09:54:21 +0000559 while (true) {
560 int result = sem_trywait(&sem_);
561 if (result == 0) return true; // Successfully got semaphore.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000562 if (!to) return false; // Timeout.
Steve Blockd0582a62009-12-15 09:54:21 +0000563 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000564 usleep(ts.tv_nsec / 1000);
565 to--;
Steve Blockd0582a62009-12-15 09:54:21 +0000566 }
567}
568
569
570Semaphore* OS::CreateSemaphore(int count) {
571 return new OpenBSDSemaphore(count);
572}
573
574
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000575static pthread_t GetThreadID() {
576 pthread_t thread_id = pthread_self();
577 return thread_id;
Steve Blockd0582a62009-12-15 09:54:21 +0000578}
579
580
581class Sampler::PlatformData : public Malloced {
582 public:
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000583 PlatformData() : vm_tid_(GetThreadID()) {}
584
585 pthread_t vm_tid() const { return vm_tid_; }
586
587 private:
588 pthread_t vm_tid_;
589};
590
591
592static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
593 USE(info);
594 if (signal != SIGPROF) return;
595 Isolate* isolate = Isolate::UncheckedCurrent();
596 if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
597 // We require a fully initialized and entered isolate.
598 return;
599 }
600 if (v8::Locker::IsActive() &&
601 !isolate->thread_manager()->IsLockedByCurrentThread()) {
602 return;
Steve Blockd0582a62009-12-15 09:54:21 +0000603 }
604
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000605 Sampler* sampler = isolate->logger()->sampler();
606 if (sampler == NULL || !sampler->IsActive()) return;
607
608 TickSample sample_obj;
609 TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
610 if (sample == NULL) sample = &sample_obj;
611
612 // Extracting the sample from the context is extremely machine dependent.
613 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
614 sample->state = isolate->current_vm_state();
615#if V8_HOST_ARCH_IA32
616 sample->pc = reinterpret_cast<Address>(ucontext->sc_eip);
617 sample->sp = reinterpret_cast<Address>(ucontext->sc_esp);
618 sample->fp = reinterpret_cast<Address>(ucontext->sc_ebp);
619#elif V8_HOST_ARCH_X64
620 sample->pc = reinterpret_cast<Address>(ucontext->sc_rip);
621 sample->sp = reinterpret_cast<Address>(ucontext->sc_rsp);
622 sample->fp = reinterpret_cast<Address>(ucontext->sc_rbp);
623#elif V8_HOST_ARCH_ARM
624 sample->pc = reinterpret_cast<Address>(ucontext->sc_r15);
625 sample->sp = reinterpret_cast<Address>(ucontext->sc_r13);
626 sample->fp = reinterpret_cast<Address>(ucontext->sc_r11);
627#endif
628 sampler->SampleStack(sample);
629 sampler->Tick(sample);
630}
631
632
633class SignalSender : public Thread {
634 public:
635 enum SleepInterval {
636 HALF_INTERVAL,
637 FULL_INTERVAL
638 };
639
640 explicit SignalSender(int interval)
641 : Thread("SignalSender"),
642 interval_(interval) {}
643
644 static void AddActiveSampler(Sampler* sampler) {
645 ScopedLock lock(mutex_);
646 SamplerRegistry::AddActiveSampler(sampler);
647 if (instance_ == NULL) {
648 // Install a signal handler.
649 struct sigaction sa;
650 sa.sa_sigaction = ProfilerSignalHandler;
651 sigemptyset(&sa.sa_mask);
652 sa.sa_flags = SA_RESTART | SA_SIGINFO;
653 signal_handler_installed_ =
654 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
655
656 // Start a thread that sends SIGPROF signal to VM threads.
657 instance_ = new SignalSender(sampler->interval());
658 instance_->Start();
659 } else {
660 ASSERT(instance_->interval_ == sampler->interval());
661 }
662 }
663
664 static void RemoveActiveSampler(Sampler* sampler) {
665 ScopedLock lock(mutex_);
666 SamplerRegistry::RemoveActiveSampler(sampler);
667 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
668 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
669 delete instance_;
670 instance_ = NULL;
671
672 // Restore the old signal handler.
673 if (signal_handler_installed_) {
674 sigaction(SIGPROF, &old_signal_handler_, 0);
675 signal_handler_installed_ = false;
676 }
677 }
678 }
679
680 // Implement Thread::Run().
681 virtual void Run() {
682 SamplerRegistry::State state;
683 while ((state = SamplerRegistry::GetState()) !=
684 SamplerRegistry::HAS_NO_SAMPLERS) {
685 bool cpu_profiling_enabled =
686 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
687 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
688 // When CPU profiling is enabled both JavaScript and C++ code is
689 // profiled. We must not suspend.
690 if (!cpu_profiling_enabled) {
691 if (rate_limiter_.SuspendIfNecessary()) continue;
692 }
693 if (cpu_profiling_enabled && runtime_profiler_enabled) {
694 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
695 return;
696 }
697 Sleep(HALF_INTERVAL);
698 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
699 return;
700 }
701 Sleep(HALF_INTERVAL);
702 } else {
703 if (cpu_profiling_enabled) {
704 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
705 this)) {
706 return;
707 }
708 }
709 if (runtime_profiler_enabled) {
710 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
711 NULL)) {
712 return;
713 }
714 }
715 Sleep(FULL_INTERVAL);
716 }
717 }
718 }
719
720 static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
721 if (!sampler->IsProfiling()) return;
722 SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
723 sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
724 }
725
726 static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
727 if (!sampler->isolate()->IsInitialized()) return;
728 sampler->isolate()->runtime_profiler()->NotifyTick();
729 }
730
731 void SendProfilingSignal(pthread_t tid) {
732 if (!signal_handler_installed_) return;
733 pthread_kill(tid, SIGPROF);
734 }
735
736 void Sleep(SleepInterval full_or_half) {
737 // Convert ms to us and subtract 100 us to compensate delays
738 // occuring during signal delivery.
739 useconds_t interval = interval_ * 1000 - 100;
740 if (full_or_half == HALF_INTERVAL) interval /= 2;
741 int result = usleep(interval);
742#ifdef DEBUG
743 if (result != 0 && errno != EINTR) {
744 fprintf(stderr,
745 "SignalSender usleep error; interval = %u, errno = %d\n",
746 interval,
747 errno);
748 ASSERT(result == 0 || errno == EINTR);
749 }
750#endif
751 USE(result);
752 }
753
754 const int interval_;
755 RuntimeProfilerRateLimiter rate_limiter_;
756
757 // Protects the process wide state below.
758 static Mutex* mutex_;
759 static SignalSender* instance_;
760 static bool signal_handler_installed_;
761 static struct sigaction old_signal_handler_;
762
763 DISALLOW_COPY_AND_ASSIGN(SignalSender);
Steve Blockd0582a62009-12-15 09:54:21 +0000764};
765
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000766Mutex* SignalSender::mutex_ = OS::CreateMutex();
767SignalSender* SignalSender::instance_ = NULL;
768struct sigaction SignalSender::old_signal_handler_;
769bool SignalSender::signal_handler_installed_ = false;
770
Steve Blockd0582a62009-12-15 09:54:21 +0000771
Steve Block44f0eee2011-05-26 01:26:41 +0100772Sampler::Sampler(Isolate* isolate, int interval)
773 : isolate_(isolate),
774 interval_(interval),
Ben Murdochb0fe1622011-05-05 13:52:32 +0100775 profiling_(false),
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800776 active_(false),
777 samples_taken_(0) {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000778 data_ = new PlatformData;
Steve Blockd0582a62009-12-15 09:54:21 +0000779}
780
781
782Sampler::~Sampler() {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000783 ASSERT(!IsActive());
Steve Blockd0582a62009-12-15 09:54:21 +0000784 delete data_;
785}
786
787
788void Sampler::Start() {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000789 ASSERT(!IsActive());
790 SetActive(true);
791 SignalSender::AddActiveSampler(this);
Steve Blockd0582a62009-12-15 09:54:21 +0000792}
793
794
795void Sampler::Stop() {
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000796 ASSERT(IsActive());
797 SignalSender::RemoveActiveSampler(this);
798 SetActive(false);
Steve Blockd0582a62009-12-15 09:54:21 +0000799}
800
Steve Blockd0582a62009-12-15 09:54:21 +0000801
802} } // namespace v8::internal