blob: 0da1c08fdd5cb0f63246e69e6b034acdb10348ea [file] [log] [blame]
ulan@chromium.org2efb9002012-01-19 15:36:35 +00001// Copyright 2012 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>
lrn@chromium.org5d00b602011-01-05 09:51:43 +000034#include <sys/prctl.h>
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000035#include <sys/time.h>
36#include <sys/resource.h>
lrn@chromium.org303ada72010-10-27 09:33:13 +000037#include <sys/syscall.h>
ager@chromium.org381abbb2009-02-25 13:23:22 +000038#include <sys/types.h>
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000039#include <stdlib.h>
40
41// Ubuntu Dapper requires memory pages to be marked as
42// executable. Otherwise, OS raises an exception when executing code
43// in that page.
44#include <sys/types.h> // mmap & munmap
ager@chromium.org236ad962008-09-25 09:45:57 +000045#include <sys/mman.h> // mmap & munmap
46#include <sys/stat.h> // open
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000047#include <fcntl.h> // open
48#include <unistd.h> // sysconf
49#ifdef __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000050#include <execinfo.h> // backtrace, backtrace_symbols
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000051#endif // def __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000052#include <strings.h> // index
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053#include <errno.h>
54#include <stdarg.h>
55
56#undef MAP_TYPE
57
58#include "v8.h"
59
60#include "platform.h"
ager@chromium.orga1645e22009-09-09 19:27:10 +000061#include "v8threads.h"
kasperl@chromium.orga5551262010-12-07 12:49:48 +000062#include "vm-state-inl.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000063
64
kasperl@chromium.org71affb52009-05-26 05:44:31 +000065namespace v8 {
66namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000067
68// 0 is never a valid thread id on Linux since tids and pids share a
69// name space and pid 0 is reserved (see man 2 kill).
70static const pthread_t kNoThread = (pthread_t) 0;
71
72
73double ceiling(double x) {
74 return ceil(x);
75}
76
77
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000078static Mutex* limit_mutex = NULL;
79
80
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +000081void OS::SetUp() {
ricow@chromium.org9fa09672011-07-25 11:05:35 +000082 // Seed the random number generator. We preserve microsecond resolution.
83 uint64_t seed = Ticks() ^ (getpid() << 16);
ager@chromium.org9258b6b2008-09-11 09:11:10 +000084 srandom(static_cast<unsigned int>(seed));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000085 limit_mutex = CreateMutex();
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +000086
87#ifdef __arm__
88 // When running on ARM hardware check that the EABI used by V8 and
89 // by the C code is the same.
90 bool hard_float = OS::ArmUsingHardFloat();
91 if (hard_float) {
92#if !USE_EABI_HARDFLOAT
93 PrintF("ERROR: Binary compiled with -mfloat-abi=hard but without "
94 "-DUSE_EABI_HARDFLOAT\n");
95 exit(1);
96#endif
97 } else {
98#if USE_EABI_HARDFLOAT
99 PrintF("ERROR: Binary not compiled with -mfloat-abi=hard but with "
100 "-DUSE_EABI_HARDFLOAT\n");
101 exit(1);
102#endif
103 }
104#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000105}
106
107
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000108uint64_t OS::CpuFeaturesImpliedByPlatform() {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000109 return 0; // Linux runs on anything.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000110}
111
112
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000113#ifdef __arm__
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000114static bool CPUInfoContainsString(const char * search_string) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000115 const char* file_name = "/proc/cpuinfo";
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000116 // This is written as a straight shot one pass parser
117 // and not using STL string and ifstream because,
118 // on Linux, it's reading from a (non-mmap-able)
119 // character special device.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000120 FILE* f = NULL;
121 const char* what = search_string;
122
123 if (NULL == (f = fopen(file_name, "r")))
124 return false;
125
126 int k;
127 while (EOF != (k = fgetc(f))) {
128 if (k == *what) {
129 ++what;
130 while ((*what != '\0') && (*what == fgetc(f))) {
131 ++what;
132 }
133 if (*what == '\0') {
134 fclose(f);
135 return true;
136 } else {
137 what = search_string;
138 }
139 }
140 }
141 fclose(f);
142
143 // Did not find string in the proc file.
144 return false;
145}
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000146
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000147
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000148bool OS::ArmCpuHasFeature(CpuFeature feature) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000149 const char* search_string = NULL;
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000150 // Simple detection of VFP at runtime for Linux.
151 // It is based on /proc/cpuinfo, which reveals hardware configuration
152 // to user-space applications. According to ARM (mid 2009), no similar
153 // facility is universally available on the ARM architectures,
154 // so it's up to individual OSes to provide such.
155 switch (feature) {
156 case VFP3:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000157 search_string = "vfpv3";
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000158 break;
159 case ARMv7:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000160 search_string = "ARMv7";
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000161 break;
162 default:
163 UNREACHABLE();
164 }
165
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000166 if (CPUInfoContainsString(search_string)) {
167 return true;
168 }
169
170 if (feature == VFP3) {
171 // Some old kernels will report vfp not vfpv3. Here we make a last attempt
172 // to detect vfpv3 by checking for vfp *and* neon, since neon is only
173 // available on architectures with vfpv3.
174 // Checking neon on its own is not enough as it is possible to have neon
175 // without vfp.
176 if (CPUInfoContainsString("vfp") && CPUInfoContainsString("neon")) {
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000177 return true;
178 }
179 }
180
181 return false;
182}
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000183
184
185// Simple helper function to detect whether the C code is compiled with
186// option -mfloat-abi=hard. The register d0 is loaded with 1.0 and the register
187// pair r0, r1 is loaded with 0.0. If -mfloat-abi=hard is pased to GCC then
188// calling this will return 1.0 and otherwise 0.0.
189static void ArmUsingHardFloatHelper() {
190 asm("mov r0, #0");
191#if defined(__VFP_FP__) && !defined(__SOFTFP__)
192 // Load 0x3ff00000 into r1 using instructions available in both ARM
193 // and Thumb mode.
194 asm("mov r1, #3");
195 asm("mov r2, #255");
196 asm("lsl r1, r1, #8");
197 asm("orr r1, r1, r2");
lrn@chromium.org1c092762011-05-09 09:42:16 +0000198 asm("lsl r1, r1, #20");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000199 // For vmov d0, r0, r1 use ARM mode.
200#ifdef __thumb__
201 asm volatile(
202 "@ Enter ARM Mode \n\t"
203 " adr r3, 1f \n\t"
204 " bx r3 \n\t"
205 " .ALIGN 4 \n\t"
206 " .ARM \n"
207 "1: vmov d0, r0, r1 \n\t"
208 "@ Enter THUMB Mode\n\t"
209 " adr r3, 2f+1 \n\t"
210 " bx r3 \n\t"
211 " .THUMB \n"
212 "2: \n\t");
213#else
214 asm("vmov d0, r0, r1");
215#endif // __thumb__
216#endif // defined(__VFP_FP__) && !defined(__SOFTFP__)
217 asm("mov r1, #0");
218}
219
220
221bool OS::ArmUsingHardFloat() {
222 // Cast helper function from returning void to returning double.
223 typedef double (*F)();
224 F f = FUNCTION_CAST<F>(FUNCTION_ADDR(ArmUsingHardFloatHelper));
225 return f() == 1.0;
226}
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000227#endif // def __arm__
228
229
lrn@chromium.org7516f052011-03-30 08:52:27 +0000230#ifdef __mips__
231bool OS::MipsCpuHasFeature(CpuFeature feature) {
232 const char* search_string = NULL;
233 const char* file_name = "/proc/cpuinfo";
234 // Simple detection of FPU at runtime for Linux.
235 // It is based on /proc/cpuinfo, which reveals hardware configuration
236 // to user-space applications. According to MIPS (early 2010), no similar
237 // facility is universally available on the MIPS architectures,
238 // so it's up to individual OSes to provide such.
239 //
240 // This is written as a straight shot one pass parser
241 // and not using STL string and ifstream because,
242 // on Linux, it's reading from a (non-mmap-able)
243 // character special device.
244
245 switch (feature) {
246 case FPU:
247 search_string = "FPU";
248 break;
249 default:
250 UNREACHABLE();
251 }
252
253 FILE* f = NULL;
254 const char* what = search_string;
255
256 if (NULL == (f = fopen(file_name, "r")))
257 return false;
258
259 int k;
260 while (EOF != (k = fgetc(f))) {
261 if (k == *what) {
262 ++what;
263 while ((*what != '\0') && (*what == fgetc(f))) {
264 ++what;
265 }
266 if (*what == '\0') {
267 fclose(f);
268 return true;
269 } else {
270 what = search_string;
271 }
272 }
273 }
274 fclose(f);
275
276 // Did not find string in the proc file.
277 return false;
278}
279#endif // def __mips__
280
281
ager@chromium.org236ad962008-09-25 09:45:57 +0000282int OS::ActivationFrameAlignment() {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000283#ifdef V8_TARGET_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000284 // On EABI ARM targets this is required for fp correctness in the
285 // runtime system.
ager@chromium.org3a6061e2009-03-12 14:24:36 +0000286 return 8;
ager@chromium.org5c838252010-02-19 08:53:10 +0000287#elif V8_TARGET_ARCH_MIPS
288 return 8;
289#endif
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000290 // With gcc 4.4 the tree vectorization optimizer can generate code
ager@chromium.orge2902be2009-06-08 12:21:35 +0000291 // that requires 16 byte alignment such as movdqa on x86.
292 return 16;
ager@chromium.org236ad962008-09-25 09:45:57 +0000293}
294
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000295
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000296void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000297#if (defined(V8_TARGET_ARCH_ARM) && defined(__arm__)) || \
298 (defined(V8_TARGET_ARCH_MIPS) && defined(__mips__))
299 // Only use on ARM or MIPS hardware.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000300 MemoryBarrier();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000301#else
302 __asm__ __volatile__("" : : : "memory");
303 // An x86 store acts as a release barrier.
304#endif
305 *ptr = value;
306}
307
308
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000309const char* OS::LocalTimezone(double time) {
310 if (isnan(time)) return "";
311 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
312 struct tm* t = localtime(&tv);
313 if (NULL == t) return "";
314 return t->tm_zone;
315}
316
317
318double OS::LocalTimeOffset() {
319 time_t tv = time(NULL);
320 struct tm* t = localtime(&tv);
321 // tm_gmtoff includes any daylight savings offset, so subtract it.
322 return static_cast<double>(t->tm_gmtoff * msPerSecond -
323 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
324}
325
326
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000327// We keep the lowest and highest addresses mapped as a quick way of
328// determining that pointers are outside the heap (used mostly in assertions
ulan@chromium.org2efb9002012-01-19 15:36:35 +0000329// and verification). The estimate is conservative, i.e., not all addresses in
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000330// 'allocated' space are actually allocated to our heap. The range is
331// [lowest, highest), inclusive on the low and and exclusive on the high end.
332static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
333static void* highest_ever_allocated = reinterpret_cast<void*>(0);
334
335
336static void UpdateAllocatedSpaceLimits(void* address, int size) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000337 ASSERT(limit_mutex != NULL);
338 ScopedLock lock(limit_mutex);
339
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000340 lowest_ever_allocated = Min(lowest_ever_allocated, address);
341 highest_ever_allocated =
342 Max(highest_ever_allocated,
343 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
344}
345
346
347bool OS::IsOutsideAllocatedSpace(void* address) {
348 return address < lowest_ever_allocated || address >= highest_ever_allocated;
349}
350
351
352size_t OS::AllocateAlignment() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000353 return sysconf(_SC_PAGESIZE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000354}
355
356
kasper.lund7276f142008-07-30 08:49:36 +0000357void* OS::Allocate(const size_t requested,
358 size_t* allocated,
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000359 bool is_executable) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000360 const size_t msize = RoundUp(requested, AllocateAlignment());
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000361 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +0000362 void* addr = OS::GetRandomMmapAddr();
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000363 void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000364 if (mbase == MAP_FAILED) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000365 LOG(i::Isolate::Current(),
366 StringEvent("OS::Allocate", "mmap failed"));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000367 return NULL;
368 }
369 *allocated = msize;
370 UpdateAllocatedSpaceLimits(mbase, msize);
371 return mbase;
372}
373
374
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000375void OS::Free(void* address, const size_t size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000376 // TODO(1240712): munmap has a return value which is ignored here.
ager@chromium.orga1645e22009-09-09 19:27:10 +0000377 int result = munmap(address, size);
378 USE(result);
379 ASSERT(result == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000380}
381
382
383void OS::Sleep(int milliseconds) {
384 unsigned int ms = static_cast<unsigned int>(milliseconds);
385 usleep(1000 * ms);
386}
387
388
389void OS::Abort() {
390 // Redirect to std abort to signal abnormal program termination.
391 abort();
392}
393
394
kasper.lund7276f142008-07-30 08:49:36 +0000395void OS::DebugBreak() {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000396// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
397// which is the architecture of generated code).
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000398#if (defined(__arm__) || defined(__thumb__))
399# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
kasper.lund7276f142008-07-30 08:49:36 +0000400 asm("bkpt 0");
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000401# endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000402#elif defined(__mips__)
403 asm("break");
kasper.lund7276f142008-07-30 08:49:36 +0000404#else
405 asm("int $3");
406#endif
407}
408
409
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000410class PosixMemoryMappedFile : public OS::MemoryMappedFile {
411 public:
412 PosixMemoryMappedFile(FILE* file, void* memory, int size)
413 : file_(file), memory_(memory), size_(size) { }
414 virtual ~PosixMemoryMappedFile();
415 virtual void* memory() { return memory_; }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000416 virtual int size() { return size_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000417 private:
418 FILE* file_;
419 void* memory_;
420 int size_;
421};
422
423
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000424OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000425 FILE* file = fopen(name, "r+");
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000426 if (file == NULL) return NULL;
427
428 fseek(file, 0, SEEK_END);
429 int size = ftell(file);
430
431 void* memory =
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +0000432 mmap(OS::GetRandomMmapAddr(),
433 size,
434 PROT_READ | PROT_WRITE,
435 MAP_SHARED,
436 fileno(file),
437 0);
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000438 return new PosixMemoryMappedFile(file, memory, size);
439}
440
441
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000442OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
443 void* initial) {
444 FILE* file = fopen(name, "w+");
445 if (file == NULL) return NULL;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000446 int result = fwrite(initial, size, 1, file);
447 if (result < 1) {
448 fclose(file);
449 return NULL;
450 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000451 void* memory =
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +0000452 mmap(OS::GetRandomMmapAddr(),
453 size,
454 PROT_READ | PROT_WRITE,
455 MAP_SHARED,
456 fileno(file),
457 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000458 return new PosixMemoryMappedFile(file, memory, size);
459}
460
461
462PosixMemoryMappedFile::~PosixMemoryMappedFile() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000463 if (memory_) OS::Free(memory_, size_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000464 fclose(file_);
465}
466
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000467
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000468void OS::LogSharedLibraryAddresses() {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000469 // This function assumes that the layout of the file is as follows:
470 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
471 // If we encounter an unexpected situation we abort scanning further entries.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000472 FILE* fp = fopen("/proc/self/maps", "r");
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000473 if (fp == NULL) return;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000474
475 // Allocate enough room to be able to store a full file name.
476 const int kLibNameLen = FILENAME_MAX + 1;
477 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
478
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000479 i::Isolate* isolate = ISOLATE;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000480 // This loop will terminate once the scanning hits an EOF.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000481 while (true) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000482 uintptr_t start, end;
483 char attr_r, attr_w, attr_x, attr_p;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000484 // Parse the addresses and permission bits at the beginning of the line.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000485 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
486 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 +0000487
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000488 int c;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000489 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
490 // Found a read-only executable entry. Skip characters until we reach
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000491 // the beginning of the filename or the end of the line.
492 do {
493 c = getc(fp);
494 } while ((c != EOF) && (c != '\n') && (c != '/'));
495 if (c == EOF) break; // EOF: Was unexpected, just exit.
496
497 // Process the filename if found.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000498 if (c == '/') {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000499 ungetc(c, fp); // Push the '/' back into the stream to be read below.
500
501 // Read to the end of the line. Exit if the read fails.
502 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
503
504 // Drop the newline character read by fgets. We do not need to check
505 // for a zero-length string because we know that we at least read the
506 // '/' character.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000507 lib_name[strlen(lib_name) - 1] = '\0';
508 } else {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000509 // No library name found, just record the raw address range.
510 snprintf(lib_name, kLibNameLen,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000511 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
512 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000513 LOG(isolate, SharedLibraryEvent(lib_name, start, end));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000514 } else {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000515 // Entry not describing executable data. Skip to end of line to set up
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000516 // reading the next entry.
517 do {
518 c = getc(fp);
519 } while ((c != EOF) && (c != '\n'));
520 if (c == EOF) break;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000521 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000522 }
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000523 free(lib_name);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000524 fclose(fp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525}
526
527
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000528static const char kGCFakeMmap[] = "/tmp/__v8_gc__";
529
530
531void OS::SignalCodeMovingGC() {
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000532 // Support for ll_prof.py.
533 //
534 // The Linux profiler built into the kernel logs all mmap's with
535 // PROT_EXEC so that analysis tools can properly attribute ticks. We
536 // do a mmap with a name known by ll_prof.py and immediately munmap
537 // it. This injects a GC marker into the stream of events generated
538 // by the kernel and allows us to synchronize V8 code log and the
539 // kernel log.
540 int size = sysconf(_SC_PAGESIZE);
541 FILE* f = fopen(kGCFakeMmap, "w+");
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +0000542 void* addr = mmap(OS::GetRandomMmapAddr(),
543 size,
544 PROT_READ | PROT_EXEC,
545 MAP_PRIVATE,
546 fileno(f),
547 0);
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000548 ASSERT(addr != MAP_FAILED);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000549 OS::Free(addr, size);
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000550 fclose(f);
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000551}
552
553
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000554int OS::StackWalk(Vector<OS::StackFrame> frames) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000555 // backtrace is a glibc extension.
556#ifdef __GLIBC__
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000557 int frames_size = frames.length();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000558 ScopedVector<void*> addresses(frames_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000559
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000560 int frames_count = backtrace(addresses.start(), frames_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000562 char** symbols = backtrace_symbols(addresses.start(), frames_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000563 if (symbols == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000564 return kStackWalkError;
565 }
566
567 for (int i = 0; i < frames_count; i++) {
568 frames[i].address = addresses[i];
569 // Format a text representation of the frame based on the information
570 // available.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000571 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
572 "%s",
573 symbols[i]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574 // Make sure line termination is in place.
575 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
576 }
577
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578 free(symbols);
579
580 return frames_count;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000581#else // ndef __GLIBC__
582 return 0;
583#endif // ndef __GLIBC__
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584}
585
586
587// Constants used for mmap.
588static const int kMmapFd = -1;
589static const int kMmapFdOffset = 0;
590
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000591VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000592
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000593VirtualMemory::VirtualMemory(size_t size) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000594 address_ = ReserveRegion(size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000595 size_ = size;
596}
597
598
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000599VirtualMemory::VirtualMemory(size_t size, size_t alignment)
600 : address_(NULL), size_(0) {
601 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
602 size_t request_size = RoundUp(size + alignment,
603 static_cast<intptr_t>(OS::AllocateAlignment()));
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +0000604 void* reservation = mmap(OS::GetRandomMmapAddr(),
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000605 request_size,
606 PROT_NONE,
607 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
608 kMmapFd,
609 kMmapFdOffset);
610 if (reservation == MAP_FAILED) return;
611
612 Address base = static_cast<Address>(reservation);
613 Address aligned_base = RoundUp(base, alignment);
614 ASSERT_LE(base, aligned_base);
615
616 // Unmap extra memory reserved before and after the desired block.
617 if (aligned_base != base) {
618 size_t prefix_size = static_cast<size_t>(aligned_base - base);
619 OS::Free(base, prefix_size);
620 request_size -= prefix_size;
621 }
622
623 size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
624 ASSERT_LE(aligned_size, request_size);
625
626 if (aligned_size != request_size) {
627 size_t suffix_size = request_size - aligned_size;
628 OS::Free(aligned_base + aligned_size, suffix_size);
629 request_size -= suffix_size;
630 }
631
632 ASSERT(aligned_size == request_size);
633
634 address_ = static_cast<void*>(aligned_base);
635 size_ = aligned_size;
636}
637
638
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000639VirtualMemory::~VirtualMemory() {
640 if (IsReserved()) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000641 bool result = ReleaseRegion(address(), size());
642 ASSERT(result);
643 USE(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000644 }
645}
646
647
648bool VirtualMemory::IsReserved() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000649 return address_ != NULL;
650}
651
652
653void VirtualMemory::Reset() {
654 address_ = NULL;
655 size_ = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000656}
657
658
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000659bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000660 return CommitRegion(address, size, is_executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000661}
662
663
664bool VirtualMemory::Uncommit(void* address, size_t size) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000665 return UncommitRegion(address, size);
666}
667
668
yangguo@chromium.orgab30bb82012-02-24 14:41:46 +0000669bool VirtualMemory::Guard(void* address) {
670 OS::Guard(address, OS::CommitPageSize());
671 return true;
672}
673
674
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000675void* VirtualMemory::ReserveRegion(size_t size) {
rossberg@chromium.orgb4b2aa62011-10-13 09:49:59 +0000676 void* result = mmap(OS::GetRandomMmapAddr(),
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000677 size,
678 PROT_NONE,
679 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
680 kMmapFd,
681 kMmapFdOffset);
682
683 if (result == MAP_FAILED) return NULL;
684
685 return result;
686}
687
688
689bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
690 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
691 if (MAP_FAILED == mmap(base,
692 size,
693 prot,
694 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
695 kMmapFd,
696 kMmapFdOffset)) {
697 return false;
698 }
699
700 UpdateAllocatedSpaceLimits(base, size);
701 return true;
702}
703
704
705bool VirtualMemory::UncommitRegion(void* base, size_t size) {
706 return mmap(base,
707 size,
708 PROT_NONE,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000709 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000710 kMmapFd,
711 kMmapFdOffset) != MAP_FAILED;
712}
713
714
715bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
716 return munmap(base, size) == 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000717}
718
719
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000720class Thread::PlatformData : public Malloced {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000721 public:
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000722 PlatformData() : thread_(kNoThread) {}
ager@chromium.org41826e72009-03-30 13:30:57 +0000723
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724 pthread_t thread_; // Thread handle for pthread.
725};
726
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000727Thread::Thread(const Options& options)
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000728 : data_(new PlatformData()),
yangguo@chromium.org659ceec2012-01-26 07:37:54 +0000729 stack_size_(options.stack_size()) {
730 set_name(options.name());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000731}
732
733
734Thread::~Thread() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000735 delete data_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000736}
737
738
739static void* ThreadEntry(void* arg) {
740 Thread* thread = reinterpret_cast<Thread*>(arg);
741 // This is also initialized by the first argument to pthread_create() but we
742 // don't know which thread will run first (the original thread or the new
743 // one) so we initialize it here too.
danno@chromium.orgb6451162011-08-17 14:33:23 +0000744#ifdef PR_SET_NAME
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000745 prctl(PR_SET_NAME,
746 reinterpret_cast<unsigned long>(thread->name()), // NOLINT
747 0, 0, 0);
danno@chromium.orgb6451162011-08-17 14:33:23 +0000748#endif
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000749 thread->data()->thread_ = pthread_self();
750 ASSERT(thread->data()->thread_ != kNoThread);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000751 thread->Run();
752 return NULL;
753}
754
755
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000756void Thread::set_name(const char* name) {
757 strncpy(name_, name, sizeof(name_));
758 name_[sizeof(name_) - 1] = '\0';
759}
760
761
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000762void Thread::Start() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000763 pthread_attr_t* attr_ptr = NULL;
764 pthread_attr_t attr;
765 if (stack_size_ > 0) {
766 pthread_attr_init(&attr);
767 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
768 attr_ptr = &attr;
769 }
danno@chromium.orgc612e022011-11-10 11:38:15 +0000770 int result = pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
771 CHECK_EQ(0, result);
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000772 ASSERT(data_->thread_ != kNoThread);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000773}
774
775
776void Thread::Join() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000777 pthread_join(data_->thread_, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000778}
779
780
781Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
782 pthread_key_t key;
783 int result = pthread_key_create(&key, NULL);
784 USE(result);
785 ASSERT(result == 0);
786 return static_cast<LocalStorageKey>(key);
787}
788
789
790void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
791 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
792 int result = pthread_key_delete(pthread_key);
793 USE(result);
794 ASSERT(result == 0);
795}
796
797
798void* Thread::GetThreadLocal(LocalStorageKey key) {
799 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
800 return pthread_getspecific(pthread_key);
801}
802
803
804void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
805 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
806 pthread_setspecific(pthread_key, value);
807}
808
809
810void Thread::YieldCPU() {
811 sched_yield();
812}
813
814
815class LinuxMutex : public Mutex {
816 public:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000817 LinuxMutex() {
818 pthread_mutexattr_t attrs;
819 int result = pthread_mutexattr_init(&attrs);
820 ASSERT(result == 0);
821 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
822 ASSERT(result == 0);
823 result = pthread_mutex_init(&mutex_, &attrs);
824 ASSERT(result == 0);
rossberg@chromium.org717967f2011-07-20 13:44:42 +0000825 USE(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000826 }
827
828 virtual ~LinuxMutex() { pthread_mutex_destroy(&mutex_); }
829
830 virtual int Lock() {
831 int result = pthread_mutex_lock(&mutex_);
832 return result;
833 }
834
835 virtual int Unlock() {
836 int result = pthread_mutex_unlock(&mutex_);
837 return result;
838 }
839
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000840 virtual bool TryLock() {
841 int result = pthread_mutex_trylock(&mutex_);
842 // Return false if the lock is busy and locking failed.
843 if (result == EBUSY) {
844 return false;
845 }
846 ASSERT(result == 0); // Verify no other errors.
847 return true;
848 }
849
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000850 private:
851 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
852};
853
854
855Mutex* OS::CreateMutex() {
856 return new LinuxMutex();
857}
858
859
860class LinuxSemaphore : public Semaphore {
861 public:
862 explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); }
863 virtual ~LinuxSemaphore() { sem_destroy(&sem_); }
864
kasper.lund7276f142008-07-30 08:49:36 +0000865 virtual void Wait();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000866 virtual bool Wait(int timeout);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000867 virtual void Signal() { sem_post(&sem_); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000868 private:
869 sem_t sem_;
870};
871
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000872
kasper.lund7276f142008-07-30 08:49:36 +0000873void LinuxSemaphore::Wait() {
874 while (true) {
875 int result = sem_wait(&sem_);
876 if (result == 0) return; // Successfully got semaphore.
877 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
878 }
879}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000880
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000881
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000882#ifndef TIMEVAL_TO_TIMESPEC
883#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
884 (ts)->tv_sec = (tv)->tv_sec; \
885 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
886} while (false)
887#endif
888
889
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000890bool LinuxSemaphore::Wait(int timeout) {
891 const long kOneSecondMicros = 1000000; // NOLINT
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000892
893 // Split timeout into second and nanosecond parts.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000894 struct timeval delta;
895 delta.tv_usec = timeout % kOneSecondMicros;
896 delta.tv_sec = timeout / kOneSecondMicros;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000897
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000898 struct timeval current_time;
899 // Get the current time.
900 if (gettimeofday(&current_time, NULL) == -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000901 return false;
902 }
903
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000904 // Calculate time for end of timeout.
905 struct timeval end_time;
906 timeradd(&current_time, &delta, &end_time);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000907
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000908 struct timespec ts;
909 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000910 // Wait for semaphore signalled or timeout.
911 while (true) {
912 int result = sem_timedwait(&sem_, &ts);
913 if (result == 0) return true; // Successfully got semaphore.
914 if (result > 0) {
915 // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
916 errno = result;
917 result = -1;
918 }
919 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
920 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
921 }
922}
923
924
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000925Semaphore* OS::CreateSemaphore(int count) {
926 return new LinuxSemaphore(count);
927}
928
ager@chromium.org381abbb2009-02-25 13:23:22 +0000929
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000930#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
931// Android runs a fairly new Linux kernel, so signal info is there,
932// but the C library doesn't have the structs defined.
933
934struct sigcontext {
935 uint32_t trap_no;
936 uint32_t error_code;
937 uint32_t oldmask;
938 uint32_t gregs[16];
939 uint32_t arm_cpsr;
940 uint32_t fault_address;
941};
942typedef uint32_t __sigset_t;
943typedef struct sigcontext mcontext_t;
944typedef struct ucontext {
945 uint32_t uc_flags;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000946 struct ucontext* uc_link;
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000947 stack_t uc_stack;
948 mcontext_t uc_mcontext;
949 __sigset_t uc_sigmask;
950} ucontext_t;
951enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
952
danno@chromium.orgfa458e42012-02-01 10:48:36 +0000953#elif !defined(__GLIBC__) && defined(__mips__)
954// MIPS version of sigcontext, for Android bionic.
955struct sigcontext {
956 uint32_t regmask;
957 uint32_t status;
958 uint64_t pc;
959 uint64_t gregs[32];
960 uint64_t fpregs[32];
961 uint32_t acx;
962 uint32_t fpc_csr;
963 uint32_t fpc_eir;
964 uint32_t used_math;
965 uint32_t dsp;
966 uint64_t mdhi;
967 uint64_t mdlo;
968 uint32_t hi1;
969 uint32_t lo1;
970 uint32_t hi2;
971 uint32_t lo2;
972 uint32_t hi3;
973 uint32_t lo3;
974};
975typedef uint32_t __sigset_t;
976typedef struct sigcontext mcontext_t;
977typedef struct ucontext {
978 uint32_t uc_flags;
979 struct ucontext* uc_link;
980 stack_t uc_stack;
981 mcontext_t uc_mcontext;
982 __sigset_t uc_sigmask;
983} ucontext_t;
984
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000985#endif
986
987
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000988static int GetThreadID() {
989 // Glibc doesn't provide a wrapper for gettid(2).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000990#if defined(ANDROID)
991 return syscall(__NR_gettid);
992#else
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000993 return syscall(SYS_gettid);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000994#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000995}
996
997
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000998static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
999 USE(info);
1000 if (signal != SIGPROF) return;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001001 Isolate* isolate = Isolate::UncheckedCurrent();
1002 if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
1003 // We require a fully initialized and entered isolate.
1004 return;
1005 }
vitalyr@chromium.org0ec56d62011-04-15 22:22:08 +00001006 if (v8::Locker::IsActive() &&
1007 !isolate->thread_manager()->IsLockedByCurrentThread()) {
1008 return;
1009 }
1010
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001011 Sampler* sampler = isolate->logger()->sampler();
1012 if (sampler == NULL || !sampler->IsActive()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001013
lrn@chromium.org25156de2010-04-06 13:10:27 +00001014 TickSample sample_obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001015 TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
ager@chromium.org357bf652010-04-12 11:30:10 +00001016 if (sample == NULL) sample = &sample_obj;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001017
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001018 // Extracting the sample from the context is extremely machine dependent.
1019 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
1020 mcontext_t& mcontext = ucontext->uc_mcontext;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001021 sample->state = isolate->current_vm_state();
ager@chromium.org9085a012009-05-11 19:22:57 +00001022#if V8_HOST_ARCH_IA32
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001023 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
1024 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
1025 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
ager@chromium.org9085a012009-05-11 19:22:57 +00001026#elif V8_HOST_ARCH_X64
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001027 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
1028 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
1029 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
ager@chromium.org9085a012009-05-11 19:22:57 +00001030#elif V8_HOST_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001031// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
1032#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001033 sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
1034 sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
1035 sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001036#else
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001037 sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
1038 sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
1039 sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
danno@chromium.orgc612e022011-11-10 11:38:15 +00001040#endif // (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
ager@chromium.org5c838252010-02-19 08:53:10 +00001041#elif V8_HOST_ARCH_MIPS
danno@chromium.orgc612e022011-11-10 11:38:15 +00001042 sample->pc = reinterpret_cast<Address>(mcontext.pc);
1043 sample->sp = reinterpret_cast<Address>(mcontext.gregs[29]);
1044 sample->fp = reinterpret_cast<Address>(mcontext.gregs[30]);
1045#endif // V8_HOST_ARCH_*
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001046 sampler->SampleStack(sample);
1047 sampler->Tick(sample);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001048}
1049
1050
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001051class Sampler::PlatformData : public Malloced {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001052 public:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001053 PlatformData() : vm_tid_(GetThreadID()) {}
1054
1055 int vm_tid() const { return vm_tid_; }
1056
1057 private:
1058 const int vm_tid_;
1059};
1060
1061
1062class SignalSender : public Thread {
1063 public:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001064 enum SleepInterval {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001065 HALF_INTERVAL,
1066 FULL_INTERVAL
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001067 };
1068
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00001069 static const int kSignalSenderStackSize = 64 * KB;
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00001070
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001071 explicit SignalSender(int interval)
yangguo@chromium.org659ceec2012-01-26 07:37:54 +00001072 : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
lrn@chromium.org303ada72010-10-27 09:33:13 +00001073 vm_tgid_(getpid()),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001074 interval_(interval) {}
1075
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001076 static void InstallSignalHandler() {
1077 struct sigaction sa;
1078 sa.sa_sigaction = ProfilerSignalHandler;
1079 sigemptyset(&sa.sa_mask);
1080 sa.sa_flags = SA_RESTART | SA_SIGINFO;
1081 signal_handler_installed_ =
1082 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
1083 }
1084
1085 static void RestoreSignalHandler() {
1086 if (signal_handler_installed_) {
1087 sigaction(SIGPROF, &old_signal_handler_, 0);
1088 signal_handler_installed_ = false;
1089 }
1090 }
1091
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001092 static void AddActiveSampler(Sampler* sampler) {
1093 ScopedLock lock(mutex_);
1094 SamplerRegistry::AddActiveSampler(sampler);
1095 if (instance_ == NULL) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001096 // Start a thread that will send SIGPROF signal to VM threads,
1097 // when CPU profiling will be enabled.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001098 instance_ = new SignalSender(sampler->interval());
1099 instance_->Start();
1100 } else {
1101 ASSERT(instance_->interval_ == sampler->interval());
1102 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001103 }
1104
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001105 static void RemoveActiveSampler(Sampler* sampler) {
1106 ScopedLock lock(mutex_);
1107 SamplerRegistry::RemoveActiveSampler(sampler);
1108 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00001109 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001110 delete instance_;
1111 instance_ = NULL;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001112 RestoreSignalHandler();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001113 }
1114 }
1115
1116 // Implement Thread::Run().
1117 virtual void Run() {
1118 SamplerRegistry::State state;
1119 while ((state = SamplerRegistry::GetState()) !=
1120 SamplerRegistry::HAS_NO_SAMPLERS) {
1121 bool cpu_profiling_enabled =
1122 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
1123 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001124 if (cpu_profiling_enabled && !signal_handler_installed_) {
1125 InstallSignalHandler();
1126 } else if (!cpu_profiling_enabled && signal_handler_installed_) {
1127 RestoreSignalHandler();
1128 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001129 // When CPU profiling is enabled both JavaScript and C++ code is
1130 // profiled. We must not suspend.
1131 if (!cpu_profiling_enabled) {
1132 if (rate_limiter_.SuspendIfNecessary()) continue;
1133 }
1134 if (cpu_profiling_enabled && runtime_profiler_enabled) {
1135 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
1136 return;
1137 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001138 Sleep(HALF_INTERVAL);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001139 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
1140 return;
1141 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001142 Sleep(HALF_INTERVAL);
1143 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001144 if (cpu_profiling_enabled) {
1145 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
1146 this)) {
1147 return;
1148 }
1149 }
1150 if (runtime_profiler_enabled) {
1151 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
1152 NULL)) {
1153 return;
1154 }
1155 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001156 Sleep(FULL_INTERVAL);
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +00001157 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00001158 }
1159 }
1160
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001161 static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
1162 if (!sampler->IsProfiling()) return;
1163 SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
1164 sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
1165 }
1166
1167 static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
1168 if (!sampler->isolate()->IsInitialized()) return;
1169 sampler->isolate()->runtime_profiler()->NotifyTick();
1170 }
1171
1172 void SendProfilingSignal(int tid) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001173 if (!signal_handler_installed_) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001174 // Glibc doesn't provide a wrapper for tgkill(2).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001175#if defined(ANDROID)
1176 syscall(__NR_tgkill, vm_tgid_, tid, SIGPROF);
1177#else
1178 syscall(SYS_tgkill, vm_tgid_, tid, SIGPROF);
1179#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001180 }
1181
1182 void Sleep(SleepInterval full_or_half) {
1183 // Convert ms to us and subtract 100 us to compensate delays
1184 // occuring during signal delivery.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001185 useconds_t interval = interval_ * 1000 - 100;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001186 if (full_or_half == HALF_INTERVAL) interval /= 2;
ulan@chromium.org2efb9002012-01-19 15:36:35 +00001187#if defined(ANDROID)
1188 usleep(interval);
1189#else
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001190 int result = usleep(interval);
1191#ifdef DEBUG
1192 if (result != 0 && errno != EINTR) {
1193 fprintf(stderr,
1194 "SignalSender usleep error; interval = %u, errno = %d\n",
1195 interval,
1196 errno);
1197 ASSERT(result == 0 || errno == EINTR);
1198 }
ulan@chromium.org2efb9002012-01-19 15:36:35 +00001199#endif // DEBUG
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001200 USE(result);
ulan@chromium.org2efb9002012-01-19 15:36:35 +00001201#endif // ANDROID
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001202 }
1203
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001204 const int vm_tgid_;
1205 const int interval_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001206 RuntimeProfilerRateLimiter rate_limiter_;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001207
1208 // Protects the process wide state below.
1209 static Mutex* mutex_;
1210 static SignalSender* instance_;
1211 static bool signal_handler_installed_;
1212 static struct sigaction old_signal_handler_;
1213
jkummerow@chromium.org05ed9dd2012-01-23 14:42:48 +00001214 private:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001215 DISALLOW_COPY_AND_ASSIGN(SignalSender);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001216};
1217
1218
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001219Mutex* SignalSender::mutex_ = OS::CreateMutex();
1220SignalSender* SignalSender::instance_ = NULL;
1221struct sigaction SignalSender::old_signal_handler_;
1222bool SignalSender::signal_handler_installed_ = false;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001223
1224
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001225Sampler::Sampler(Isolate* isolate, int interval)
1226 : isolate_(isolate),
1227 interval_(interval),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001228 profiling_(false),
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001229 active_(false),
1230 samples_taken_(0) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001231 data_ = new PlatformData;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001232}
1233
1234
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001235Sampler::~Sampler() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001236 ASSERT(!IsActive());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001237 delete data_;
1238}
1239
1240
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001241void Sampler::Start() {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001242 ASSERT(!IsActive());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001243 SetActive(true);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001244 SignalSender::AddActiveSampler(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001245}
1246
1247
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001248void Sampler::Stop() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001249 ASSERT(IsActive());
1250 SignalSender::RemoveActiveSampler(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001251 SetActive(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001252}
1253
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001254
1255} } // namespace v8::internal