blob: 511759c485e245891dd41a2fa792128e91915f91 [file] [log] [blame]
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001// Copyright 2012 the V8 project authors. All rights reserved.
ager@chromium.orga74f0da2008-12-03 16:05:52 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000028// Platform specific code for 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
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000057#include "platform-posix.h"
ager@chromium.orga74f0da2008-12-03 16:05:52 +000058#include "platform.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000059#include "vm-state-inl.h"
ager@chromium.orga74f0da2008-12-03 16:05:52 +000060
61
kasperl@chromium.org71affb52009-05-26 05:44:31 +000062namespace v8 {
63namespace internal {
ager@chromium.orga74f0da2008-12-03 16:05:52 +000064
65// 0 is never a valid thread id on FreeBSD since tids and pids share a
66// name space and pid 0 is used to kill the group (see man 2 kill).
67static const pthread_t kNoThread = (pthread_t) 0;
68
69
70double ceiling(double x) {
71 // Correct as on OS X
72 if (-1.0 < x && x < 0.0) {
73 return -0.0;
74 } else {
75 return ceil(x);
76 }
77}
78
79
lrn@chromium.org7516f052011-03-30 08:52:27 +000080static Mutex* limit_mutex = NULL;
81
82
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000083void OS::PostSetUp() {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +000084 POSIXPostSetUp();
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000085}
86
87
ricow@chromium.org30ce4112010-05-31 10:38:25 +000088void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
89 __asm__ __volatile__("" : : : "memory");
90 *ptr = value;
91}
92
93
ager@chromium.orgc4c92722009-11-18 14:12:51 +000094uint64_t OS::CpuFeaturesImpliedByPlatform() {
95 return 0; // FreeBSD runs on anything.
96}
97
98
ager@chromium.orga74f0da2008-12-03 16:05:52 +000099int OS::ActivationFrameAlignment() {
100 // 16 byte alignment on FreeBSD
101 return 16;
102}
103
104
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000105const char* OS::LocalTimezone(double time) {
106 if (isnan(time)) return "";
107 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
108 struct tm* t = localtime(&tv);
109 if (NULL == t) return "";
110 return t->tm_zone;
111}
112
113
114double OS::LocalTimeOffset() {
115 time_t tv = time(NULL);
116 struct tm* t = localtime(&tv);
117 // tm_gmtoff includes any daylight savings offset, so subtract it.
118 return static_cast<double>(t->tm_gmtoff * msPerSecond -
119 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
120}
121
122
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000123// We keep the lowest and highest addresses mapped as a quick way of
124// determining that pointers are outside the heap (used mostly in assertions
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000125// and verification). The estimate is conservative, i.e., not all addresses in
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000126// 'allocated' space are actually allocated to our heap. The range is
127// [lowest, highest), inclusive on the low and and exclusive on the high end.
128static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
129static void* highest_ever_allocated = reinterpret_cast<void*>(0);
130
131
132static void UpdateAllocatedSpaceLimits(void* address, int size) {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000133 ASSERT(limit_mutex != NULL);
134 ScopedLock lock(limit_mutex);
135
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000136 lowest_ever_allocated = Min(lowest_ever_allocated, address);
137 highest_ever_allocated =
138 Max(highest_ever_allocated,
139 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
140}
141
142
143bool OS::IsOutsideAllocatedSpace(void* address) {
144 return address < lowest_ever_allocated || address >= highest_ever_allocated;
145}
146
147
148size_t OS::AllocateAlignment() {
149 return getpagesize();
150}
151
152
153void* OS::Allocate(const size_t requested,
154 size_t* allocated,
155 bool executable) {
156 const size_t msize = RoundUp(requested, getpagesize());
157 int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
158 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
159
160 if (mbase == MAP_FAILED) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000161 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000162 return NULL;
163 }
164 *allocated = msize;
165 UpdateAllocatedSpaceLimits(mbase, msize);
166 return mbase;
167}
168
169
170void OS::Free(void* buf, const size_t length) {
171 // TODO(1240712): munmap has a return value which is ignored here.
ager@chromium.orga1645e22009-09-09 19:27:10 +0000172 int result = munmap(buf, length);
173 USE(result);
174 ASSERT(result == 0);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000175}
176
177
178void OS::Sleep(int milliseconds) {
179 unsigned int ms = static_cast<unsigned int>(milliseconds);
180 usleep(1000 * ms);
181}
182
183
184void OS::Abort() {
185 // Redirect to std abort to signal abnormal program termination.
186 abort();
187}
188
189
190void OS::DebugBreak() {
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000191#if (defined(__arm__) || defined(__thumb__))
192# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000193 asm("bkpt 0");
ricow@chromium.org0b9f8502010-08-18 07:45:01 +0000194# endif
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000195#else
196 asm("int $3");
197#endif
198}
199
200
201class PosixMemoryMappedFile : public OS::MemoryMappedFile {
202 public:
203 PosixMemoryMappedFile(FILE* file, void* memory, int size)
204 : file_(file), memory_(memory), size_(size) { }
205 virtual ~PosixMemoryMappedFile();
206 virtual void* memory() { return memory_; }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000207 virtual int size() { return size_; }
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000208 private:
209 FILE* file_;
210 void* memory_;
211 int size_;
212};
213
214
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000215OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000216 FILE* file = fopen(name, "r+");
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000217 if (file == NULL) return NULL;
218
219 fseek(file, 0, SEEK_END);
220 int size = ftell(file);
221
222 void* memory =
223 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
224 return new PosixMemoryMappedFile(file, memory, size);
225}
226
227
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000228OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
229 void* initial) {
230 FILE* file = fopen(name, "w+");
231 if (file == NULL) return NULL;
232 int result = fwrite(initial, size, 1, file);
233 if (result < 1) {
234 fclose(file);
235 return NULL;
236 }
237 void* memory =
238 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
239 return new PosixMemoryMappedFile(file, memory, size);
240}
241
242
243PosixMemoryMappedFile::~PosixMemoryMappedFile() {
244 if (memory_) munmap(memory_, size_);
245 fclose(file_);
246}
247
248
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000249static unsigned StringToLong(char* buffer) {
250 return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
251}
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000252
253
254void OS::LogSharedLibraryAddresses() {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000255 static const int MAP_LENGTH = 1024;
256 int fd = open("/proc/self/maps", O_RDONLY);
257 if (fd < 0) return;
258 while (true) {
259 char addr_buffer[11];
260 addr_buffer[0] = '0';
261 addr_buffer[1] = 'x';
262 addr_buffer[10] = 0;
263 int result = read(fd, addr_buffer + 2, 8);
264 if (result < 8) break;
265 unsigned start = StringToLong(addr_buffer);
266 result = read(fd, addr_buffer + 2, 1);
267 if (result < 1) break;
268 if (addr_buffer[2] != '-') break;
269 result = read(fd, addr_buffer + 2, 8);
270 if (result < 8) break;
271 unsigned end = StringToLong(addr_buffer);
272 char buffer[MAP_LENGTH];
273 int bytes_read = -1;
274 do {
275 bytes_read++;
276 if (bytes_read >= MAP_LENGTH - 1)
277 break;
278 result = read(fd, buffer + bytes_read, 1);
279 if (result < 1) break;
280 } while (buffer[bytes_read] != '\n');
281 buffer[bytes_read] = 0;
282 // Ignore mappings that are not executable.
283 if (buffer[3] != 'x') continue;
284 char* start_of_path = index(buffer, '/');
285 // There may be no filename in this line. Skip to next.
286 if (start_of_path == NULL) continue;
287 buffer[bytes_read] = 0;
lrn@chromium.org7516f052011-03-30 08:52:27 +0000288 LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end));
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000289 }
290 close(fd);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000291}
292
293
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000294void OS::SignalCodeMovingGC() {
295}
296
297
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000298int OS::StackWalk(Vector<OS::StackFrame> frames) {
299 int frames_size = frames.length();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000300 ScopedVector<void*> addresses(frames_size);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000301
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000302 int frames_count = backtrace(addresses.start(), frames_size);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000303
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000304 char** symbols = backtrace_symbols(addresses.start(), frames_count);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000305 if (symbols == NULL) {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000306 return kStackWalkError;
307 }
308
309 for (int i = 0; i < frames_count; i++) {
310 frames[i].address = addresses[i];
311 // Format a text representation of the frame based on the information
312 // available.
313 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
314 "%s",
315 symbols[i]);
316 // Make sure line termination is in place.
317 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
318 }
319
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000320 free(symbols);
321
322 return frames_count;
323}
324
325
326// Constants used for mmap.
327static const int kMmapFd = -1;
328static const int kMmapFdOffset = 0;
329
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000330VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000331
332VirtualMemory::VirtualMemory(size_t size) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000333 address_ = ReserveRegion(size);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000334 size_ = size;
335}
336
337
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000338VirtualMemory::VirtualMemory(size_t size, size_t alignment)
339 : address_(NULL), size_(0) {
340 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
341 size_t request_size = RoundUp(size + alignment,
342 static_cast<intptr_t>(OS::AllocateAlignment()));
343 void* reservation = mmap(OS::GetRandomMmapAddr(),
344 request_size,
345 PROT_NONE,
346 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
347 kMmapFd,
348 kMmapFdOffset);
349 if (reservation == MAP_FAILED) return;
350
351 Address base = static_cast<Address>(reservation);
352 Address aligned_base = RoundUp(base, alignment);
353 ASSERT_LE(base, aligned_base);
354
355 // Unmap extra memory reserved before and after the desired block.
356 if (aligned_base != base) {
357 size_t prefix_size = static_cast<size_t>(aligned_base - base);
358 OS::Free(base, prefix_size);
359 request_size -= prefix_size;
360 }
361
362 size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
363 ASSERT_LE(aligned_size, request_size);
364
365 if (aligned_size != request_size) {
366 size_t suffix_size = request_size - aligned_size;
367 OS::Free(aligned_base + aligned_size, suffix_size);
368 request_size -= suffix_size;
369 }
370
371 ASSERT(aligned_size == request_size);
372
373 address_ = static_cast<void*>(aligned_base);
374 size_ = aligned_size;
375}
376
377
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000378VirtualMemory::~VirtualMemory() {
379 if (IsReserved()) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000380 bool result = ReleaseRegion(address(), size());
381 ASSERT(result);
382 USE(result);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000383 }
384}
385
386
387bool VirtualMemory::IsReserved() {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000388 return address_ != NULL;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000389}
390
391
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000392void VirtualMemory::Reset() {
393 address_ = NULL;
394 size_ = 0;
395}
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000396
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000397
398bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
399 return CommitRegion(address, size, is_executable);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000400}
401
402
403bool VirtualMemory::Uncommit(void* address, size_t size) {
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000404 return UncommitRegion(address, size);
405}
406
407
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000408bool VirtualMemory::Guard(void* address) {
409 OS::Guard(address, OS::CommitPageSize());
410 return true;
411}
412
413
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000414void* VirtualMemory::ReserveRegion(size_t size) {
415 void* result = mmap(OS::GetRandomMmapAddr(),
416 size,
417 PROT_NONE,
418 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
419 kMmapFd,
420 kMmapFdOffset);
421
422 if (result == MAP_FAILED) return NULL;
423
424 return result;
425}
426
427
428bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
429 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
430 if (MAP_FAILED == mmap(base,
431 size,
432 prot,
433 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
434 kMmapFd,
435 kMmapFdOffset)) {
436 return false;
437 }
438
439 UpdateAllocatedSpaceLimits(base, size);
440 return true;
441}
442
443
444bool VirtualMemory::UncommitRegion(void* base, size_t size) {
445 return mmap(base,
446 size,
447 PROT_NONE,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000448 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +0000449 kMmapFd,
450 kMmapFdOffset) != MAP_FAILED;
451}
452
453
454bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
455 return munmap(base, size) == 0;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000456}
457
458
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000459class Thread::PlatformData : public Malloced {
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000460 public:
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000461 pthread_t thread_; // Thread handle for pthread.
462};
463
464
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000465Thread::Thread(const Options& options)
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000466 : data_(new PlatformData),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000467 stack_size_(options.stack_size()) {
468 set_name(options.name());
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000469}
470
471
472Thread::~Thread() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000473 delete data_;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000474}
475
476
477static void* ThreadEntry(void* arg) {
478 Thread* thread = reinterpret_cast<Thread*>(arg);
479 // This is also initialized by the first argument to pthread_create() but we
480 // don't know which thread will run first (the original thread or the new
481 // one) so we initialize it here too.
lrn@chromium.org1c092762011-05-09 09:42:16 +0000482 thread->data()->thread_ = pthread_self();
483 ASSERT(thread->data()->thread_ != kNoThread);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000484 thread->Run();
485 return NULL;
486}
487
488
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000489void Thread::set_name(const char* name) {
490 strncpy(name_, name, sizeof(name_));
491 name_[sizeof(name_) - 1] = '\0';
492}
493
494
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000495void Thread::Start() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000496 pthread_attr_t* attr_ptr = NULL;
497 pthread_attr_t attr;
498 if (stack_size_ > 0) {
499 pthread_attr_init(&attr);
500 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
501 attr_ptr = &attr;
502 }
lrn@chromium.org1c092762011-05-09 09:42:16 +0000503 pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
504 ASSERT(data_->thread_ != kNoThread);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000505}
506
507
508void Thread::Join() {
lrn@chromium.org1c092762011-05-09 09:42:16 +0000509 pthread_join(data_->thread_, NULL);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000510}
511
512
513Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
514 pthread_key_t key;
515 int result = pthread_key_create(&key, NULL);
516 USE(result);
517 ASSERT(result == 0);
518 return static_cast<LocalStorageKey>(key);
519}
520
521
522void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
523 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
524 int result = pthread_key_delete(pthread_key);
525 USE(result);
526 ASSERT(result == 0);
527}
528
529
530void* Thread::GetThreadLocal(LocalStorageKey key) {
531 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
532 return pthread_getspecific(pthread_key);
533}
534
535
536void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
537 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
538 pthread_setspecific(pthread_key, value);
539}
540
541
542void Thread::YieldCPU() {
543 sched_yield();
544}
545
546
547class FreeBSDMutex : public Mutex {
548 public:
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000549 FreeBSDMutex() {
550 pthread_mutexattr_t attrs;
551 int result = pthread_mutexattr_init(&attrs);
552 ASSERT(result == 0);
553 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
554 ASSERT(result == 0);
555 result = pthread_mutex_init(&mutex_, &attrs);
556 ASSERT(result == 0);
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +0000557 USE(result);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000558 }
559
560 virtual ~FreeBSDMutex() { pthread_mutex_destroy(&mutex_); }
561
562 virtual int Lock() {
563 int result = pthread_mutex_lock(&mutex_);
564 return result;
565 }
566
567 virtual int Unlock() {
568 int result = pthread_mutex_unlock(&mutex_);
569 return result;
570 }
571
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000572 virtual bool TryLock() {
573 int result = pthread_mutex_trylock(&mutex_);
574 // Return false if the lock is busy and locking failed.
575 if (result == EBUSY) {
576 return false;
577 }
578 ASSERT(result == 0); // Verify no other errors.
579 return true;
580 }
581
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000582 private:
583 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
584};
585
586
587Mutex* OS::CreateMutex() {
588 return new FreeBSDMutex();
589}
590
591
592class FreeBSDSemaphore : public Semaphore {
593 public:
594 explicit FreeBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
595 virtual ~FreeBSDSemaphore() { sem_destroy(&sem_); }
596
597 virtual void Wait();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000598 virtual bool Wait(int timeout);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000599 virtual void Signal() { sem_post(&sem_); }
600 private:
601 sem_t sem_;
602};
603
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000604
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000605void FreeBSDSemaphore::Wait() {
606 while (true) {
607 int result = sem_wait(&sem_);
608 if (result == 0) return; // Successfully got semaphore.
609 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
610 }
611}
612
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000613
614bool FreeBSDSemaphore::Wait(int timeout) {
615 const long kOneSecondMicros = 1000000; // NOLINT
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000616
617 // Split timeout into second and nanosecond parts.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000618 struct timeval delta;
619 delta.tv_usec = timeout % kOneSecondMicros;
620 delta.tv_sec = timeout / kOneSecondMicros;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000621
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000622 struct timeval current_time;
623 // Get the current time.
624 if (gettimeofday(&current_time, NULL) == -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000625 return false;
626 }
627
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000628 // Calculate time for end of timeout.
629 struct timeval end_time;
630 timeradd(&current_time, &delta, &end_time);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000631
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000632 struct timespec ts;
633 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000634 while (true) {
635 int result = sem_timedwait(&sem_, &ts);
636 if (result == 0) return true; // Successfully got semaphore.
637 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
638 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
639 }
640}
641
642
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000643Semaphore* OS::CreateSemaphore(int count) {
644 return new FreeBSDSemaphore(count);
645}
646
ager@chromium.org381abbb2009-02-25 13:23:22 +0000647
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000648static pthread_t GetThreadID() {
649 pthread_t thread_id = pthread_self();
650 return thread_id;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000651}
652
653
654class Sampler::PlatformData : public Malloced {
655 public:
lrn@chromium.org7516f052011-03-30 08:52:27 +0000656 PlatformData() : vm_tid_(GetThreadID()) {}
657
658 pthread_t vm_tid() const { return vm_tid_; }
659
660 private:
661 pthread_t vm_tid_;
662};
663
664
665static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
666 USE(info);
667 if (signal != SIGPROF) return;
668 Isolate* isolate = Isolate::UncheckedCurrent();
669 if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
670 // We require a fully initialized and entered isolate.
671 return;
672 }
vitalyr@chromium.org0ec56d62011-04-15 22:22:08 +0000673 if (v8::Locker::IsActive() &&
674 !isolate->thread_manager()->IsLockedByCurrentThread()) {
675 return;
676 }
677
lrn@chromium.org7516f052011-03-30 08:52:27 +0000678 Sampler* sampler = isolate->logger()->sampler();
679 if (sampler == NULL || !sampler->IsActive()) return;
680
681 TickSample sample_obj;
682 TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
683 if (sample == NULL) sample = &sample_obj;
684
685 // Extracting the sample from the context is extremely machine dependent.
686 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
687 mcontext_t& mcontext = ucontext->uc_mcontext;
688 sample->state = isolate->current_vm_state();
689#if V8_HOST_ARCH_IA32
690 sample->pc = reinterpret_cast<Address>(mcontext.mc_eip);
691 sample->sp = reinterpret_cast<Address>(mcontext.mc_esp);
692 sample->fp = reinterpret_cast<Address>(mcontext.mc_ebp);
693#elif V8_HOST_ARCH_X64
694 sample->pc = reinterpret_cast<Address>(mcontext.mc_rip);
695 sample->sp = reinterpret_cast<Address>(mcontext.mc_rsp);
696 sample->fp = reinterpret_cast<Address>(mcontext.mc_rbp);
697#elif V8_HOST_ARCH_ARM
698 sample->pc = reinterpret_cast<Address>(mcontext.mc_r15);
699 sample->sp = reinterpret_cast<Address>(mcontext.mc_r13);
700 sample->fp = reinterpret_cast<Address>(mcontext.mc_r11);
701#endif
702 sampler->SampleStack(sample);
703 sampler->Tick(sample);
704}
705
706
707class SignalSender : public Thread {
708 public:
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000709 enum SleepInterval {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000710 HALF_INTERVAL,
711 FULL_INTERVAL
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000712 };
713
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +0000714 static const int kSignalSenderStackSize = 64 * KB;
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000715
lrn@chromium.org7516f052011-03-30 08:52:27 +0000716 explicit SignalSender(int interval)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000717 : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
lrn@chromium.org7516f052011-03-30 08:52:27 +0000718 interval_(interval) {}
719
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000720 static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
721 static void TearDown() { delete mutex_; }
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000722
lrn@chromium.org7516f052011-03-30 08:52:27 +0000723 static void AddActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000724 ScopedLock lock(mutex_);
lrn@chromium.org7516f052011-03-30 08:52:27 +0000725 SamplerRegistry::AddActiveSampler(sampler);
726 if (instance_ == NULL) {
727 // Install a signal handler.
728 struct sigaction sa;
729 sa.sa_sigaction = ProfilerSignalHandler;
730 sigemptyset(&sa.sa_mask);
731 sa.sa_flags = SA_RESTART | SA_SIGINFO;
732 signal_handler_installed_ =
733 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
734
735 // Start a thread that sends SIGPROF signal to VM threads.
736 instance_ = new SignalSender(sampler->interval());
737 instance_->Start();
738 } else {
739 ASSERT(instance_->interval_ == sampler->interval());
740 }
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000741 }
742
lrn@chromium.org7516f052011-03-30 08:52:27 +0000743 static void RemoveActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000744 ScopedLock lock(mutex_);
lrn@chromium.org7516f052011-03-30 08:52:27 +0000745 SamplerRegistry::RemoveActiveSampler(sampler);
746 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000747 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
lrn@chromium.org7516f052011-03-30 08:52:27 +0000748 delete instance_;
749 instance_ = NULL;
750
751 // Restore the old signal handler.
752 if (signal_handler_installed_) {
753 sigaction(SIGPROF, &old_signal_handler_, 0);
754 signal_handler_installed_ = false;
755 }
756 }
757 }
758
759 // Implement Thread::Run().
760 virtual void Run() {
761 SamplerRegistry::State state;
762 while ((state = SamplerRegistry::GetState()) !=
763 SamplerRegistry::HAS_NO_SAMPLERS) {
764 bool cpu_profiling_enabled =
765 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
766 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
767 // When CPU profiling is enabled both JavaScript and C++ code is
768 // profiled. We must not suspend.
769 if (!cpu_profiling_enabled) {
770 if (rate_limiter_.SuspendIfNecessary()) continue;
771 }
772 if (cpu_profiling_enabled && runtime_profiler_enabled) {
773 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
774 return;
775 }
776 Sleep(HALF_INTERVAL);
777 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
778 return;
779 }
780 Sleep(HALF_INTERVAL);
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000781 } else {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000782 if (cpu_profiling_enabled) {
783 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
784 this)) {
785 return;
786 }
787 }
788 if (runtime_profiler_enabled) {
789 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
790 NULL)) {
791 return;
792 }
793 }
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000794 Sleep(FULL_INTERVAL);
795 }
796 }
797 }
798
lrn@chromium.org7516f052011-03-30 08:52:27 +0000799 static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
800 if (!sampler->IsProfiling()) return;
801 SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
802 sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
803 }
804
805 static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
806 if (!sampler->isolate()->IsInitialized()) return;
807 sampler->isolate()->runtime_profiler()->NotifyTick();
808 }
809
810 void SendProfilingSignal(pthread_t tid) {
811 if (!signal_handler_installed_) return;
812 pthread_kill(tid, SIGPROF);
813 }
814
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000815 void Sleep(SleepInterval full_or_half) {
816 // Convert ms to us and subtract 100 us to compensate delays
817 // occuring during signal delivery.
lrn@chromium.org7516f052011-03-30 08:52:27 +0000818 useconds_t interval = interval_ * 1000 - 100;
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000819 if (full_or_half == HALF_INTERVAL) interval /= 2;
820 int result = usleep(interval);
821#ifdef DEBUG
822 if (result != 0 && errno != EINTR) {
823 fprintf(stderr,
824 "SignalSender usleep error; interval = %u, errno = %d\n",
825 interval,
826 errno);
827 ASSERT(result == 0 || errno == EINTR);
828 }
829#endif
830 USE(result);
831 }
832
lrn@chromium.org7516f052011-03-30 08:52:27 +0000833 const int interval_;
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000834 RuntimeProfilerRateLimiter rate_limiter_;
lrn@chromium.org7516f052011-03-30 08:52:27 +0000835
836 // Protects the process wide state below.
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000837 static Mutex* mutex_;
lrn@chromium.org7516f052011-03-30 08:52:27 +0000838 static SignalSender* instance_;
839 static bool signal_handler_installed_;
840 static struct sigaction old_signal_handler_;
841
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000842 private:
lrn@chromium.org7516f052011-03-30 08:52:27 +0000843 DISALLOW_COPY_AND_ASSIGN(SignalSender);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000844};
845
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000846Mutex* SignalSender::mutex_ = NULL;
lrn@chromium.org7516f052011-03-30 08:52:27 +0000847SignalSender* SignalSender::instance_ = NULL;
848struct sigaction SignalSender::old_signal_handler_;
849bool SignalSender::signal_handler_installed_ = false;
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000850
851
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000852void OS::SetUp() {
853 // Seed the random number generator.
854 // Convert the current time to a 64-bit integer first, before converting it
855 // to an unsigned. Going directly can cause an overflow and the seed to be
856 // set to all ones. The seed will be identical for different instances that
857 // call this setup code within the same millisecond.
858 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
859 srandom(static_cast<unsigned int>(seed));
860 limit_mutex = CreateMutex();
861 SignalSender::SetUp();
862}
863
864
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000865void OS::TearDown() {
866 SignalSender::TearDown();
867 delete limit_mutex;
868}
869
870
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000871Sampler::Sampler(Isolate* isolate, int interval)
872 : isolate_(isolate),
873 interval_(interval),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000874 profiling_(false),
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000875 active_(false),
876 samples_taken_(0) {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000877 data_ = new PlatformData;
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000878}
879
880
881Sampler::~Sampler() {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000882 ASSERT(!IsActive());
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000883 delete data_;
884}
885
886
887void Sampler::Start() {
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000888 ASSERT(!IsActive());
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000889 SetActive(true);
lrn@chromium.org7516f052011-03-30 08:52:27 +0000890 SignalSender::AddActiveSampler(this);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000891}
892
893
894void Sampler::Stop() {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000895 ASSERT(IsActive());
896 SignalSender::RemoveActiveSampler(this);
ricow@chromium.orgbadaffc2011-03-17 12:15:27 +0000897 SetActive(false);
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000898}
899
ager@chromium.orga74f0da2008-12-03 16:05:52 +0000900
901} } // namespace v8::internal