blob: 4c7b01759231c74f8167773401e55a482e9923a5 [file] [log] [blame]
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001// Copyright 2012 the V8 project authors. All rights reserved.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Platform specific code for Cygwin goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
30
31#include <errno.h>
32#include <pthread.h>
33#include <semaphore.h>
34#include <stdarg.h>
35#include <strings.h> // index
36#include <sys/time.h>
37#include <sys/mman.h> // mmap & munmap
38#include <unistd.h> // sysconf
39
40#undef MAP_TYPE
41
42#include "v8.h"
43
danno@chromium.org8c0a43f2012-04-03 08:37:53 +000044#include "platform-posix.h"
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000045#include "platform.h"
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +000046#include "simulator.h"
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000047#include "v8threads.h"
48#include "vm-state-inl.h"
49#include "win32-headers.h"
50
51namespace v8 {
52namespace internal {
53
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000054
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000055static Mutex* limit_mutex = NULL;
56
57
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000058const char* OS::LocalTimezone(double time) {
ulan@chromium.org77ca49a2013-04-22 09:43:56 +000059 if (std::isnan(time)) return "";
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000060 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
61 struct tm* t = localtime(&tv);
62 if (NULL == t) return "";
63 return tzname[0]; // The location of the timezone string on Cygwin.
64}
65
66
67double OS::LocalTimeOffset() {
68 // On Cygwin, struct tm does not contain a tm_gmtoff field.
69 time_t utc = time(NULL);
70 ASSERT(utc != -1);
71 struct tm* loc = localtime(&utc);
72 ASSERT(loc != NULL);
73 // time - localtime includes any daylight savings offset, so subtract it.
74 return static_cast<double>((mktime(loc) - utc) * msPerSecond -
75 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0));
76}
77
78
79// We keep the lowest and highest addresses mapped as a quick way of
80// determining that pointers are outside the heap (used mostly in assertions
ulan@chromium.org2efb9002012-01-19 15:36:35 +000081// and verification). The estimate is conservative, i.e., not all addresses in
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000082// 'allocated' space are actually allocated to our heap. The range is
83// [lowest, highest), inclusive on the low and and exclusive on the high end.
84static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
85static void* highest_ever_allocated = reinterpret_cast<void*>(0);
86
87
88static void UpdateAllocatedSpaceLimits(void* address, int size) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +000089 ASSERT(limit_mutex != NULL);
90 ScopedLock lock(limit_mutex);
91
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +000092 lowest_ever_allocated = Min(lowest_ever_allocated, address);
93 highest_ever_allocated =
94 Max(highest_ever_allocated,
95 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
96}
97
98
99bool OS::IsOutsideAllocatedSpace(void* address) {
100 return address < lowest_ever_allocated || address >= highest_ever_allocated;
101}
102
103
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000104void* OS::Allocate(const size_t requested,
105 size_t* allocated,
106 bool is_executable) {
107 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
108 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
109 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
110 if (mbase == MAP_FAILED) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000111 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000112 return NULL;
113 }
114 *allocated = msize;
115 UpdateAllocatedSpaceLimits(mbase, msize);
116 return mbase;
117}
118
119
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000120void OS::DumpBacktrace() {
121 // Currently unsupported.
122}
123
124
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000125class PosixMemoryMappedFile : public OS::MemoryMappedFile {
126 public:
127 PosixMemoryMappedFile(FILE* file, void* memory, int size)
128 : file_(file), memory_(memory), size_(size) { }
129 virtual ~PosixMemoryMappedFile();
130 virtual void* memory() { return memory_; }
131 virtual int size() { return size_; }
132 private:
133 FILE* file_;
134 void* memory_;
135 int size_;
136};
137
138
139OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
140 FILE* file = fopen(name, "r+");
141 if (file == NULL) return NULL;
142
143 fseek(file, 0, SEEK_END);
144 int size = ftell(file);
145
146 void* memory =
147 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
148 return new PosixMemoryMappedFile(file, memory, size);
149}
150
151
152OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
153 void* initial) {
154 FILE* file = fopen(name, "w+");
155 if (file == NULL) return NULL;
156 int result = fwrite(initial, size, 1, file);
157 if (result < 1) {
158 fclose(file);
159 return NULL;
160 }
161 void* memory =
162 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
163 return new PosixMemoryMappedFile(file, memory, size);
164}
165
166
167PosixMemoryMappedFile::~PosixMemoryMappedFile() {
168 if (memory_) munmap(memory_, size_);
169 fclose(file_);
170}
171
172
173void OS::LogSharedLibraryAddresses() {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000174 // This function assumes that the layout of the file is as follows:
175 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
176 // If we encounter an unexpected situation we abort scanning further entries.
177 FILE* fp = fopen("/proc/self/maps", "r");
178 if (fp == NULL) return;
179
180 // Allocate enough room to be able to store a full file name.
181 const int kLibNameLen = FILENAME_MAX + 1;
182 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
183
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000184 i::Isolate* isolate = ISOLATE;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000185 // This loop will terminate once the scanning hits an EOF.
186 while (true) {
187 uintptr_t start, end;
188 char attr_r, attr_w, attr_x, attr_p;
189 // Parse the addresses and permission bits at the beginning of the line.
190 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
191 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
192
193 int c;
194 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
195 // Found a read-only executable entry. Skip characters until we reach
196 // the beginning of the filename or the end of the line.
197 do {
198 c = getc(fp);
199 } while ((c != EOF) && (c != '\n') && (c != '/'));
200 if (c == EOF) break; // EOF: Was unexpected, just exit.
201
202 // Process the filename if found.
203 if (c == '/') {
204 ungetc(c, fp); // Push the '/' back into the stream to be read below.
205
206 // Read to the end of the line. Exit if the read fails.
207 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
208
209 // Drop the newline character read by fgets. We do not need to check
210 // for a zero-length string because we know that we at least read the
211 // '/' character.
212 lib_name[strlen(lib_name) - 1] = '\0';
213 } else {
214 // No library name found, just record the raw address range.
215 snprintf(lib_name, kLibNameLen,
216 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
217 }
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000218 LOG(isolate, SharedLibraryEvent(lib_name, start, end));
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000219 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000220 // Entry not describing executable data. Skip to end of line to set up
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000221 // reading the next entry.
222 do {
223 c = getc(fp);
224 } while ((c != EOF) && (c != '\n'));
225 if (c == EOF) break;
226 }
227 }
228 free(lib_name);
229 fclose(fp);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000230}
231
232
233void OS::SignalCodeMovingGC() {
234 // Nothing to do on Cygwin.
235}
236
237
238int OS::StackWalk(Vector<OS::StackFrame> frames) {
239 // Not supported on Cygwin.
240 return 0;
241}
242
243
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000244// The VirtualMemory implementation is taken from platform-win32.cc.
245// The mmap-based virtual memory implementation as it is used on most posix
246// platforms does not work well because Cygwin does not support MAP_FIXED.
247// This causes VirtualMemory::Commit to not always commit the memory region
248// specified.
249
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000250static void* GetRandomAddr() {
251 Isolate* isolate = Isolate::UncheckedCurrent();
252 // Note that the current isolate isn't set up in a call path via
253 // CpuFeatures::Probe. We don't care about randomization in this case because
254 // the code page is immediately freed.
255 if (isolate != NULL) {
256 // The address range used to randomize RWX allocations in OS::Allocate
257 // Try not to map pages into the default range that windows loads DLLs
258 // Use a multiple of 64k to prevent committing unused memory.
259 // Note: This does not guarantee RWX regions will be within the
260 // range kAllocationRandomAddressMin to kAllocationRandomAddressMax
261#ifdef V8_HOST_ARCH_64_BIT
262 static const intptr_t kAllocationRandomAddressMin = 0x0000000080000000;
263 static const intptr_t kAllocationRandomAddressMax = 0x000003FFFFFF0000;
264#else
265 static const intptr_t kAllocationRandomAddressMin = 0x04000000;
266 static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000;
267#endif
268 uintptr_t address = (V8::RandomPrivate(isolate) << kPageSizeBits)
269 | kAllocationRandomAddressMin;
270 address &= kAllocationRandomAddressMax;
271 return reinterpret_cast<void *>(address);
272 }
273 return NULL;
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000274}
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000275
276
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000277static void* RandomizedVirtualAlloc(size_t size, int action, int protection) {
278 LPVOID base = NULL;
279
280 if (protection == PAGE_EXECUTE_READWRITE || protection == PAGE_NOACCESS) {
281 // For exectutable pages try and randomize the allocation address
282 for (size_t attempts = 0; base == NULL && attempts < 3; ++attempts) {
283 base = VirtualAlloc(GetRandomAddr(), size, action, protection);
284 }
285 }
286
287 // After three attempts give up and let the OS find an address to use.
288 if (base == NULL) base = VirtualAlloc(NULL, size, action, protection);
289
290 return base;
291}
292
293
294VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
295
296
297VirtualMemory::VirtualMemory(size_t size)
298 : address_(ReserveRegion(size)), size_(size) { }
299
300
301VirtualMemory::VirtualMemory(size_t size, size_t alignment)
302 : address_(NULL), size_(0) {
303 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
304 size_t request_size = RoundUp(size + alignment,
305 static_cast<intptr_t>(OS::AllocateAlignment()));
306 void* address = ReserveRegion(request_size);
307 if (address == NULL) return;
308 Address base = RoundUp(static_cast<Address>(address), alignment);
309 // Try reducing the size by freeing and then reallocating a specific area.
310 bool result = ReleaseRegion(address, request_size);
311 USE(result);
312 ASSERT(result);
313 address = VirtualAlloc(base, size, MEM_RESERVE, PAGE_NOACCESS);
314 if (address != NULL) {
315 request_size = size;
316 ASSERT(base == static_cast<Address>(address));
317 } else {
318 // Resizing failed, just go with a bigger area.
319 address = ReserveRegion(request_size);
320 if (address == NULL) return;
321 }
322 address_ = address;
323 size_ = request_size;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000324}
325
326
327VirtualMemory::~VirtualMemory() {
328 if (IsReserved()) {
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000329 bool result = ReleaseRegion(address_, size_);
330 ASSERT(result);
331 USE(result);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000332 }
333}
334
335
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000336bool VirtualMemory::IsReserved() {
337 return address_ != NULL;
338}
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000339
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000340
341void VirtualMemory::Reset() {
342 address_ = NULL;
343 size_ = 0;
344}
345
346
347bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
348 return CommitRegion(address, size, is_executable);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000349}
350
351
352bool VirtualMemory::Uncommit(void* address, size_t size) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +0000353 ASSERT(IsReserved());
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000354 return UncommitRegion(address, size);
355}
356
357
358void* VirtualMemory::ReserveRegion(size_t size) {
359 return RandomizedVirtualAlloc(size, MEM_RESERVE, PAGE_NOACCESS);
360}
361
362
363bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
364 int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
365 if (NULL == VirtualAlloc(base, size, MEM_COMMIT, prot)) {
366 return false;
367 }
368
369 UpdateAllocatedSpaceLimits(base, static_cast<int>(size));
370 return true;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000371}
372
373
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000374bool VirtualMemory::Guard(void* address) {
375 if (NULL == VirtualAlloc(address,
376 OS::CommitPageSize(),
377 MEM_COMMIT,
378 PAGE_READONLY | PAGE_GUARD)) {
379 return false;
380 }
381 return true;
382}
383
384
mstarzinger@chromium.orge27d6172013-04-17 11:51:44 +0000385bool VirtualMemory::UncommitRegion(void* base, size_t size) {
386 return VirtualFree(base, size, MEM_DECOMMIT) != 0;
387}
388
389
390bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
391 return VirtualFree(base, 0, MEM_RELEASE) != 0;
392}
393
394
danno@chromium.org72204d52012-10-31 10:02:10 +0000395bool VirtualMemory::HasLazyCommits() {
396 // TODO(alph): implement for the platform.
397 return false;
398}
399
400
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000401class CygwinSemaphore : public Semaphore {
402 public:
403 explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); }
404 virtual ~CygwinSemaphore() { sem_destroy(&sem_); }
405
406 virtual void Wait();
407 virtual bool Wait(int timeout);
408 virtual void Signal() { sem_post(&sem_); }
409 private:
410 sem_t sem_;
411};
412
413
414void CygwinSemaphore::Wait() {
415 while (true) {
416 int result = sem_wait(&sem_);
417 if (result == 0) return; // Successfully got semaphore.
418 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
419 }
420}
421
422
423#ifndef TIMEVAL_TO_TIMESPEC
424#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
425 (ts)->tv_sec = (tv)->tv_sec; \
426 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
427} while (false)
428#endif
429
430
431bool CygwinSemaphore::Wait(int timeout) {
432 const long kOneSecondMicros = 1000000; // NOLINT
433
434 // Split timeout into second and nanosecond parts.
435 struct timeval delta;
436 delta.tv_usec = timeout % kOneSecondMicros;
437 delta.tv_sec = timeout / kOneSecondMicros;
438
439 struct timeval current_time;
440 // Get the current time.
441 if (gettimeofday(&current_time, NULL) == -1) {
442 return false;
443 }
444
445 // Calculate time for end of timeout.
446 struct timeval end_time;
447 timeradd(&current_time, &delta, &end_time);
448
449 struct timespec ts;
450 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
451 // Wait for semaphore signalled or timeout.
452 while (true) {
453 int result = sem_timedwait(&sem_, &ts);
454 if (result == 0) return true; // Successfully got semaphore.
455 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
456 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
457 }
458}
459
460
461Semaphore* OS::CreateSemaphore(int count) {
462 return new CygwinSemaphore(count);
463}
464
465
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000466void OS::SetUp() {
467 // Seed the random number generator.
468 // Convert the current time to a 64-bit integer first, before converting it
469 // to an unsigned. Going directly can cause an overflow and the seed to be
470 // set to all ones. The seed will be identical for different instances that
471 // call this setup code within the same millisecond.
472 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
473 srandom(static_cast<unsigned int>(seed));
474 limit_mutex = CreateMutex();
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000475}
476
477
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000478void OS::TearDown() {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +0000479 delete limit_mutex;
480}
481
482
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000483} } // namespace v8::internal