blob: eefaec9042b8741da4f9ed81d05e399e41bb6fd0 [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +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 Linux goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000030
31#include <pthread.h>
32#include <semaphore.h>
33#include <signal.h>
34#include <sys/time.h>
35#include <sys/resource.h>
lrn@chromium.org303ada72010-10-27 09:33:13 +000036#include <sys/syscall.h>
ager@chromium.org381abbb2009-02-25 13:23:22 +000037#include <sys/types.h>
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038#include <stdlib.h>
39
40// Ubuntu Dapper requires memory pages to be marked as
41// executable. Otherwise, OS raises an exception when executing code
42// in that page.
43#include <sys/types.h> // mmap & munmap
ager@chromium.org236ad962008-09-25 09:45:57 +000044#include <sys/mman.h> // mmap & munmap
45#include <sys/stat.h> // open
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000046#include <fcntl.h> // open
47#include <unistd.h> // sysconf
48#ifdef __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000049#include <execinfo.h> // backtrace, backtrace_symbols
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000050#endif // def __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000051#include <strings.h> // index
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000052#include <errno.h>
53#include <stdarg.h>
54
55#undef MAP_TYPE
56
57#include "v8.h"
58
59#include "platform.h"
ager@chromium.orga1645e22009-09-09 19:27:10 +000060#include "top.h"
61#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000062
63
kasperl@chromium.org71affb52009-05-26 05:44:31 +000064namespace v8 {
65namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000066
67// 0 is never a valid thread id on Linux since tids and pids share a
68// name space and pid 0 is reserved (see man 2 kill).
69static const pthread_t kNoThread = (pthread_t) 0;
70
71
72double ceiling(double x) {
73 return ceil(x);
74}
75
76
77void OS::Setup() {
78 // Seed the random number generator.
ager@chromium.org9258b6b2008-09-11 09:11:10 +000079 // Convert the current time to a 64-bit integer first, before converting it
80 // to an unsigned. Going directly can cause an overflow and the seed to be
81 // set to all ones. The seed will be identical for different instances that
82 // call this setup code within the same millisecond.
83 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
84 srandom(static_cast<unsigned int>(seed));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000085}
86
87
ager@chromium.orgc4c92722009-11-18 14:12:51 +000088uint64_t OS::CpuFeaturesImpliedByPlatform() {
89#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
90 // Here gcc is telling us that we are on an ARM and gcc is assuming that we
91 // have VFP3 instructions. If gcc can assume it then so can we.
92 return 1u << VFP3;
ager@chromium.org5c838252010-02-19 08:53:10 +000093#elif CAN_USE_ARMV7_INSTRUCTIONS
94 return 1u << ARMv7;
ager@chromium.orgc4c92722009-11-18 14:12:51 +000095#else
96 return 0; // Linux runs on anything.
97#endif
98}
99
100
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000101#ifdef __arm__
102bool OS::ArmCpuHasFeature(CpuFeature feature) {
103 const char* search_string = NULL;
104 const char* file_name = "/proc/cpuinfo";
105 // Simple detection of VFP at runtime for Linux.
106 // It is based on /proc/cpuinfo, which reveals hardware configuration
107 // to user-space applications. According to ARM (mid 2009), no similar
108 // facility is universally available on the ARM architectures,
109 // so it's up to individual OSes to provide such.
110 //
111 // This is written as a straight shot one pass parser
112 // and not using STL string and ifstream because,
113 // on Linux, it's reading from a (non-mmap-able)
114 // character special device.
115 switch (feature) {
116 case VFP3:
117 search_string = "vfp";
118 break;
ager@chromium.org5c838252010-02-19 08:53:10 +0000119 case ARMv7:
120 search_string = "ARMv7";
121 break;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000122 default:
123 UNREACHABLE();
124 }
125
126 FILE* f = NULL;
127 const char* what = search_string;
128
129 if (NULL == (f = fopen(file_name, "r")))
130 return false;
131
132 int k;
133 while (EOF != (k = fgetc(f))) {
134 if (k == *what) {
135 ++what;
136 while ((*what != '\0') && (*what == fgetc(f))) {
137 ++what;
138 }
139 if (*what == '\0') {
140 fclose(f);
141 return true;
142 } else {
143 what = search_string;
144 }
145 }
146 }
147 fclose(f);
148
149 // Did not find string in the proc file.
150 return false;
151}
152#endif // def __arm__
153
154
ager@chromium.org236ad962008-09-25 09:45:57 +0000155int OS::ActivationFrameAlignment() {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000156#ifdef V8_TARGET_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000157 // On EABI ARM targets this is required for fp correctness in the
158 // runtime system.
ager@chromium.org3a6061e2009-03-12 14:24:36 +0000159 return 8;
ager@chromium.org5c838252010-02-19 08:53:10 +0000160#elif V8_TARGET_ARCH_MIPS
161 return 8;
162#endif
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000163 // With gcc 4.4 the tree vectorization optimizer can generate code
ager@chromium.orge2902be2009-06-08 12:21:35 +0000164 // that requires 16 byte alignment such as movdqa on x86.
165 return 16;
ager@chromium.org236ad962008-09-25 09:45:57 +0000166}
167
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000168
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000169#ifdef V8_TARGET_ARCH_ARM
170// 0xffff0fa0 is the hard coded address of a function provided by
171// the kernel which implements a memory barrier. On older
172// ARM architecture revisions (pre-v6) this may be implemented using
173// a syscall. This address is stable, and in active use (hard coded)
174// by at least glibc-2.7 and the Android C library.
175typedef void (*LinuxKernelMemoryBarrierFunc)(void);
176LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
177 (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
178#endif
179
180void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000181#if defined(V8_TARGET_ARCH_ARM) && defined(__arm__)
182 // Only use on ARM hardware.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000183 pLinuxKernelMemoryBarrier();
184#else
185 __asm__ __volatile__("" : : : "memory");
186 // An x86 store acts as a release barrier.
187#endif
188 *ptr = value;
189}
190
191
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000192const char* OS::LocalTimezone(double time) {
193 if (isnan(time)) return "";
194 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
195 struct tm* t = localtime(&tv);
196 if (NULL == t) return "";
197 return t->tm_zone;
198}
199
200
201double OS::LocalTimeOffset() {
202 time_t tv = time(NULL);
203 struct tm* t = localtime(&tv);
204 // tm_gmtoff includes any daylight savings offset, so subtract it.
205 return static_cast<double>(t->tm_gmtoff * msPerSecond -
206 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
207}
208
209
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000210// We keep the lowest and highest addresses mapped as a quick way of
211// determining that pointers are outside the heap (used mostly in assertions
212// and verification). The estimate is conservative, ie, not all addresses in
213// 'allocated' space are actually allocated to our heap. The range is
214// [lowest, highest), inclusive on the low and and exclusive on the high end.
215static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
216static void* highest_ever_allocated = reinterpret_cast<void*>(0);
217
218
219static void UpdateAllocatedSpaceLimits(void* address, int size) {
220 lowest_ever_allocated = Min(lowest_ever_allocated, address);
221 highest_ever_allocated =
222 Max(highest_ever_allocated,
223 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
224}
225
226
227bool OS::IsOutsideAllocatedSpace(void* address) {
228 return address < lowest_ever_allocated || address >= highest_ever_allocated;
229}
230
231
232size_t OS::AllocateAlignment() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000233 return sysconf(_SC_PAGESIZE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000234}
235
236
kasper.lund7276f142008-07-30 08:49:36 +0000237void* OS::Allocate(const size_t requested,
238 size_t* allocated,
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000239 bool is_executable) {
sgjesse@chromium.orgc3a01972010-08-04 09:46:24 +0000240 // TODO(805): Port randomization of allocated executable memory to Linux.
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000241 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000242 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
kasper.lund7276f142008-07-30 08:49:36 +0000243 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000244 if (mbase == MAP_FAILED) {
245 LOG(StringEvent("OS::Allocate", "mmap failed"));
246 return NULL;
247 }
248 *allocated = msize;
249 UpdateAllocatedSpaceLimits(mbase, msize);
250 return mbase;
251}
252
253
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000254void OS::Free(void* address, const size_t size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000255 // TODO(1240712): munmap has a return value which is ignored here.
ager@chromium.orga1645e22009-09-09 19:27:10 +0000256 int result = munmap(address, size);
257 USE(result);
258 ASSERT(result == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000259}
260
261
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000262#ifdef ENABLE_HEAP_PROTECTION
263
264void OS::Protect(void* address, size_t size) {
265 // TODO(1240712): mprotect has a return value which is ignored here.
266 mprotect(address, size, PROT_READ);
267}
268
269
270void OS::Unprotect(void* address, size_t size, bool is_executable) {
271 // TODO(1240712): mprotect has a return value which is ignored here.
272 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
273 mprotect(address, size, prot);
274}
275
276#endif
277
278
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279void OS::Sleep(int milliseconds) {
280 unsigned int ms = static_cast<unsigned int>(milliseconds);
281 usleep(1000 * ms);
282}
283
284
285void OS::Abort() {
286 // Redirect to std abort to signal abnormal program termination.
287 abort();
288}
289
290
kasper.lund7276f142008-07-30 08:49:36 +0000291void OS::DebugBreak() {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000292// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
293// which is the architecture of generated code).
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000294#if (defined(__arm__) || defined(__thumb__))
295# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
kasper.lund7276f142008-07-30 08:49:36 +0000296 asm("bkpt 0");
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000297# endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000298#elif defined(__mips__)
299 asm("break");
kasper.lund7276f142008-07-30 08:49:36 +0000300#else
301 asm("int $3");
302#endif
303}
304
305
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000306class PosixMemoryMappedFile : public OS::MemoryMappedFile {
307 public:
308 PosixMemoryMappedFile(FILE* file, void* memory, int size)
309 : file_(file), memory_(memory), size_(size) { }
310 virtual ~PosixMemoryMappedFile();
311 virtual void* memory() { return memory_; }
312 private:
313 FILE* file_;
314 void* memory_;
315 int size_;
316};
317
318
319OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
320 void* initial) {
321 FILE* file = fopen(name, "w+");
322 if (file == NULL) return NULL;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000323 int result = fwrite(initial, size, 1, file);
324 if (result < 1) {
325 fclose(file);
326 return NULL;
327 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000328 void* memory =
329 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
330 return new PosixMemoryMappedFile(file, memory, size);
331}
332
333
334PosixMemoryMappedFile::~PosixMemoryMappedFile() {
335 if (memory_) munmap(memory_, size_);
336 fclose(file_);
337}
338
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000339
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340void OS::LogSharedLibraryAddresses() {
341#ifdef ENABLE_LOGGING_AND_PROFILING
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000342 // This function assumes that the layout of the file is as follows:
343 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
344 // If we encounter an unexpected situation we abort scanning further entries.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000345 FILE* fp = fopen("/proc/self/maps", "r");
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000346 if (fp == NULL) return;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000347
348 // Allocate enough room to be able to store a full file name.
349 const int kLibNameLen = FILENAME_MAX + 1;
350 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
351
352 // This loop will terminate once the scanning hits an EOF.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000353 while (true) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000354 uintptr_t start, end;
355 char attr_r, attr_w, attr_x, attr_p;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000356 // Parse the addresses and permission bits at the beginning of the line.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000357 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
358 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000359
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000360 int c;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000361 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
362 // Found a read-only executable entry. Skip characters until we reach
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000363 // the beginning of the filename or the end of the line.
364 do {
365 c = getc(fp);
366 } while ((c != EOF) && (c != '\n') && (c != '/'));
367 if (c == EOF) break; // EOF: Was unexpected, just exit.
368
369 // Process the filename if found.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000370 if (c == '/') {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000371 ungetc(c, fp); // Push the '/' back into the stream to be read below.
372
373 // Read to the end of the line. Exit if the read fails.
374 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
375
376 // Drop the newline character read by fgets. We do not need to check
377 // for a zero-length string because we know that we at least read the
378 // '/' character.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000379 lib_name[strlen(lib_name) - 1] = '\0';
380 } else {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000381 // No library name found, just record the raw address range.
382 snprintf(lib_name, kLibNameLen,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000383 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
384 }
385 LOG(SharedLibraryEvent(lib_name, start, end));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000386 } else {
387 // Entry not describing executable data. Skip to end of line to setup
388 // reading the next entry.
389 do {
390 c = getc(fp);
391 } while ((c != EOF) && (c != '\n'));
392 if (c == EOF) break;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000393 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000394 }
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000395 free(lib_name);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000396 fclose(fp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000397#endif
398}
399
400
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000401static const char kGCFakeMmap[] = "/tmp/__v8_gc__";
402
403
404void OS::SignalCodeMovingGC() {
405#ifdef ENABLE_LOGGING_AND_PROFILING
406 // Support for ll_prof.py.
407 //
408 // The Linux profiler built into the kernel logs all mmap's with
409 // PROT_EXEC so that analysis tools can properly attribute ticks. We
410 // do a mmap with a name known by ll_prof.py and immediately munmap
411 // it. This injects a GC marker into the stream of events generated
412 // by the kernel and allows us to synchronize V8 code log and the
413 // kernel log.
414 int size = sysconf(_SC_PAGESIZE);
415 FILE* f = fopen(kGCFakeMmap, "w+");
416 void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE,
417 fileno(f), 0);
418 ASSERT(addr != MAP_FAILED);
419 munmap(addr, size);
420 fclose(f);
421#endif
422}
423
424
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000425int OS::StackWalk(Vector<OS::StackFrame> frames) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000426 // backtrace is a glibc extension.
427#ifdef __GLIBC__
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000428 int frames_size = frames.length();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000429 ScopedVector<void*> addresses(frames_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000430
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000431 int frames_count = backtrace(addresses.start(), frames_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000432
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000433 char** symbols = backtrace_symbols(addresses.start(), frames_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000434 if (symbols == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000435 return kStackWalkError;
436 }
437
438 for (int i = 0; i < frames_count; i++) {
439 frames[i].address = addresses[i];
440 // Format a text representation of the frame based on the information
441 // available.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000442 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
443 "%s",
444 symbols[i]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000445 // Make sure line termination is in place.
446 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
447 }
448
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000449 free(symbols);
450
451 return frames_count;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000452#else // ndef __GLIBC__
453 return 0;
454#endif // ndef __GLIBC__
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000455}
456
457
458// Constants used for mmap.
459static const int kMmapFd = -1;
460static const int kMmapFdOffset = 0;
461
462
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000463VirtualMemory::VirtualMemory(size_t size) {
464 address_ = mmap(NULL, size, PROT_NONE,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000465 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
466 kMmapFd, kMmapFdOffset);
467 size_ = size;
468}
469
470
471VirtualMemory::~VirtualMemory() {
472 if (IsReserved()) {
473 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
474 }
475}
476
477
478bool VirtualMemory::IsReserved() {
479 return address_ != MAP_FAILED;
480}
481
482
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000483bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
484 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
kasper.lund7276f142008-07-30 08:49:36 +0000485 if (MAP_FAILED == mmap(address, size, prot,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000486 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
487 kMmapFd, kMmapFdOffset)) {
488 return false;
489 }
490
491 UpdateAllocatedSpaceLimits(address, size);
492 return true;
493}
494
495
496bool VirtualMemory::Uncommit(void* address, size_t size) {
497 return mmap(address, size, PROT_NONE,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000498 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000499 kMmapFd, kMmapFdOffset) != MAP_FAILED;
500}
501
502
503class ThreadHandle::PlatformData : public Malloced {
504 public:
505 explicit PlatformData(ThreadHandle::Kind kind) {
506 Initialize(kind);
507 }
508
509 void Initialize(ThreadHandle::Kind kind) {
510 switch (kind) {
511 case ThreadHandle::SELF: thread_ = pthread_self(); break;
512 case ThreadHandle::INVALID: thread_ = kNoThread; break;
513 }
514 }
ager@chromium.org41826e72009-03-30 13:30:57 +0000515
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000516 pthread_t thread_; // Thread handle for pthread.
517};
518
519
520ThreadHandle::ThreadHandle(Kind kind) {
521 data_ = new PlatformData(kind);
522}
523
524
525void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
526 data_->Initialize(kind);
527}
528
529
530ThreadHandle::~ThreadHandle() {
531 delete data_;
532}
533
534
535bool ThreadHandle::IsSelf() const {
536 return pthread_equal(data_->thread_, pthread_self());
537}
538
539
540bool ThreadHandle::IsValid() const {
541 return data_->thread_ != kNoThread;
542}
543
544
545Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
546}
547
548
549Thread::~Thread() {
550}
551
552
553static void* ThreadEntry(void* arg) {
554 Thread* thread = reinterpret_cast<Thread*>(arg);
555 // This is also initialized by the first argument to pthread_create() but we
556 // don't know which thread will run first (the original thread or the new
557 // one) so we initialize it here too.
558 thread->thread_handle_data()->thread_ = pthread_self();
559 ASSERT(thread->IsValid());
560 thread->Run();
561 return NULL;
562}
563
564
565void Thread::Start() {
566 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
567 ASSERT(IsValid());
568}
569
570
571void Thread::Join() {
572 pthread_join(thread_handle_data()->thread_, NULL);
573}
574
575
576Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
577 pthread_key_t key;
578 int result = pthread_key_create(&key, NULL);
579 USE(result);
580 ASSERT(result == 0);
581 return static_cast<LocalStorageKey>(key);
582}
583
584
585void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
586 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
587 int result = pthread_key_delete(pthread_key);
588 USE(result);
589 ASSERT(result == 0);
590}
591
592
593void* Thread::GetThreadLocal(LocalStorageKey key) {
594 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
595 return pthread_getspecific(pthread_key);
596}
597
598
599void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
600 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
601 pthread_setspecific(pthread_key, value);
602}
603
604
605void Thread::YieldCPU() {
606 sched_yield();
607}
608
609
610class LinuxMutex : public Mutex {
611 public:
612
613 LinuxMutex() {
614 pthread_mutexattr_t attrs;
615 int result = pthread_mutexattr_init(&attrs);
616 ASSERT(result == 0);
617 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
618 ASSERT(result == 0);
619 result = pthread_mutex_init(&mutex_, &attrs);
620 ASSERT(result == 0);
621 }
622
623 virtual ~LinuxMutex() { pthread_mutex_destroy(&mutex_); }
624
625 virtual int Lock() {
626 int result = pthread_mutex_lock(&mutex_);
627 return result;
628 }
629
630 virtual int Unlock() {
631 int result = pthread_mutex_unlock(&mutex_);
632 return result;
633 }
634
635 private:
636 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
637};
638
639
640Mutex* OS::CreateMutex() {
641 return new LinuxMutex();
642}
643
644
645class LinuxSemaphore : public Semaphore {
646 public:
647 explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); }
648 virtual ~LinuxSemaphore() { sem_destroy(&sem_); }
649
kasper.lund7276f142008-07-30 08:49:36 +0000650 virtual void Wait();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000651 virtual bool Wait(int timeout);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000652 virtual void Signal() { sem_post(&sem_); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000653 private:
654 sem_t sem_;
655};
656
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000657
kasper.lund7276f142008-07-30 08:49:36 +0000658void LinuxSemaphore::Wait() {
659 while (true) {
660 int result = sem_wait(&sem_);
661 if (result == 0) return; // Successfully got semaphore.
662 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
663 }
664}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000665
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000666
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000667#ifndef TIMEVAL_TO_TIMESPEC
668#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
669 (ts)->tv_sec = (tv)->tv_sec; \
670 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
671} while (false)
672#endif
673
674
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000675bool LinuxSemaphore::Wait(int timeout) {
676 const long kOneSecondMicros = 1000000; // NOLINT
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000677
678 // Split timeout into second and nanosecond parts.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000679 struct timeval delta;
680 delta.tv_usec = timeout % kOneSecondMicros;
681 delta.tv_sec = timeout / kOneSecondMicros;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000682
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000683 struct timeval current_time;
684 // Get the current time.
685 if (gettimeofday(&current_time, NULL) == -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000686 return false;
687 }
688
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000689 // Calculate time for end of timeout.
690 struct timeval end_time;
691 timeradd(&current_time, &delta, &end_time);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000692
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000693 struct timespec ts;
694 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000695 // Wait for semaphore signalled or timeout.
696 while (true) {
697 int result = sem_timedwait(&sem_, &ts);
698 if (result == 0) return true; // Successfully got semaphore.
699 if (result > 0) {
700 // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
701 errno = result;
702 result = -1;
703 }
704 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
705 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
706 }
707}
708
709
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000710Semaphore* OS::CreateSemaphore(int count) {
711 return new LinuxSemaphore(count);
712}
713
ager@chromium.org381abbb2009-02-25 13:23:22 +0000714
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000715#ifdef ENABLE_LOGGING_AND_PROFILING
716
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000717static Sampler* active_sampler_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000718
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000719
720#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
721// Android runs a fairly new Linux kernel, so signal info is there,
722// but the C library doesn't have the structs defined.
723
724struct sigcontext {
725 uint32_t trap_no;
726 uint32_t error_code;
727 uint32_t oldmask;
728 uint32_t gregs[16];
729 uint32_t arm_cpsr;
730 uint32_t fault_address;
731};
732typedef uint32_t __sigset_t;
733typedef struct sigcontext mcontext_t;
734typedef struct ucontext {
735 uint32_t uc_flags;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000736 struct ucontext* uc_link;
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000737 stack_t uc_stack;
738 mcontext_t uc_mcontext;
739 __sigset_t uc_sigmask;
740} ucontext_t;
741enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
742
743#endif
744
745
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000746static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000747#ifndef V8_HOST_ARCH_MIPS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000748 USE(info);
749 if (signal != SIGPROF) return;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000750 if (active_sampler_ == NULL) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000751
lrn@chromium.org25156de2010-04-06 13:10:27 +0000752 TickSample sample_obj;
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000753 TickSample* sample = CpuProfiler::TickSampleEvent();
ager@chromium.org357bf652010-04-12 11:30:10 +0000754 if (sample == NULL) sample = &sample_obj;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000755
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000756 // We always sample the VM state.
ager@chromium.org357bf652010-04-12 11:30:10 +0000757 sample->state = VMState::current_state();
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000758
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000759 // If profiling, we extract the current pc and sp.
760 if (active_sampler_->IsProfiling()) {
761 // Extracting the sample from the context is extremely machine dependent.
762 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
763 mcontext_t& mcontext = ucontext->uc_mcontext;
ager@chromium.org9085a012009-05-11 19:22:57 +0000764#if V8_HOST_ARCH_IA32
ager@chromium.org357bf652010-04-12 11:30:10 +0000765 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
766 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
767 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
ager@chromium.org9085a012009-05-11 19:22:57 +0000768#elif V8_HOST_ARCH_X64
ager@chromium.org357bf652010-04-12 11:30:10 +0000769 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
770 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
771 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
ager@chromium.org9085a012009-05-11 19:22:57 +0000772#elif V8_HOST_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000773// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
774#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
ager@chromium.org357bf652010-04-12 11:30:10 +0000775 sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
776 sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
777 sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000778#else
ager@chromium.org357bf652010-04-12 11:30:10 +0000779 sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
780 sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
781 sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000782#endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000783#elif V8_HOST_ARCH_MIPS
784 // Implement this on MIPS.
ager@chromium.org357bf652010-04-12 11:30:10 +0000785 UNIMPLEMENTED();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000786#endif
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000787 active_sampler_->SampleStack(sample);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000788 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000789
lrn@chromium.org25156de2010-04-06 13:10:27 +0000790 active_sampler_->Tick(sample);
791#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000792}
793
794
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000795class Sampler::PlatformData : public Malloced {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000796 public:
lrn@chromium.org303ada72010-10-27 09:33:13 +0000797 explicit PlatformData(Sampler* sampler)
798 : sampler_(sampler),
799 signal_handler_installed_(false),
800 vm_tgid_(getpid()),
801 // Glibc doesn't provide a wrapper for gettid(2).
802 vm_tid_(syscall(SYS_gettid)),
803 signal_sender_launched_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000804 }
805
lrn@chromium.org303ada72010-10-27 09:33:13 +0000806 void SignalSender() {
807 while (sampler_->IsActive()) {
808 // Glibc doesn't provide a wrapper for tgkill(2).
809 syscall(SYS_tgkill, vm_tgid_, vm_tid_, SIGPROF);
810 // Convert ms to us and subtract 100 us to compensate delays
811 // occuring during signal delivery.
812 int result = usleep(sampler_->interval_ * 1000 - 100);
813 ASSERT(result == 0 || errno == EINTR);
814 USE(result);
815 }
816 }
817
818 Sampler* sampler_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000819 bool signal_handler_installed_;
820 struct sigaction old_signal_handler_;
lrn@chromium.org303ada72010-10-27 09:33:13 +0000821 int vm_tgid_;
822 int vm_tid_;
823 bool signal_sender_launched_;
824 pthread_t signal_sender_thread_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000825};
826
827
lrn@chromium.org303ada72010-10-27 09:33:13 +0000828static void* SenderEntry(void* arg) {
829 Sampler::PlatformData* data =
830 reinterpret_cast<Sampler::PlatformData*>(arg);
831 data->SignalSender();
832 return 0;
833}
834
835
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000836Sampler::Sampler(int interval, bool profiling)
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000837 : interval_(interval),
838 profiling_(profiling),
839 synchronous_(profiling),
840 active_(false) {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000841 data_ = new PlatformData(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000842}
843
844
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000845Sampler::~Sampler() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000846 delete data_;
847}
848
849
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000850void Sampler::Start() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000851 // There can only be one active sampler at the time on POSIX
852 // platforms.
853 if (active_sampler_ != NULL) return;
854
855 // Request profiling signals.
856 struct sigaction sa;
857 sa.sa_sigaction = ProfilerSignalHandler;
858 sigemptyset(&sa.sa_mask);
859 sa.sa_flags = SA_SIGINFO;
860 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
861 data_->signal_handler_installed_ = true;
862
lrn@chromium.org303ada72010-10-27 09:33:13 +0000863 // Start a thread that sends SIGPROF signal to VM thread.
864 // Sending the signal ourselves instead of relying on itimer provides
865 // much better accuracy.
866 active_ = true;
867 if (pthread_create(
868 &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
869 data_->signal_sender_launched_ = true;
870 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000871
872 // Set this sampler as the active sampler.
873 active_sampler_ = this;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000874}
875
876
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000877void Sampler::Stop() {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000878 active_ = false;
879
880 // Wait for signal sender termination (it will exit after setting
881 // active_ to false).
882 if (data_->signal_sender_launched_) {
883 pthread_join(data_->signal_sender_thread_, NULL);
884 data_->signal_sender_launched_ = false;
885 }
886
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000887 // Restore old signal handler
888 if (data_->signal_handler_installed_) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000889 sigaction(SIGPROF, &data_->old_signal_handler_, 0);
890 data_->signal_handler_installed_ = false;
891 }
892
893 // This sampler is no longer the active sampler.
894 active_sampler_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000895}
896
ager@chromium.orga1645e22009-09-09 19:27:10 +0000897
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000898#endif // ENABLE_LOGGING_AND_PROFILING
899
900} } // namespace v8::internal