blob: ba33a8444e23760a2b47585cb82e5c750074a8fd [file] [log] [blame]
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001// Copyright 2012 the V8 project authors. All rights reserved.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +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
rossberg@chromium.orgfab14982012-01-05 15:02:15 +000028// Platform specific code for OpenBSD and NetBSD goes here. For the POSIX
29// comaptible parts the implementation is in platform-posix.cc.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000030
31#include <pthread.h>
32#include <semaphore.h>
33#include <signal.h>
34#include <sys/time.h>
35#include <sys/resource.h>
danno@chromium.orgc612e022011-11-10 11:38:15 +000036#include <sys/syscall.h>
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000037#include <sys/types.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
danno@chromium.orgc612e022011-11-10 11:38:15 +000043#include <fcntl.h> // open
44#include <unistd.h> // sysconf
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000045#include <execinfo.h> // backtrace, backtrace_symbols
46#include <strings.h> // index
47#include <errno.h>
48#include <stdarg.h>
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000049
50#undef MAP_TYPE
51
52#include "v8.h"
53
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000054#include "platform-posix.h"
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000055#include "platform.h"
danno@chromium.orgc612e022011-11-10 11:38:15 +000056#include "v8threads.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000057#include "vm-state-inl.h"
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000058
59
60namespace v8 {
61namespace internal {
62
danno@chromium.orgc612e022011-11-10 11:38:15 +000063// 0 is never a valid thread id on Linux and OpenBSD since tids and pids share a
64// name space and pid 0 is reserved (see man 2 kill).
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000065static const pthread_t kNoThread = (pthread_t) 0;
66
67
68double ceiling(double x) {
danno@chromium.orgc612e022011-11-10 11:38:15 +000069 return ceil(x);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +000070}
71
72
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +000073static Mutex* limit_mutex = NULL;
74
75
danno@chromium.orgc612e022011-11-10 11:38:15 +000076static void* GetRandomMmapAddr() {
77 Isolate* isolate = Isolate::UncheckedCurrent();
78 // Note that the current isolate isn't set up in a call path via
79 // CpuFeatures::Probe. We don't care about randomization in this case because
80 // the code page is immediately freed.
81 if (isolate != NULL) {
82#ifdef V8_TARGET_ARCH_X64
83 uint64_t rnd1 = V8::RandomPrivate(isolate);
84 uint64_t rnd2 = V8::RandomPrivate(isolate);
85 uint64_t raw_addr = (rnd1 << 32) ^ rnd2;
86 // Currently available CPUs have 48 bits of virtual addressing. Truncate
87 // the hint address to 46 bits to give the kernel a fighting chance of
88 // fulfilling our placement request.
89 raw_addr &= V8_UINT64_C(0x3ffffffff000);
90#else
91 uint32_t raw_addr = V8::RandomPrivate(isolate);
92 // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
93 // variety of ASLR modes (PAE kernel, NX compat mode, etc).
94 raw_addr &= 0x3ffff000;
95 raw_addr += 0x20000000;
96#endif
97 return reinterpret_cast<void*>(raw_addr);
98 }
99 return NULL;
100}
101
102
danno@chromium.org8c0a43f2012-04-03 08:37:53 +0000103void OS::PostSetUp() {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000104 POSIXPostSetUp();
danno@chromium.org8c0a43f2012-04-03 08:37:53 +0000105}
106
107
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000108uint64_t OS::CpuFeaturesImpliedByPlatform() {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000109 return 0;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000110}
111
112
113int OS::ActivationFrameAlignment() {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000114 // With gcc 4.4 the tree vectorization optimizer can generate code
115 // that requires 16 byte alignment such as movdqa on x86.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000116 return 16;
117}
118
119
danno@chromium.orgc612e022011-11-10 11:38:15 +0000120void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
121 __asm__ __volatile__("" : : : "memory");
122 // An x86 store acts as a release barrier.
123 *ptr = value;
124}
125
126
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000127const char* OS::LocalTimezone(double time) {
128 if (isnan(time)) return "";
129 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
130 struct tm* t = localtime(&tv);
131 if (NULL == t) return "";
132 return t->tm_zone;
133}
134
135
136double OS::LocalTimeOffset() {
137 time_t tv = time(NULL);
138 struct tm* t = localtime(&tv);
139 // tm_gmtoff includes any daylight savings offset, so subtract it.
140 return static_cast<double>(t->tm_gmtoff * msPerSecond -
141 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
142}
143
144
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000145// We keep the lowest and highest addresses mapped as a quick way of
146// determining that pointers are outside the heap (used mostly in assertions
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000147// and verification). The estimate is conservative, i.e., not all addresses in
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000148// 'allocated' space are actually allocated to our heap. The range is
149// [lowest, highest), inclusive on the low and and exclusive on the high end.
150static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
151static void* highest_ever_allocated = reinterpret_cast<void*>(0);
152
153
154static void UpdateAllocatedSpaceLimits(void* address, int size) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000155 ASSERT(limit_mutex != NULL);
156 ScopedLock lock(limit_mutex);
157
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000158 lowest_ever_allocated = Min(lowest_ever_allocated, address);
159 highest_ever_allocated =
160 Max(highest_ever_allocated,
161 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
162}
163
164
165bool OS::IsOutsideAllocatedSpace(void* address) {
166 return address < lowest_ever_allocated || address >= highest_ever_allocated;
167}
168
169
170size_t OS::AllocateAlignment() {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000171 return sysconf(_SC_PAGESIZE);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000172}
173
174
175void* OS::Allocate(const size_t requested,
176 size_t* allocated,
danno@chromium.orgc612e022011-11-10 11:38:15 +0000177 bool is_executable) {
178 const size_t msize = RoundUp(requested, AllocateAlignment());
179 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
180 void* addr = GetRandomMmapAddr();
181 void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000182 if (mbase == MAP_FAILED) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000183 LOG(i::Isolate::Current(),
184 StringEvent("OS::Allocate", "mmap failed"));
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000185 return NULL;
186 }
187 *allocated = msize;
188 UpdateAllocatedSpaceLimits(mbase, msize);
189 return mbase;
190}
191
192
danno@chromium.orgc612e022011-11-10 11:38:15 +0000193void OS::Free(void* address, const size_t size) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000194 // TODO(1240712): munmap has a return value which is ignored here.
danno@chromium.orgc612e022011-11-10 11:38:15 +0000195 int result = munmap(address, size);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000196 USE(result);
197 ASSERT(result == 0);
198}
199
200
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000201void OS::Sleep(int milliseconds) {
202 unsigned int ms = static_cast<unsigned int>(milliseconds);
203 usleep(1000 * ms);
204}
205
206
207void OS::Abort() {
208 // Redirect to std abort to signal abnormal program termination.
209 abort();
210}
211
212
213void OS::DebugBreak() {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000214 asm("int $3");
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000215}
216
217
218class PosixMemoryMappedFile : public OS::MemoryMappedFile {
219 public:
220 PosixMemoryMappedFile(FILE* file, void* memory, int size)
221 : file_(file), memory_(memory), size_(size) { }
222 virtual ~PosixMemoryMappedFile();
223 virtual void* memory() { return memory_; }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000224 virtual int size() { return size_; }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000225 private:
226 FILE* file_;
227 void* memory_;
228 int size_;
229};
230
231
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000232OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000233 FILE* file = fopen(name, "r+");
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000234 if (file == NULL) return NULL;
235
236 fseek(file, 0, SEEK_END);
237 int size = ftell(file);
238
239 void* memory =
240 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
241 return new PosixMemoryMappedFile(file, memory, size);
242}
243
244
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000245OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
246 void* initial) {
247 FILE* file = fopen(name, "w+");
248 if (file == NULL) return NULL;
249 int result = fwrite(initial, size, 1, file);
250 if (result < 1) {
251 fclose(file);
252 return NULL;
253 }
254 void* memory =
255 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
256 return new PosixMemoryMappedFile(file, memory, size);
257}
258
259
260PosixMemoryMappedFile::~PosixMemoryMappedFile() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000261 if (memory_) OS::Free(memory_, size_);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000262 fclose(file_);
263}
264
265
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000266void OS::LogSharedLibraryAddresses() {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000267 // This function assumes that the layout of the file is as follows:
268 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
269 // If we encounter an unexpected situation we abort scanning further entries.
270 FILE* fp = fopen("/proc/self/maps", "r");
271 if (fp == NULL) return;
272
273 // Allocate enough room to be able to store a full file name.
274 const int kLibNameLen = FILENAME_MAX + 1;
275 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
276
277 i::Isolate* isolate = ISOLATE;
278 // This loop will terminate once the scanning hits an EOF.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000279 while (true) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000280 uintptr_t start, end;
281 char attr_r, attr_w, attr_x, attr_p;
282 // Parse the addresses and permission bits at the beginning of the line.
283 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
284 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
285
286 int c;
287 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
288 // Found a read-only executable entry. Skip characters until we reach
289 // the beginning of the filename or the end of the line.
290 do {
291 c = getc(fp);
292 } while ((c != EOF) && (c != '\n') && (c != '/'));
293 if (c == EOF) break; // EOF: Was unexpected, just exit.
294
295 // Process the filename if found.
296 if (c == '/') {
297 ungetc(c, fp); // Push the '/' back into the stream to be read below.
298
299 // Read to the end of the line. Exit if the read fails.
300 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
301
302 // Drop the newline character read by fgets. We do not need to check
303 // for a zero-length string because we know that we at least read the
304 // '/' character.
305 lib_name[strlen(lib_name) - 1] = '\0';
306 } else {
307 // No library name found, just record the raw address range.
308 snprintf(lib_name, kLibNameLen,
309 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
310 }
311 LOG(isolate, SharedLibraryEvent(lib_name, start, end));
312 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000313 // Entry not describing executable data. Skip to end of line to set up
danno@chromium.orgc612e022011-11-10 11:38:15 +0000314 // reading the next entry.
315 do {
316 c = getc(fp);
317 } while ((c != EOF) && (c != '\n'));
318 if (c == EOF) break;
319 }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000320 }
danno@chromium.orgc612e022011-11-10 11:38:15 +0000321 free(lib_name);
322 fclose(fp);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000323}
324
325
danno@chromium.orgc612e022011-11-10 11:38:15 +0000326static const char kGCFakeMmap[] = "/tmp/__v8_gc__";
327
328
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000329void OS::SignalCodeMovingGC() {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000330 // Support for ll_prof.py.
331 //
332 // The Linux profiler built into the kernel logs all mmap's with
333 // PROT_EXEC so that analysis tools can properly attribute ticks. We
334 // do a mmap with a name known by ll_prof.py and immediately munmap
335 // it. This injects a GC marker into the stream of events generated
336 // by the kernel and allows us to synchronize V8 code log and the
337 // kernel log.
338 int size = sysconf(_SC_PAGESIZE);
339 FILE* f = fopen(kGCFakeMmap, "w+");
340 void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE,
341 fileno(f), 0);
342 ASSERT(addr != MAP_FAILED);
343 OS::Free(addr, size);
344 fclose(f);
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000345}
346
347
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000348int OS::StackWalk(Vector<OS::StackFrame> frames) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000349 // backtrace is a glibc extension.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000350 int frames_size = frames.length();
351 ScopedVector<void*> addresses(frames_size);
352
353 int frames_count = backtrace(addresses.start(), frames_size);
354
355 char** symbols = backtrace_symbols(addresses.start(), frames_count);
356 if (symbols == NULL) {
357 return kStackWalkError;
358 }
359
360 for (int i = 0; i < frames_count; i++) {
361 frames[i].address = addresses[i];
362 // Format a text representation of the frame based on the information
363 // available.
364 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
365 "%s",
366 symbols[i]);
367 // Make sure line termination is in place.
368 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
369 }
370
371 free(symbols);
372
373 return frames_count;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000374}
375
376
377// Constants used for mmap.
378static const int kMmapFd = -1;
379static const int kMmapFdOffset = 0;
380
danno@chromium.orgc612e022011-11-10 11:38:15 +0000381VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000382
383VirtualMemory::VirtualMemory(size_t size) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000384 address_ = ReserveRegion(size);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000385 size_ = size;
386}
387
388
danno@chromium.orgc612e022011-11-10 11:38:15 +0000389VirtualMemory::VirtualMemory(size_t size, size_t alignment)
390 : address_(NULL), size_(0) {
391 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
392 size_t request_size = RoundUp(size + alignment,
393 static_cast<intptr_t>(OS::AllocateAlignment()));
394 void* reservation = mmap(GetRandomMmapAddr(),
395 request_size,
396 PROT_NONE,
397 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
398 kMmapFd,
399 kMmapFdOffset);
400 if (reservation == MAP_FAILED) return;
401
402 Address base = static_cast<Address>(reservation);
403 Address aligned_base = RoundUp(base, alignment);
404 ASSERT_LE(base, aligned_base);
405
406 // Unmap extra memory reserved before and after the desired block.
407 if (aligned_base != base) {
408 size_t prefix_size = static_cast<size_t>(aligned_base - base);
409 OS::Free(base, prefix_size);
410 request_size -= prefix_size;
411 }
412
413 size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
414 ASSERT_LE(aligned_size, request_size);
415
416 if (aligned_size != request_size) {
417 size_t suffix_size = request_size - aligned_size;
418 OS::Free(aligned_base + aligned_size, suffix_size);
419 request_size -= suffix_size;
420 }
421
422 ASSERT(aligned_size == request_size);
423
424 address_ = static_cast<void*>(aligned_base);
425 size_ = aligned_size;
426}
427
428
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000429VirtualMemory::~VirtualMemory() {
430 if (IsReserved()) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000431 bool result = ReleaseRegion(address(), size());
432 ASSERT(result);
433 USE(result);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000434 }
435}
436
437
438bool VirtualMemory::IsReserved() {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000439 return address_ != NULL;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000440}
441
442
danno@chromium.orgc612e022011-11-10 11:38:15 +0000443void VirtualMemory::Reset() {
444 address_ = NULL;
445 size_ = 0;
446}
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000447
danno@chromium.orgc612e022011-11-10 11:38:15 +0000448
449bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
450 return CommitRegion(address, size, is_executable);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000451}
452
453
454bool VirtualMemory::Uncommit(void* address, size_t size) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000455 return UncommitRegion(address, size);
456}
457
458
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000459bool VirtualMemory::Guard(void* address) {
460 OS::Guard(address, OS::CommitPageSize());
461 return true;
462}
463
464
danno@chromium.orgc612e022011-11-10 11:38:15 +0000465void* VirtualMemory::ReserveRegion(size_t size) {
466 void* result = mmap(GetRandomMmapAddr(),
467 size,
468 PROT_NONE,
469 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
470 kMmapFd,
471 kMmapFdOffset);
472
473 if (result == MAP_FAILED) return NULL;
474
475 return result;
476}
477
478
479bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
480 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
481 if (MAP_FAILED == mmap(base,
482 size,
483 prot,
484 MAP_PRIVATE | MAP_ANON | MAP_FIXED,
485 kMmapFd,
486 kMmapFdOffset)) {
487 return false;
488 }
489
490 UpdateAllocatedSpaceLimits(base, size);
491 return true;
492}
493
494
495bool VirtualMemory::UncommitRegion(void* base, size_t size) {
496 return mmap(base,
497 size,
498 PROT_NONE,
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000499 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE | MAP_FIXED,
danno@chromium.orgc612e022011-11-10 11:38:15 +0000500 kMmapFd,
501 kMmapFdOffset) != MAP_FAILED;
502}
503
504
505bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
506 return munmap(base, size) == 0;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000507}
508
509
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000510class Thread::PlatformData : public Malloced {
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000511 public:
danno@chromium.orgc612e022011-11-10 11:38:15 +0000512 PlatformData() : thread_(kNoThread) {}
513
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000514 pthread_t thread_; // Thread handle for pthread.
515};
516
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000517Thread::Thread(const Options& options)
danno@chromium.orgc612e022011-11-10 11:38:15 +0000518 : data_(new PlatformData()),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000519 stack_size_(options.stack_size()) {
520 set_name(options.name());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000521}
522
523
524Thread::~Thread() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000525 delete data_;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000526}
527
528
529static void* ThreadEntry(void* arg) {
530 Thread* thread = reinterpret_cast<Thread*>(arg);
531 // This is also initialized by the first argument to pthread_create() but we
532 // don't know which thread will run first (the original thread or the new
533 // one) so we initialize it here too.
danno@chromium.orgc612e022011-11-10 11:38:15 +0000534#ifdef PR_SET_NAME
535 prctl(PR_SET_NAME,
536 reinterpret_cast<unsigned long>(thread->name()), // NOLINT
537 0, 0, 0);
538#endif
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000539 thread->data()->thread_ = pthread_self();
540 ASSERT(thread->data()->thread_ != kNoThread);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000541 thread->Run();
542 return NULL;
543}
544
545
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000546void Thread::set_name(const char* name) {
547 strncpy(name_, name, sizeof(name_));
548 name_[sizeof(name_) - 1] = '\0';
549}
550
551
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000552void Thread::Start() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000553 pthread_attr_t* attr_ptr = NULL;
554 pthread_attr_t attr;
555 if (stack_size_ > 0) {
556 pthread_attr_init(&attr);
557 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
558 attr_ptr = &attr;
559 }
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000560 pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000561 ASSERT(data_->thread_ != kNoThread);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000562}
563
564
565void Thread::Join() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000566 pthread_join(data_->thread_, NULL);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000567}
568
569
570Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
571 pthread_key_t key;
572 int result = pthread_key_create(&key, NULL);
573 USE(result);
574 ASSERT(result == 0);
575 return static_cast<LocalStorageKey>(key);
576}
577
578
579void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
580 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
581 int result = pthread_key_delete(pthread_key);
582 USE(result);
583 ASSERT(result == 0);
584}
585
586
587void* Thread::GetThreadLocal(LocalStorageKey key) {
588 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
589 return pthread_getspecific(pthread_key);
590}
591
592
593void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
594 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
595 pthread_setspecific(pthread_key, value);
596}
597
598
599void Thread::YieldCPU() {
600 sched_yield();
601}
602
603
604class OpenBSDMutex : public Mutex {
605 public:
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000606 OpenBSDMutex() {
607 pthread_mutexattr_t attrs;
608 int result = pthread_mutexattr_init(&attrs);
609 ASSERT(result == 0);
610 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
611 ASSERT(result == 0);
612 result = pthread_mutex_init(&mutex_, &attrs);
613 ASSERT(result == 0);
danno@chromium.orgc612e022011-11-10 11:38:15 +0000614 USE(result);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000615 }
616
617 virtual ~OpenBSDMutex() { pthread_mutex_destroy(&mutex_); }
618
619 virtual int Lock() {
620 int result = pthread_mutex_lock(&mutex_);
621 return result;
622 }
623
624 virtual int Unlock() {
625 int result = pthread_mutex_unlock(&mutex_);
626 return result;
627 }
628
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000629 virtual bool TryLock() {
630 int result = pthread_mutex_trylock(&mutex_);
631 // Return false if the lock is busy and locking failed.
632 if (result == EBUSY) {
633 return false;
634 }
635 ASSERT(result == 0); // Verify no other errors.
636 return true;
637 }
638
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000639 private:
640 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
641};
642
643
644Mutex* OS::CreateMutex() {
645 return new OpenBSDMutex();
646}
647
648
649class OpenBSDSemaphore : public Semaphore {
650 public:
651 explicit OpenBSDSemaphore(int count) { sem_init(&sem_, 0, count); }
652 virtual ~OpenBSDSemaphore() { sem_destroy(&sem_); }
653
654 virtual void Wait();
655 virtual bool Wait(int timeout);
656 virtual void Signal() { sem_post(&sem_); }
657 private:
658 sem_t sem_;
659};
660
661
662void OpenBSDSemaphore::Wait() {
663 while (true) {
664 int result = sem_wait(&sem_);
665 if (result == 0) return; // Successfully got semaphore.
666 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
667 }
668}
669
670
danno@chromium.orgc612e022011-11-10 11:38:15 +0000671#ifndef TIMEVAL_TO_TIMESPEC
672#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
673 (ts)->tv_sec = (tv)->tv_sec; \
674 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
675} while (false)
676#endif
677
678
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000679bool OpenBSDSemaphore::Wait(int timeout) {
680 const long kOneSecondMicros = 1000000; // NOLINT
681
682 // Split timeout into second and nanosecond parts.
683 struct timeval delta;
684 delta.tv_usec = timeout % kOneSecondMicros;
685 delta.tv_sec = timeout / kOneSecondMicros;
686
687 struct timeval current_time;
688 // Get the current time.
689 if (gettimeofday(&current_time, NULL) == -1) {
690 return false;
691 }
692
693 // Calculate time for end of timeout.
694 struct timeval end_time;
695 timeradd(&current_time, &delta, &end_time);
696
697 struct timespec ts;
698 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000699
700 int to = ts.tv_sec;
701
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000702 while (true) {
703 int result = sem_trywait(&sem_);
704 if (result == 0) return true; // Successfully got semaphore.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000705 if (!to) return false; // Timeout.
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000706 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000707 usleep(ts.tv_nsec / 1000);
708 to--;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000709 }
710}
711
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000712Semaphore* OS::CreateSemaphore(int count) {
713 return new OpenBSDSemaphore(count);
714}
715
716
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000717static pthread_t GetThreadID() {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000718 return pthread_self();
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000719}
720
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000721static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
722 USE(info);
723 if (signal != SIGPROF) return;
724 Isolate* isolate = Isolate::UncheckedCurrent();
725 if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
726 // We require a fully initialized and entered isolate.
727 return;
728 }
729 if (v8::Locker::IsActive() &&
730 !isolate->thread_manager()->IsLockedByCurrentThread()) {
731 return;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000732 }
733
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000734 Sampler* sampler = isolate->logger()->sampler();
735 if (sampler == NULL || !sampler->IsActive()) return;
736
737 TickSample sample_obj;
738 TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
739 if (sample == NULL) sample = &sample_obj;
740
741 // Extracting the sample from the context is extremely machine dependent.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000742 sample->state = isolate->current_vm_state();
rossberg@chromium.orgfab14982012-01-05 15:02:15 +0000743 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
744#ifdef __NetBSD__
745 mcontext_t& mcontext = ucontext->uc_mcontext;
746#if V8_HOST_ARCH_IA32
747 sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_EIP]);
748 sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_ESP]);
749 sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_EBP]);
750#elif V8_HOST_ARCH_X64
751 sample->pc = reinterpret_cast<Address>(mcontext.__gregs[_REG_RIP]);
752 sample->sp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RSP]);
753 sample->fp = reinterpret_cast<Address>(mcontext.__gregs[_REG_RBP]);
754#endif // V8_HOST_ARCH
755#else // OpenBSD
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000756#if V8_HOST_ARCH_IA32
757 sample->pc = reinterpret_cast<Address>(ucontext->sc_eip);
758 sample->sp = reinterpret_cast<Address>(ucontext->sc_esp);
759 sample->fp = reinterpret_cast<Address>(ucontext->sc_ebp);
760#elif V8_HOST_ARCH_X64
761 sample->pc = reinterpret_cast<Address>(ucontext->sc_rip);
762 sample->sp = reinterpret_cast<Address>(ucontext->sc_rsp);
763 sample->fp = reinterpret_cast<Address>(ucontext->sc_rbp);
rossberg@chromium.orgfab14982012-01-05 15:02:15 +0000764#endif // V8_HOST_ARCH
765#endif // __NetBSD__
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000766 sampler->SampleStack(sample);
767 sampler->Tick(sample);
768}
769
770
danno@chromium.orgc612e022011-11-10 11:38:15 +0000771class Sampler::PlatformData : public Malloced {
772 public:
773 PlatformData() : vm_tid_(GetThreadID()) {}
774
775 pthread_t vm_tid() const { return vm_tid_; }
776
777 private:
778 pthread_t vm_tid_;
779};
780
781
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000782class SignalSender : public Thread {
783 public:
784 enum SleepInterval {
785 HALF_INTERVAL,
786 FULL_INTERVAL
787 };
788
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +0000789 static const int kSignalSenderStackSize = 64 * KB;
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000790
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000791 explicit SignalSender(int interval)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000792 : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
danno@chromium.orgc612e022011-11-10 11:38:15 +0000793 vm_tgid_(getpid()),
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000794 interval_(interval) {}
795
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000796 static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
797 static void TearDown() { delete mutex_; }
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000798
danno@chromium.orgc612e022011-11-10 11:38:15 +0000799 static void InstallSignalHandler() {
800 struct sigaction sa;
801 sa.sa_sigaction = ProfilerSignalHandler;
802 sigemptyset(&sa.sa_mask);
803 sa.sa_flags = SA_RESTART | SA_SIGINFO;
804 signal_handler_installed_ =
805 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
806 }
807
808 static void RestoreSignalHandler() {
809 if (signal_handler_installed_) {
810 sigaction(SIGPROF, &old_signal_handler_, 0);
811 signal_handler_installed_ = false;
812 }
813 }
814
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000815 static void AddActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000816 ScopedLock lock(mutex_);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000817 SamplerRegistry::AddActiveSampler(sampler);
818 if (instance_ == NULL) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000819 // Start a thread that will send SIGPROF signal to VM threads,
820 // when CPU profiling will be enabled.
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000821 instance_ = new SignalSender(sampler->interval());
822 instance_->Start();
823 } else {
824 ASSERT(instance_->interval_ == sampler->interval());
825 }
826 }
827
828 static void RemoveActiveSampler(Sampler* sampler) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000829 ScopedLock lock(mutex_);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000830 SamplerRegistry::RemoveActiveSampler(sampler);
831 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +0000832 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000833 delete instance_;
834 instance_ = NULL;
danno@chromium.orgc612e022011-11-10 11:38:15 +0000835 RestoreSignalHandler();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000836 }
837 }
838
839 // Implement Thread::Run().
840 virtual void Run() {
841 SamplerRegistry::State state;
842 while ((state = SamplerRegistry::GetState()) !=
843 SamplerRegistry::HAS_NO_SAMPLERS) {
844 bool cpu_profiling_enabled =
845 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
846 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
danno@chromium.orgc612e022011-11-10 11:38:15 +0000847 if (cpu_profiling_enabled && !signal_handler_installed_) {
848 InstallSignalHandler();
849 } else if (!cpu_profiling_enabled && signal_handler_installed_) {
850 RestoreSignalHandler();
851 }
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000852 // When CPU profiling is enabled both JavaScript and C++ code is
853 // profiled. We must not suspend.
854 if (!cpu_profiling_enabled) {
855 if (rate_limiter_.SuspendIfNecessary()) continue;
856 }
857 if (cpu_profiling_enabled && runtime_profiler_enabled) {
858 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
859 return;
860 }
861 Sleep(HALF_INTERVAL);
862 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
863 return;
864 }
865 Sleep(HALF_INTERVAL);
866 } else {
867 if (cpu_profiling_enabled) {
868 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
869 this)) {
870 return;
871 }
872 }
873 if (runtime_profiler_enabled) {
874 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
875 NULL)) {
876 return;
877 }
878 }
879 Sleep(FULL_INTERVAL);
880 }
881 }
882 }
883
884 static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
885 if (!sampler->IsProfiling()) return;
886 SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
887 sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
888 }
889
890 static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
891 if (!sampler->isolate()->IsInitialized()) return;
892 sampler->isolate()->runtime_profiler()->NotifyTick();
893 }
894
895 void SendProfilingSignal(pthread_t tid) {
896 if (!signal_handler_installed_) return;
897 pthread_kill(tid, SIGPROF);
898 }
899
900 void Sleep(SleepInterval full_or_half) {
901 // Convert ms to us and subtract 100 us to compensate delays
902 // occuring during signal delivery.
903 useconds_t interval = interval_ * 1000 - 100;
904 if (full_or_half == HALF_INTERVAL) interval /= 2;
905 int result = usleep(interval);
906#ifdef DEBUG
907 if (result != 0 && errno != EINTR) {
908 fprintf(stderr,
909 "SignalSender usleep error; interval = %u, errno = %d\n",
910 interval,
911 errno);
912 ASSERT(result == 0 || errno == EINTR);
913 }
914#endif
915 USE(result);
916 }
917
danno@chromium.orgc612e022011-11-10 11:38:15 +0000918 const int vm_tgid_;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000919 const int interval_;
920 RuntimeProfilerRateLimiter rate_limiter_;
921
922 // Protects the process wide state below.
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000923 static Mutex* mutex_;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000924 static SignalSender* instance_;
925 static bool signal_handler_installed_;
926 static struct sigaction old_signal_handler_;
927
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +0000928 private:
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000929 DISALLOW_COPY_AND_ASSIGN(SignalSender);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000930};
931
danno@chromium.orgc612e022011-11-10 11:38:15 +0000932
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000933Mutex* SignalSender::mutex_ = NULL;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000934SignalSender* SignalSender::instance_ = NULL;
935struct sigaction SignalSender::old_signal_handler_;
936bool SignalSender::signal_handler_installed_ = false;
937
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000938
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000939void OS::SetUp() {
940 // Seed the random number generator. We preserve microsecond resolution.
941 uint64_t seed = Ticks() ^ (getpid() << 16);
942 srandom(static_cast<unsigned int>(seed));
943 limit_mutex = CreateMutex();
944 SignalSender::SetUp();
945}
946
947
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000948void OS::TearDown() {
949 SignalSender::TearDown();
950 delete limit_mutex;
951}
952
953
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000954Sampler::Sampler(Isolate* isolate, int interval)
955 : isolate_(isolate),
956 interval_(interval),
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000957 profiling_(false),
ager@chromium.orgbeb25712010-11-29 08:02:25 +0000958 active_(false),
959 samples_taken_(0) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000960 data_ = new PlatformData;
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000961}
962
963
964Sampler::~Sampler() {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000965 ASSERT(!IsActive());
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000966 delete data_;
967}
968
969
970void Sampler::Start() {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000971 ASSERT(!IsActive());
972 SetActive(true);
973 SignalSender::AddActiveSampler(this);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000974}
975
976
977void Sampler::Stop() {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +0000978 ASSERT(IsActive());
979 SignalSender::RemoveActiveSampler(this);
980 SetActive(false);
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000981}
982
sgjesse@chromium.orgac6aa172009-12-04 12:29:05 +0000983
984} } // namespace v8::internal