blob: 9d9f1b795a4b91848f5fe7d0369b7185761b2d25 [file] [log] [blame]
ager@chromium.orga74f0da2008-12-03 16:05:52 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000028// Platform specific code for FreeBSD goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
ager@chromium.orga74f0da2008-12-03 16:05:52 +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>
ager@chromium.orga74f0da2008-12-03 16:05:52 +000037#include <sys/ucontext.h>
38#include <stdlib.h>
39
40#include <sys/types.h> // mmap & munmap
41#include <sys/mman.h> // mmap & munmap
42#include <sys/stat.h> // open
43#include <sys/fcntl.h> // open
44#include <unistd.h> // getpagesize
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +000045// If you don't have execinfo.h then you need devel/libexecinfo from ports.
ager@chromium.orga74f0da2008-12-03 16:05:52 +000046#include <execinfo.h> // backtrace, backtrace_symbols
47#include <strings.h> // index
48#include <errno.h>
49#include <stdarg.h>
50#include <limits.h>
51
52#undef MAP_TYPE
53
54#include "v8.h"
lrn@chromium.org1c092762011-05-09 09:42:16 +000055#include "v8threads.h"
ager@chromium.orga74f0da2008-12-03 16:05:52 +000056
57#include "platform.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000058#include "vm-state-inl.h"
ager@chromium.orga74f0da2008-12-03 16:05:52 +000059
60
kasperl@chromium.org71affb52009-05-26 05:44:31 +000061namespace v8 {
62namespace internal {
ager@chromium.orga74f0da2008-12-03 16:05:52 +000063
64// 0 is never a valid thread id on FreeBSD since tids and pids share a
65// name space and pid 0 is used to kill the group (see man 2 kill).
66static const pthread_t kNoThread = (pthread_t) 0;
67
68
69double ceiling(double x) {
70 // Correct as on OS X
71 if (-1.0 < x && x < 0.0) {
72 return -0.0;
73 } else {
74 return ceil(x);
75 }
76}
77
78
lrn@chromium.org7516f052011-03-30 08:52:27 +000079static Mutex* limit_mutex = NULL;
80
81
ager@chromium.orga74f0da2008-12-03 16:05:52 +000082void OS::Setup() {
83 // Seed the random number generator.
84 // Convert the current time to a 64-bit integer first, before converting it
85 // to an unsigned. Going directly can cause an overflow and the seed to be
86 // set to all ones. The seed will be identical for different instances that
87 // call this setup code within the same millisecond.
88 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
89 srandom(static_cast<unsigned int>(seed));
lrn@chromium.org7516f052011-03-30 08:52:27 +000090 limit_mutex = CreateMutex();
ager@chromium.orga74f0da2008-12-03 16:05:52 +000091}
92
93
ricow@chromium.org30ce4112010-05-31 10:38:25 +000094void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
95 __asm__ __volatile__("" : : : "memory");
96 *ptr = value;
97}
98
99
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000100uint64_t OS::CpuFeaturesImpliedByPlatform() {
101 return 0; // FreeBSD runs on anything.
102}
103
104
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000105int OS::ActivationFrameAlignment() {
106 // 16 byte alignment on FreeBSD
107 return 16;
108}
109
110
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000111const char* OS::LocalTimezone(double time) {
112 if (isnan(time)) return "";
113 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
114 struct tm* t = localtime(&tv);
115 if (NULL == t) return "";
116 return t->tm_zone;
117}
118
119
120double OS::LocalTimeOffset() {
121 time_t tv = time(NULL);
122 struct tm* t = localtime(&tv);
123 // tm_gmtoff includes any daylight savings offset, so subtract it.
124 return static_cast<double>(t->tm_gmtoff * msPerSecond -
125 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
126}
127
128
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000129// We keep the lowest and highest addresses mapped as a quick way of
130// determining that pointers are outside the heap (used mostly in assertions
131// and verification). The estimate is conservative, ie, not all addresses in
132// 'allocated' space are actually allocated to our heap. The range is
133// [lowest, highest), inclusive on the low and and exclusive on the high end.
134static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
135static void* highest_ever_allocated = reinterpret_cast<void*>(0);
136
137
138static void UpdateAllocatedSpaceLimits(void* address, int size) {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000139 ASSERT(limit_mutex != NULL);
140 ScopedLock lock(limit_mutex);
141
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000142 lowest_ever_allocated = Min(lowest_ever_allocated, address);
143 highest_ever_allocated =
144 Max(highest_ever_allocated,
145 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
146}
147
148
149bool OS::IsOutsideAllocatedSpace(void* address) {
150 return address < lowest_ever_allocated || address >= highest_ever_allocated;
151}
152
153
154size_t OS::AllocateAlignment() {
155 return getpagesize();
156}
157
158
159void* OS::Allocate(const size_t requested,
160 size_t* allocated,
161 bool executable) {
162 const size_t msize = RoundUp(requested, getpagesize());
163 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
164 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
165
166 if (mbase == MAP_FAILED) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000167 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000168 return NULL;
169 }
170 *allocated = msize;
171 UpdateAllocatedSpaceLimits(mbase, msize);
172 return mbase;
173}
174
175
176void OS::Free(void* buf, const size_t length) {
177 // TODO(1240712): munmap has a return value which is ignored here.
ager@chromium.orga1645e22009-09-09 19:27:10 +0000178 int result = munmap(buf, length);
179 USE(result);
180 ASSERT(result == 0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000181}
182
183
184void OS::Sleep(int milliseconds) {
185 unsigned int ms = static_cast<unsigned int>(milliseconds);
186 usleep(1000 * ms);
187}
188
189
190void OS::Abort() {
191 // Redirect to std abort to signal abnormal program termination.
192 abort();
193}
194
195
196void OS::DebugBreak() {
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000197#if (defined(__arm__) || defined(__thumb__))
198# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000199 asm("bkpt 0");
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000200# endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000201#else
202 asm("int $3");
203#endif
204}
205
206
207class PosixMemoryMappedFile : public OS::MemoryMappedFile {
208 public:
209 PosixMemoryMappedFile(FILE* file, void* memory, int size)
210 : file_(file), memory_(memory), size_(size) { }
211 virtual ~PosixMemoryMappedFile();
212 virtual void* memory() { return memory_; }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000213 virtual int size() { return size_; }
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000214 private:
215 FILE* file_;
216 void* memory_;
217 int size_;
218};
219
220
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000221OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000222 FILE* file = fopen(name, "r+");
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000223 if (file == NULL) return NULL;
224
225 fseek(file, 0, SEEK_END);
226 int size = ftell(file);
227
228 void* memory =
229 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
230 return new PosixMemoryMappedFile(file, memory, size);
231}
232
233
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000234OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
235 void* initial) {
236 FILE* file = fopen(name, "w+");
237 if (file == NULL) return NULL;
238 int result = fwrite(initial, size, 1, file);
239 if (result < 1) {
240 fclose(file);
241 return NULL;
242 }
243 void* memory =
244 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
245 return new PosixMemoryMappedFile(file, memory, size);
246}
247
248
249PosixMemoryMappedFile::~PosixMemoryMappedFile() {
250 if (memory_) munmap(memory_, size_);
251 fclose(file_);
252}
253
254
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000255static unsigned StringToLong(char* buffer) {
256 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
257}
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000258
259
260void OS::LogSharedLibraryAddresses() {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000261 static const int MAP_LENGTH = 1024;
262 int fd = open("/proc/self/maps", O_RDONLY);
263 if (fd < 0) return;
264 while (true) {
265 char addr_buffer[11];
266 addr_buffer[0] = '0';
267 addr_buffer[1] = 'x';
268 addr_buffer[10] = 0;
269 int result = read(fd, addr_buffer + 2, 8);
270 if (result < 8) break;
271 unsigned start = StringToLong(addr_buffer);
272 result = read(fd, addr_buffer + 2, 1);
273 if (result < 1) break;
274 if (addr_buffer[2] != '-') break;
275 result = read(fd, addr_buffer + 2, 8);
276 if (result < 8) break;
277 unsigned end = StringToLong(addr_buffer);
278 char buffer[MAP_LENGTH];
279 int bytes_read = -1;
280 do {
281 bytes_read++;
282 if (bytes_read >= MAP_LENGTH - 1)
283 break;
284 result = read(fd, buffer + bytes_read, 1);
285 if (result < 1) break;
286 } while (buffer[bytes_read] != '\n');
287 buffer[bytes_read] = 0;
288 // Ignore mappings that are not executable.
289 if (buffer[3] != 'x') continue;
290 char* start_of_path = index(buffer, '/');
291 // There may be no filename in this line. Skip to next.
292 if (start_of_path == NULL) continue;
293 buffer[bytes_read] = 0;
lrn@chromium.org7516f052011-03-30 08:52:27 +0000294 LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end));
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000295 }
296 close(fd);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000297}
298
299
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000300void OS::SignalCodeMovingGC() {
301}
302
303
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000304int OS::StackWalk(Vector<OS::StackFrame> frames) {
305 int frames_size = frames.length();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000306 ScopedVector<void*> addresses(frames_size);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000307
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000308 int frames_count = backtrace(addresses.start(), frames_size);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000309
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000310 char** symbols = backtrace_symbols(addresses.start(), frames_count);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000311 if (symbols == NULL) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000312 return kStackWalkError;
313 }
314
315 for (int i = 0; i < frames_count; i++) {
316 frames[i].address = addresses[i];
317 // Format a text representation of the frame based on the information
318 // available.
319 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
320 "%s",
321 symbols[i]);
322 // Make sure line termination is in place.
323 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
324 }
325
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000326 free(symbols);
327
328 return frames_count;
329}
330
331
332// Constants used for mmap.
333static const int kMmapFd = -1;
334static const int kMmapFdOffset = 0;
335
336
337VirtualMemory::VirtualMemory(size_t size) {
338 address_ = mmap(NULL, size, PROT_NONE,
339 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
340 kMmapFd, kMmapFdOffset);
341 size_ = size;
342}
343
344
345VirtualMemory::~VirtualMemory() {
346 if (IsReserved()) {
347 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
348 }
349}
350
351
352bool VirtualMemory::IsReserved() {
353 return address_ != MAP_FAILED;
354}
355
356
357bool VirtualMemory::Commit(void* address, size_t size, bool executable) {
358 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
359 if (MAP_FAILED == mmap(address, size, prot,
360 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
361 kMmapFd, kMmapFdOffset)) {
362 return false;
363 }
364
365 UpdateAllocatedSpaceLimits(address, size);
366 return true;
367}
368
369
370bool VirtualMemory::Uncommit(void* address, size_t size) {
371 return mmap(address, size, PROT_NONE,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000372 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000373 kMmapFd, kMmapFdOffset) != MAP_FAILED;
374}
375
376
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000377class Thread::PlatformData : public Malloced {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000378 public:
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000379 pthread_t thread_; // Thread handle for pthread.
380};
381
382
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000383Thread::Thread(const Options& options)
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000384 : data_(new PlatformData),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000385 stack_size_(options.stack_size) {
386 set_name(options.name);
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000387}
388
389
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000390Thread::Thread(const char* name)
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000391 : data_(new PlatformData),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000392 stack_size_(0) {
ager@chromium.org0ee099b2011-01-25 14:06:47 +0000393 set_name(name);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000394}
395
396
397Thread::~Thread() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000398 delete data_;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000399}
400
401
402static void* ThreadEntry(void* arg) {
403 Thread* thread = reinterpret_cast<Thread*>(arg);
404 // This is also initialized by the first argument to pthread_create() but we
405 // don't know which thread will run first (the original thread or the new
406 // one) so we initialize it here too.
lrn@chromium.org1c092762011-05-09 09:42:16 +0000407 thread->data()->thread_ = pthread_self();
408 ASSERT(thread->data()->thread_ != kNoThread);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000409 thread->Run();
410 return NULL;
411}
412
413
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000414void Thread::set_name(const char* name) {
415 strncpy(name_, name, sizeof(name_));
416 name_[sizeof(name_) - 1] = '\0';
417}
418
419
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000420void Thread::Start() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000421 pthread_attr_t* attr_ptr = NULL;
422 pthread_attr_t attr;
423 if (stack_size_ > 0) {
424 pthread_attr_init(&attr);
425 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
426 attr_ptr = &attr;
427 }
lrn@chromium.org1c092762011-05-09 09:42:16 +0000428 pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
429 ASSERT(data_->thread_ != kNoThread);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000430}
431
432
433void Thread::Join() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000434 pthread_join(data_->thread_, NULL);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000435}
436
437
438Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
439 pthread_key_t key;
440 int result = pthread_key_create(&key, NULL);
441 USE(result);
442 ASSERT(result == 0);
443 return static_cast<LocalStorageKey>(key);
444}
445
446
447void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
448 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
449 int result = pthread_key_delete(pthread_key);
450 USE(result);
451 ASSERT(result == 0);
452}
453
454
455void* Thread::GetThreadLocal(LocalStorageKey key) {
456 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
457 return pthread_getspecific(pthread_key);
458}
459
460
461void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
462 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
463 pthread_setspecific(pthread_key, value);
464}
465
466
467void Thread::YieldCPU() {
468 sched_yield();
469}
470
471
472class FreeBSDMutex : public Mutex {
473 public:
474
475 FreeBSDMutex() {
476 pthread_mutexattr_t attrs;
477 int result = pthread_mutexattr_init(&attrs);
478 ASSERT(result == 0);
479 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
480 ASSERT(result == 0);
481 result = pthread_mutex_init(&mutex_, &attrs);
482 ASSERT(result == 0);
483 }
484
485 virtual ~FreeBSDMutex() { pthread_mutex_destroy(&mutex_); }
486
487 virtual int Lock() {
488 int result = pthread_mutex_lock(&mutex_);
489 return result;
490 }
491
492 virtual int Unlock() {
493 int result = pthread_mutex_unlock(&mutex_);
494 return result;
495 }
496
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000497 virtual bool TryLock() {
498 int result = pthread_mutex_trylock(&mutex_);
499 // Return false if the lock is busy and locking failed.
500 if (result == EBUSY) {
501 return false;
502 }
503 ASSERT(result == 0); // Verify no other errors.
504 return true;
505 }
506
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000507 private:
508 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
509};
510
511
512Mutex* OS::CreateMutex() {
513 return new FreeBSDMutex();
514}
515
516
517class FreeBSDSemaphore : public Semaphore {
518 public:
519 explicit FreeBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
520 virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); }
521
522 virtual void Wait();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000523 virtual bool Wait(int timeout);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000524 virtual void Signal() { sem_post(&sem_); }
525 private:
526 sem_t sem_;
527};
528
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000529
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000530void FreeBSDSemaphore::Wait() {
531 while (true) {
532 int result = sem_wait(&sem_);
533 if (result == 0) return; // Successfully got semaphore.
534 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
535 }
536}
537
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000538
539bool FreeBSDSemaphore::Wait(int timeout) {
540 const long kOneSecondMicros = 1000000; // NOLINT
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000541
542 // Split timeout into second and nanosecond parts.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000543 struct timeval delta;
544 delta.tv_usec = timeout % kOneSecondMicros;
545 delta.tv_sec = timeout / kOneSecondMicros;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000546
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000547 struct timeval current_time;
548 // Get the current time.
549 if (gettimeofday(&current_time, NULL) == -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000550 return false;
551 }
552
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000553 // Calculate time for end of timeout.
554 struct timeval end_time;
555 timeradd(&current_time, &delta, &end_time);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000556
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000557 struct timespec ts;
558 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000559 while (true) {
560 int result = sem_timedwait(&sem_, &ts);
561 if (result == 0) return true; // Successfully got semaphore.
562 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
563 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
564 }
565}
566
567
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000568Semaphore* OS::CreateSemaphore(int count) {
569 return new FreeBSDSemaphore(count);
570}
571
ager@chromium.org381abbb2009-02-25 13:23:22 +0000572
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000573static pthread_t GetThreadID() {
574 pthread_t thread_id = pthread_self();
575 return thread_id;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000576}
577
578
579class Sampler::PlatformData : public Malloced {
580 public:
lrn@chromium.org7516f052011-03-30 08:52:27 +0000581 PlatformData() : vm_tid_(GetThreadID()) {}
582
583 pthread_t vm_tid() const { return vm_tid_; }
584
585 private:
586 pthread_t vm_tid_;
587};
588
589
590static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
591 USE(info);
592 if (signal != SIGPROF) return;
593 Isolate* isolate = Isolate::UncheckedCurrent();
594 if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
595 // We require a fully initialized and entered isolate.
596 return;
597 }
vitalyr@chromium.org0ec56d62011-04-15 22:22:08 +0000598 if (v8::Locker::IsActive() &&
599 !isolate->thread_manager()->IsLockedByCurrentThread()) {
600 return;
601 }
602
lrn@chromium.org7516f052011-03-30 08:52:27 +0000603 Sampler* sampler = isolate->logger()->sampler();
604 if (sampler == NULL || !sampler->IsActive()) return;
605
606 TickSample sample_obj;
607 TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
608 if (sample == NULL) sample = &sample_obj;
609
610 // Extracting the sample from the context is extremely machine dependent.
611 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
612 mcontext_t& mcontext = ucontext->uc_mcontext;
613 sample->state = isolate->current_vm_state();
614#if V8_HOST_ARCH_IA32
615 sample->pc = reinterpret_cast<Address>(mcontext.mc_eip);
616 sample->sp = reinterpret_cast<Address>(mcontext.mc_esp);
617 sample->fp = reinterpret_cast<Address>(mcontext.mc_ebp);
618#elif V8_HOST_ARCH_X64
619 sample->pc = reinterpret_cast<Address>(mcontext.mc_rip);
620 sample->sp = reinterpret_cast<Address>(mcontext.mc_rsp);
621 sample->fp = reinterpret_cast<Address>(mcontext.mc_rbp);
622#elif V8_HOST_ARCH_ARM
623 sample->pc = reinterpret_cast<Address>(mcontext.mc_r15);
624 sample->sp = reinterpret_cast<Address>(mcontext.mc_r13);
625 sample->fp = reinterpret_cast<Address>(mcontext.mc_r11);
626#endif
627 sampler->SampleStack(sample);
628 sampler->Tick(sample);
629}
630
631
632class SignalSender : public Thread {
633 public:
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000634 enum SleepInterval {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000635 HALF_INTERVAL,
636 FULL_INTERVAL
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000637 };
638
lrn@chromium.org7516f052011-03-30 08:52:27 +0000639 explicit SignalSender(int interval)
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000640 : Thread("SignalSender"),
lrn@chromium.org7516f052011-03-30 08:52:27 +0000641 interval_(interval) {}
642
643 static void AddActiveSampler(Sampler* sampler) {
644 ScopedLock lock(mutex_);
645 SamplerRegistry::AddActiveSampler(sampler);
646 if (instance_ == NULL) {
647 // Install a signal handler.
648 struct sigaction sa;
649 sa.sa_sigaction = ProfilerSignalHandler;
650 sigemptyset(&sa.sa_mask);
651 sa.sa_flags = SA_RESTART | SA_SIGINFO;
652 signal_handler_installed_ =
653 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
654
655 // Start a thread that sends SIGPROF signal to VM threads.
656 instance_ = new SignalSender(sampler->interval());
657 instance_->Start();
658 } else {
659 ASSERT(instance_->interval_ == sampler->interval());
660 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000661 }
662
lrn@chromium.org7516f052011-03-30 08:52:27 +0000663 static void RemoveActiveSampler(Sampler* sampler) {
664 ScopedLock lock(mutex_);
665 SamplerRegistry::RemoveActiveSampler(sampler);
666 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000667 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
lrn@chromium.org7516f052011-03-30 08:52:27 +0000668 delete instance_;
669 instance_ = NULL;
670
671 // Restore the old signal handler.
672 if (signal_handler_installed_) {
673 sigaction(SIGPROF, &old_signal_handler_, 0);
674 signal_handler_installed_ = false;
675 }
676 }
677 }
678
679 // Implement Thread::Run().
680 virtual void Run() {
681 SamplerRegistry::State state;
682 while ((state = SamplerRegistry::GetState()) !=
683 SamplerRegistry::HAS_NO_SAMPLERS) {
684 bool cpu_profiling_enabled =
685 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
686 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
687 // When CPU profiling is enabled both JavaScript and C++ code is
688 // profiled. We must not suspend.
689 if (!cpu_profiling_enabled) {
690 if (rate_limiter_.SuspendIfNecessary()) continue;
691 }
692 if (cpu_profiling_enabled && runtime_profiler_enabled) {
693 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
694 return;
695 }
696 Sleep(HALF_INTERVAL);
697 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
698 return;
699 }
700 Sleep(HALF_INTERVAL);
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000701 } else {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000702 if (cpu_profiling_enabled) {
703 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
704 this)) {
705 return;
706 }
707 }
708 if (runtime_profiler_enabled) {
709 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
710 NULL)) {
711 return;
712 }
713 }
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000714 Sleep(FULL_INTERVAL);
715 }
716 }
717 }
718
lrn@chromium.org7516f052011-03-30 08:52:27 +0000719 static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
720 if (!sampler->IsProfiling()) return;
721 SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
722 sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
723 }
724
725 static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
726 if (!sampler->isolate()->IsInitialized()) return;
727 sampler->isolate()->runtime_profiler()->NotifyTick();
728 }
729
730 void SendProfilingSignal(pthread_t tid) {
731 if (!signal_handler_installed_) return;
732 pthread_kill(tid, SIGPROF);
733 }
734
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000735 void Sleep(SleepInterval full_or_half) {
736 // Convert ms to us and subtract 100 us to compensate delays
737 // occuring during signal delivery.
lrn@chromium.org7516f052011-03-30 08:52:27 +0000738 useconds_t interval = interval_ * 1000 - 100;
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000739 if (full_or_half == HALF_INTERVAL) interval /= 2;
740 int result = usleep(interval);
741#ifdef DEBUG
742 if (result != 0 && errno != EINTR) {
743 fprintf(stderr,
744 "SignalSender usleep error; interval = %u, errno = %d\n",
745 interval,
746 errno);
747 ASSERT(result == 0 || errno == EINTR);
748 }
749#endif
750 USE(result);
751 }
752
lrn@chromium.org7516f052011-03-30 08:52:27 +0000753 const int interval_;
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000754 RuntimeProfilerRateLimiter rate_limiter_;
lrn@chromium.org7516f052011-03-30 08:52:27 +0000755
756 // Protects the process wide state below.
757 static Mutex* mutex_;
758 static SignalSender* instance_;
759 static bool signal_handler_installed_;
760 static struct sigaction old_signal_handler_;
761
762 DISALLOW_COPY_AND_ASSIGN(SignalSender);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000763};
764
lrn@chromium.org7516f052011-03-30 08:52:27 +0000765Mutex* SignalSender::mutex_ = OS::CreateMutex();
766SignalSender* SignalSender::instance_ = NULL;
767struct sigaction SignalSender::old_signal_handler_;
768bool SignalSender::signal_handler_installed_ = false;
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000769
770
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000771Sampler::Sampler(Isolate* isolate, int interval)
772 : isolate_(isolate),
773 interval_(interval),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000774 profiling_(false),
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000775 active_(false),
776 samples_taken_(0) {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000777 data_ = new PlatformData;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000778}
779
780
781Sampler::~Sampler() {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000782 ASSERT(!IsActive());
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000783 delete data_;
784}
785
786
787void Sampler::Start() {
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000788 ASSERT(!IsActive());
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000789 SetActive(true);
lrn@chromium.org7516f052011-03-30 08:52:27 +0000790 SignalSender::AddActiveSampler(this);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000791}
792
793
794void Sampler::Stop() {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000795 ASSERT(IsActive());
796 SignalSender::RemoveActiveSampler(this);
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000797 SetActive(false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000798}
799
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000800
801} } // namespace v8::internal