blob: 855ebf78e16fe14beba51d616c84a83bd03df9f3 [file] [log] [blame]
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +00001// Copyright 2011 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
ricow@chromium.org9fa09672011-07-25 11:05:35 +000081static void* GetRandomMmapAddr() {
82 Isolate* isolate = Isolate::UncheckedCurrent();
83 // Note that the current isolate isn't set up in a call path via
84 // CpuFeatures::Probe. We don't care about randomization in this case because
85 // the code page is immediately freed.
86 if (isolate != NULL) {
87#ifdef V8_TARGET_ARCH_X64
88 uint64_t rnd1 = V8::RandomPrivate(isolate);
89 uint64_t rnd2 = V8::RandomPrivate(isolate);
90 uint64_t raw_addr = (rnd1 << 32) ^ rnd2;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +000091 // Currently available CPUs have 48 bits of virtual addressing. Truncate
92 // the hint address to 46 bits to give the kernel a fighting chance of
93 // fulfilling our placement request.
ricow@chromium.org9fa09672011-07-25 11:05:35 +000094 raw_addr &= V8_UINT64_C(0x3ffffffff000);
95#else
96 uint32_t raw_addr = V8::RandomPrivate(isolate);
97 // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
98 // variety of ASLR modes (PAE kernel, NX compat mode, etc).
99 raw_addr &= 0x3ffff000;
100 raw_addr += 0x20000000;
101#endif
102 return reinterpret_cast<void*>(raw_addr);
103 }
104 return NULL;
105}
106
107
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000108void OS::Setup() {
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000109 // Seed the random number generator. We preserve microsecond resolution.
110 uint64_t seed = Ticks() ^ (getpid() << 16);
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000111 srandom(static_cast<unsigned int>(seed));
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000112 limit_mutex = CreateMutex();
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000113
114#ifdef __arm__
115 // When running on ARM hardware check that the EABI used by V8 and
116 // by the C code is the same.
117 bool hard_float = OS::ArmUsingHardFloat();
118 if (hard_float) {
119#if !USE_EABI_HARDFLOAT
120 PrintF("ERROR: Binary compiled with -mfloat-abi=hard but without "
121 "-DUSE_EABI_HARDFLOAT\n");
122 exit(1);
123#endif
124 } else {
125#if USE_EABI_HARDFLOAT
126 PrintF("ERROR: Binary not compiled with -mfloat-abi=hard but with "
127 "-DUSE_EABI_HARDFLOAT\n");
128 exit(1);
129#endif
130 }
131#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000132}
133
134
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000135uint64_t OS::CpuFeaturesImpliedByPlatform() {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000136 return 0; // Linux runs on anything.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000137}
138
139
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000140#ifdef __arm__
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000141static bool CPUInfoContainsString(const char * search_string) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000142 const char* file_name = "/proc/cpuinfo";
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000143 // This is written as a straight shot one pass parser
144 // and not using STL string and ifstream because,
145 // on Linux, it's reading from a (non-mmap-able)
146 // character special device.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000147 FILE* f = NULL;
148 const char* what = search_string;
149
150 if (NULL == (f = fopen(file_name, "r")))
151 return false;
152
153 int k;
154 while (EOF != (k = fgetc(f))) {
155 if (k == *what) {
156 ++what;
157 while ((*what != '\0') && (*what == fgetc(f))) {
158 ++what;
159 }
160 if (*what == '\0') {
161 fclose(f);
162 return true;
163 } else {
164 what = search_string;
165 }
166 }
167 }
168 fclose(f);
169
170 // Did not find string in the proc file.
171 return false;
172}
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000173
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000174
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000175bool OS::ArmCpuHasFeature(CpuFeature feature) {
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000176 const char* search_string = NULL;
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000177 // Simple detection of VFP at runtime for Linux.
178 // It is based on /proc/cpuinfo, which reveals hardware configuration
179 // to user-space applications. According to ARM (mid 2009), no similar
180 // facility is universally available on the ARM architectures,
181 // so it's up to individual OSes to provide such.
182 switch (feature) {
183 case VFP3:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000184 search_string = "vfpv3";
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000185 break;
186 case ARMv7:
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000187 search_string = "ARMv7";
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000188 break;
189 default:
190 UNREACHABLE();
191 }
192
ager@chromium.org5f0c45f2010-12-17 08:51:21 +0000193 if (CPUInfoContainsString(search_string)) {
194 return true;
195 }
196
197 if (feature == VFP3) {
198 // Some old kernels will report vfp not vfpv3. Here we make a last attempt
199 // to detect vfpv3 by checking for vfp *and* neon, since neon is only
200 // available on architectures with vfpv3.
201 // Checking neon on its own is not enough as it is possible to have neon
202 // without vfp.
203 if (CPUInfoContainsString("vfp") && CPUInfoContainsString("neon")) {
lrn@chromium.orgfa943b72010-11-03 08:14:36 +0000204 return true;
205 }
206 }
207
208 return false;
209}
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000210
211
212// Simple helper function to detect whether the C code is compiled with
213// option -mfloat-abi=hard. The register d0 is loaded with 1.0 and the register
214// pair r0, r1 is loaded with 0.0. If -mfloat-abi=hard is pased to GCC then
215// calling this will return 1.0 and otherwise 0.0.
216static void ArmUsingHardFloatHelper() {
217 asm("mov r0, #0");
218#if defined(__VFP_FP__) && !defined(__SOFTFP__)
219 // Load 0x3ff00000 into r1 using instructions available in both ARM
220 // and Thumb mode.
221 asm("mov r1, #3");
222 asm("mov r2, #255");
223 asm("lsl r1, r1, #8");
224 asm("orr r1, r1, r2");
lrn@chromium.org1c092762011-05-09 09:42:16 +0000225 asm("lsl r1, r1, #20");
sgjesse@chromium.org8e8294a2011-05-02 14:30:53 +0000226 // For vmov d0, r0, r1 use ARM mode.
227#ifdef __thumb__
228 asm volatile(
229 "@ Enter ARM Mode \n\t"
230 " adr r3, 1f \n\t"
231 " bx r3 \n\t"
232 " .ALIGN 4 \n\t"
233 " .ARM \n"
234 "1: vmov d0, r0, r1 \n\t"
235 "@ Enter THUMB Mode\n\t"
236 " adr r3, 2f+1 \n\t"
237 " bx r3 \n\t"
238 " .THUMB \n"
239 "2: \n\t");
240#else
241 asm("vmov d0, r0, r1");
242#endif // __thumb__
243#endif // defined(__VFP_FP__) && !defined(__SOFTFP__)
244 asm("mov r1, #0");
245}
246
247
248bool OS::ArmUsingHardFloat() {
249 // Cast helper function from returning void to returning double.
250 typedef double (*F)();
251 F f = FUNCTION_CAST<F>(FUNCTION_ADDR(ArmUsingHardFloatHelper));
252 return f() == 1.0;
253}
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000254#endif // def __arm__
255
256
lrn@chromium.org7516f052011-03-30 08:52:27 +0000257#ifdef __mips__
258bool OS::MipsCpuHasFeature(CpuFeature feature) {
259 const char* search_string = NULL;
260 const char* file_name = "/proc/cpuinfo";
261 // Simple detection of FPU at runtime for Linux.
262 // It is based on /proc/cpuinfo, which reveals hardware configuration
263 // to user-space applications. According to MIPS (early 2010), no similar
264 // facility is universally available on the MIPS architectures,
265 // so it's up to individual OSes to provide such.
266 //
267 // This is written as a straight shot one pass parser
268 // and not using STL string and ifstream because,
269 // on Linux, it's reading from a (non-mmap-able)
270 // character special device.
271
272 switch (feature) {
273 case FPU:
274 search_string = "FPU";
275 break;
276 default:
277 UNREACHABLE();
278 }
279
280 FILE* f = NULL;
281 const char* what = search_string;
282
283 if (NULL == (f = fopen(file_name, "r")))
284 return false;
285
286 int k;
287 while (EOF != (k = fgetc(f))) {
288 if (k == *what) {
289 ++what;
290 while ((*what != '\0') && (*what == fgetc(f))) {
291 ++what;
292 }
293 if (*what == '\0') {
294 fclose(f);
295 return true;
296 } else {
297 what = search_string;
298 }
299 }
300 }
301 fclose(f);
302
303 // Did not find string in the proc file.
304 return false;
305}
306#endif // def __mips__
307
308
ager@chromium.org236ad962008-09-25 09:45:57 +0000309int OS::ActivationFrameAlignment() {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000310#ifdef V8_TARGET_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000311 // On EABI ARM targets this is required for fp correctness in the
312 // runtime system.
ager@chromium.org3a6061e2009-03-12 14:24:36 +0000313 return 8;
ager@chromium.org5c838252010-02-19 08:53:10 +0000314#elif V8_TARGET_ARCH_MIPS
315 return 8;
316#endif
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000317 // With gcc 4.4 the tree vectorization optimizer can generate code
ager@chromium.orge2902be2009-06-08 12:21:35 +0000318 // that requires 16 byte alignment such as movdqa on x86.
319 return 16;
ager@chromium.org236ad962008-09-25 09:45:57 +0000320}
321
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000322
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000323void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
lrn@chromium.org7516f052011-03-30 08:52:27 +0000324#if (defined(V8_TARGET_ARCH_ARM) && defined(__arm__)) || \
325 (defined(V8_TARGET_ARCH_MIPS) && defined(__mips__))
326 // Only use on ARM or MIPS hardware.
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000327 MemoryBarrier();
kmillikin@chromium.org9155e252010-05-26 13:27:57 +0000328#else
329 __asm__ __volatile__("" : : : "memory");
330 // An x86 store acts as a release barrier.
331#endif
332 *ptr = value;
333}
334
335
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000336const char* OS::LocalTimezone(double time) {
337 if (isnan(time)) return "";
338 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
339 struct tm* t = localtime(&tv);
340 if (NULL == t) return "";
341 return t->tm_zone;
342}
343
344
345double OS::LocalTimeOffset() {
346 time_t tv = time(NULL);
347 struct tm* t = localtime(&tv);
348 // tm_gmtoff includes any daylight savings offset, so subtract it.
349 return static_cast<double>(t->tm_gmtoff * msPerSecond -
350 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
351}
352
353
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000354// We keep the lowest and highest addresses mapped as a quick way of
355// determining that pointers are outside the heap (used mostly in assertions
356// and verification). The estimate is conservative, ie, not all addresses in
357// 'allocated' space are actually allocated to our heap. The range is
358// [lowest, highest), inclusive on the low and and exclusive on the high end.
359static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
360static void* highest_ever_allocated = reinterpret_cast<void*>(0);
361
362
363static void UpdateAllocatedSpaceLimits(void* address, int size) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000364 ASSERT(limit_mutex != NULL);
365 ScopedLock lock(limit_mutex);
366
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000367 lowest_ever_allocated = Min(lowest_ever_allocated, address);
368 highest_ever_allocated =
369 Max(highest_ever_allocated,
370 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
371}
372
373
374bool OS::IsOutsideAllocatedSpace(void* address) {
375 return address < lowest_ever_allocated || address >= highest_ever_allocated;
376}
377
378
379size_t OS::AllocateAlignment() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000380 return sysconf(_SC_PAGESIZE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000381}
382
383
kasper.lund7276f142008-07-30 08:49:36 +0000384void* OS::Allocate(const size_t requested,
385 size_t* allocated,
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000386 bool is_executable) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000387 const size_t msize = RoundUp(requested, AllocateAlignment());
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000388 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
ricow@chromium.org9fa09672011-07-25 11:05:35 +0000389 void* addr = GetRandomMmapAddr();
390 void* mbase = mmap(addr, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391 if (mbase == MAP_FAILED) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000392 LOG(i::Isolate::Current(),
393 StringEvent("OS::Allocate", "mmap failed"));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000394 return NULL;
395 }
396 *allocated = msize;
397 UpdateAllocatedSpaceLimits(mbase, msize);
398 return mbase;
399}
400
401
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000402void OS::Free(void* address, const size_t size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000403 // TODO(1240712): munmap has a return value which is ignored here.
ager@chromium.orga1645e22009-09-09 19:27:10 +0000404 int result = munmap(address, size);
405 USE(result);
406 ASSERT(result == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000407}
408
409
410void OS::Sleep(int milliseconds) {
411 unsigned int ms = static_cast<unsigned int>(milliseconds);
412 usleep(1000 * ms);
413}
414
415
416void OS::Abort() {
417 // Redirect to std abort to signal abnormal program termination.
418 abort();
419}
420
421
kasper.lund7276f142008-07-30 08:49:36 +0000422void OS::DebugBreak() {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000423// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
424// which is the architecture of generated code).
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000425#if (defined(__arm__) || defined(__thumb__))
426# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
kasper.lund7276f142008-07-30 08:49:36 +0000427 asm("bkpt 0");
ager@chromium.orgea4f62e2010-08-16 16:28:43 +0000428# endif
ager@chromium.org5c838252010-02-19 08:53:10 +0000429#elif defined(__mips__)
430 asm("break");
kasper.lund7276f142008-07-30 08:49:36 +0000431#else
432 asm("int $3");
433#endif
434}
435
436
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000437class PosixMemoryMappedFile : public OS::MemoryMappedFile {
438 public:
439 PosixMemoryMappedFile(FILE* file, void* memory, int size)
440 : file_(file), memory_(memory), size_(size) { }
441 virtual ~PosixMemoryMappedFile();
442 virtual void* memory() { return memory_; }
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000443 virtual int size() { return size_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000444 private:
445 FILE* file_;
446 void* memory_;
447 int size_;
448};
449
450
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000451OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000452 FILE* file = fopen(name, "r+");
vegorov@chromium.org0a4e9012011-01-24 12:33:13 +0000453 if (file == NULL) return NULL;
454
455 fseek(file, 0, SEEK_END);
456 int size = ftell(file);
457
458 void* memory =
459 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
460 return new PosixMemoryMappedFile(file, memory, size);
461}
462
463
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000464OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
465 void* initial) {
466 FILE* file = fopen(name, "w+");
467 if (file == NULL) return NULL;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000468 int result = fwrite(initial, size, 1, file);
469 if (result < 1) {
470 fclose(file);
471 return NULL;
472 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000473 void* memory =
474 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
475 return new PosixMemoryMappedFile(file, memory, size);
476}
477
478
479PosixMemoryMappedFile::~PosixMemoryMappedFile() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000480 if (memory_) OS::Free(memory_, size_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000481 fclose(file_);
482}
483
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000484
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000485void OS::LogSharedLibraryAddresses() {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000486 // This function assumes that the layout of the file is as follows:
487 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
488 // If we encounter an unexpected situation we abort scanning further entries.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000489 FILE* fp = fopen("/proc/self/maps", "r");
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000490 if (fp == NULL) return;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000491
492 // Allocate enough room to be able to store a full file name.
493 const int kLibNameLen = FILENAME_MAX + 1;
494 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
495
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000496 i::Isolate* isolate = ISOLATE;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000497 // This loop will terminate once the scanning hits an EOF.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000498 while (true) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000499 uintptr_t start, end;
500 char attr_r, attr_w, attr_x, attr_p;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000501 // Parse the addresses and permission bits at the beginning of the line.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000502 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
503 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 +0000504
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000505 int c;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000506 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
507 // Found a read-only executable entry. Skip characters until we reach
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000508 // the beginning of the filename or the end of the line.
509 do {
510 c = getc(fp);
511 } while ((c != EOF) && (c != '\n') && (c != '/'));
512 if (c == EOF) break; // EOF: Was unexpected, just exit.
513
514 // Process the filename if found.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000515 if (c == '/') {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000516 ungetc(c, fp); // Push the '/' back into the stream to be read below.
517
518 // Read to the end of the line. Exit if the read fails.
519 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
520
521 // Drop the newline character read by fgets. We do not need to check
522 // for a zero-length string because we know that we at least read the
523 // '/' character.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000524 lib_name[strlen(lib_name) - 1] = '\0';
525 } else {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000526 // No library name found, just record the raw address range.
527 snprintf(lib_name, kLibNameLen,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000528 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
529 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000530 LOG(isolate, SharedLibraryEvent(lib_name, start, end));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000531 } else {
532 // Entry not describing executable data. Skip to end of line to setup
533 // reading the next entry.
534 do {
535 c = getc(fp);
536 } while ((c != EOF) && (c != '\n'));
537 if (c == EOF) break;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000538 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000539 }
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000540 free(lib_name);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000541 fclose(fp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000542}
543
544
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000545static const char kGCFakeMmap[] = "/tmp/__v8_gc__";
546
547
548void OS::SignalCodeMovingGC() {
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000549 // Support for ll_prof.py.
550 //
551 // The Linux profiler built into the kernel logs all mmap's with
552 // PROT_EXEC so that analysis tools can properly attribute ticks. We
553 // do a mmap with a name known by ll_prof.py and immediately munmap
554 // it. This injects a GC marker into the stream of events generated
555 // by the kernel and allows us to synchronize V8 code log and the
556 // kernel log.
557 int size = sysconf(_SC_PAGESIZE);
558 FILE* f = fopen(kGCFakeMmap, "w+");
559 void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE,
560 fileno(f), 0);
561 ASSERT(addr != MAP_FAILED);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000562 OS::Free(addr, size);
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000563 fclose(f);
whesse@chromium.org4a5224e2010-10-20 12:37:07 +0000564}
565
566
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000567int OS::StackWalk(Vector<OS::StackFrame> frames) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000568 // backtrace is a glibc extension.
569#ifdef __GLIBC__
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000570 int frames_size = frames.length();
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000571 ScopedVector<void*> addresses(frames_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000572
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000573 int frames_count = backtrace(addresses.start(), frames_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000574
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000575 char** symbols = backtrace_symbols(addresses.start(), frames_count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000576 if (symbols == NULL) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000577 return kStackWalkError;
578 }
579
580 for (int i = 0; i < frames_count; i++) {
581 frames[i].address = addresses[i];
582 // Format a text representation of the frame based on the information
583 // available.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000584 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
585 "%s",
586 symbols[i]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000587 // Make sure line termination is in place.
588 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
589 }
590
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000591 free(symbols);
592
593 return frames_count;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000594#else // ndef __GLIBC__
595 return 0;
596#endif // ndef __GLIBC__
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000597}
598
599
600// Constants used for mmap.
601static const int kMmapFd = -1;
602static const int kMmapFdOffset = 0;
603
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000604VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000605
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000606VirtualMemory::VirtualMemory(size_t size) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000607 address_ = ReserveRegion(size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000608 size_ = size;
609}
610
611
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000612VirtualMemory::VirtualMemory(size_t size, size_t alignment)
613 : address_(NULL), size_(0) {
614 ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
615 size_t request_size = RoundUp(size + alignment,
616 static_cast<intptr_t>(OS::AllocateAlignment()));
617 void* reservation = mmap(GetRandomMmapAddr(),
618 request_size,
619 PROT_NONE,
620 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
621 kMmapFd,
622 kMmapFdOffset);
623 if (reservation == MAP_FAILED) return;
624
625 Address base = static_cast<Address>(reservation);
626 Address aligned_base = RoundUp(base, alignment);
627 ASSERT_LE(base, aligned_base);
628
629 // Unmap extra memory reserved before and after the desired block.
630 if (aligned_base != base) {
631 size_t prefix_size = static_cast<size_t>(aligned_base - base);
632 OS::Free(base, prefix_size);
633 request_size -= prefix_size;
634 }
635
636 size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
637 ASSERT_LE(aligned_size, request_size);
638
639 if (aligned_size != request_size) {
640 size_t suffix_size = request_size - aligned_size;
641 OS::Free(aligned_base + aligned_size, suffix_size);
642 request_size -= suffix_size;
643 }
644
645 ASSERT(aligned_size == request_size);
646
647 address_ = static_cast<void*>(aligned_base);
648 size_ = aligned_size;
649}
650
651
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000652VirtualMemory::~VirtualMemory() {
653 if (IsReserved()) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000654 bool result = ReleaseRegion(address(), size());
655 ASSERT(result);
656 USE(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000657 }
658}
659
660
661bool VirtualMemory::IsReserved() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000662 return address_ != NULL;
663}
664
665
666void VirtualMemory::Reset() {
667 address_ = NULL;
668 size_ = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000669}
670
671
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000672bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000673 return CommitRegion(address, size, is_executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000674}
675
676
677bool VirtualMemory::Uncommit(void* address, size_t size) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000678 return UncommitRegion(address, size);
679}
680
681
682void* VirtualMemory::ReserveRegion(size_t size) {
683 void* result = mmap(GetRandomMmapAddr(),
684 size,
685 PROT_NONE,
686 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
687 kMmapFd,
688 kMmapFdOffset);
689
690 if (result == MAP_FAILED) return NULL;
691
692 return result;
693}
694
695
696bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
697 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
698 if (MAP_FAILED == mmap(base,
699 size,
700 prot,
701 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
702 kMmapFd,
703 kMmapFdOffset)) {
704 return false;
705 }
706
707 UpdateAllocatedSpaceLimits(base, size);
708 return true;
709}
710
711
712bool VirtualMemory::UncommitRegion(void* base, size_t size) {
713 return mmap(base,
714 size,
715 PROT_NONE,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000716 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +0000717 kMmapFd,
718 kMmapFdOffset) != MAP_FAILED;
719}
720
721
722bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
723 return munmap(base, size) == 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724}
725
726
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000727class Thread::PlatformData : public Malloced {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000728 public:
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000729 PlatformData() : thread_(kNoThread) {}
ager@chromium.org41826e72009-03-30 13:30:57 +0000730
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000731 pthread_t thread_; // Thread handle for pthread.
732};
733
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000734Thread::Thread(const Options& options)
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000735 : data_(new PlatformData()),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000736 stack_size_(options.stack_size) {
737 set_name(options.name);
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000738}
739
740
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000741Thread::Thread(const char* name)
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000742 : data_(new PlatformData()),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000743 stack_size_(0) {
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000744 set_name(name);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000745}
746
747
748Thread::~Thread() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000749 delete data_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000750}
751
752
753static void* ThreadEntry(void* arg) {
754 Thread* thread = reinterpret_cast<Thread*>(arg);
755 // This is also initialized by the first argument to pthread_create() but we
756 // don't know which thread will run first (the original thread or the new
757 // one) so we initialize it here too.
danno@chromium.orgb6451162011-08-17 14:33:23 +0000758#ifdef PR_SET_NAME
karlklose@chromium.org8f806e82011-03-07 14:06:08 +0000759 prctl(PR_SET_NAME,
760 reinterpret_cast<unsigned long>(thread->name()), // NOLINT
761 0, 0, 0);
danno@chromium.orgb6451162011-08-17 14:33:23 +0000762#endif
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000763 thread->data()->thread_ = pthread_self();
764 ASSERT(thread->data()->thread_ != kNoThread);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000765 thread->Run();
766 return NULL;
767}
768
769
lrn@chromium.org5d00b602011-01-05 09:51:43 +0000770void Thread::set_name(const char* name) {
771 strncpy(name_, name, sizeof(name_));
772 name_[sizeof(name_) - 1] = '\0';
773}
774
775
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000776void Thread::Start() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000777 pthread_attr_t* attr_ptr = NULL;
778 pthread_attr_t attr;
779 if (stack_size_ > 0) {
780 pthread_attr_init(&attr);
781 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
782 attr_ptr = &attr;
783 }
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000784 pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this);
785 ASSERT(data_->thread_ != kNoThread);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000786}
787
788
789void Thread::Join() {
ager@chromium.orga9aa5fa2011-04-13 08:46:07 +0000790 pthread_join(data_->thread_, NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000791}
792
793
794Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
795 pthread_key_t key;
796 int result = pthread_key_create(&key, NULL);
797 USE(result);
798 ASSERT(result == 0);
799 return static_cast<LocalStorageKey>(key);
800}
801
802
803void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
804 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
805 int result = pthread_key_delete(pthread_key);
806 USE(result);
807 ASSERT(result == 0);
808}
809
810
811void* Thread::GetThreadLocal(LocalStorageKey key) {
812 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
813 return pthread_getspecific(pthread_key);
814}
815
816
817void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
818 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
819 pthread_setspecific(pthread_key, value);
820}
821
822
823void Thread::YieldCPU() {
824 sched_yield();
825}
826
827
828class LinuxMutex : public Mutex {
829 public:
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000830 LinuxMutex() {
831 pthread_mutexattr_t attrs;
832 int result = pthread_mutexattr_init(&attrs);
833 ASSERT(result == 0);
834 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
835 ASSERT(result == 0);
836 result = pthread_mutex_init(&mutex_, &attrs);
837 ASSERT(result == 0);
rossberg@chromium.org717967f2011-07-20 13:44:42 +0000838 USE(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000839 }
840
841 virtual ~LinuxMutex() { pthread_mutex_destroy(&mutex_); }
842
843 virtual int Lock() {
844 int result = pthread_mutex_lock(&mutex_);
845 return result;
846 }
847
848 virtual int Unlock() {
849 int result = pthread_mutex_unlock(&mutex_);
850 return result;
851 }
852
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000853 virtual bool TryLock() {
854 int result = pthread_mutex_trylock(&mutex_);
855 // Return false if the lock is busy and locking failed.
856 if (result == EBUSY) {
857 return false;
858 }
859 ASSERT(result == 0); // Verify no other errors.
860 return true;
861 }
862
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000863 private:
864 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
865};
866
867
868Mutex* OS::CreateMutex() {
869 return new LinuxMutex();
870}
871
872
873class LinuxSemaphore : public Semaphore {
874 public:
875 explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); }
876 virtual ~LinuxSemaphore() { sem_destroy(&sem_); }
877
kasper.lund7276f142008-07-30 08:49:36 +0000878 virtual void Wait();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000879 virtual bool Wait(int timeout);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000880 virtual void Signal() { sem_post(&sem_); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000881 private:
882 sem_t sem_;
883};
884
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000885
kasper.lund7276f142008-07-30 08:49:36 +0000886void LinuxSemaphore::Wait() {
887 while (true) {
888 int result = sem_wait(&sem_);
889 if (result == 0) return; // Successfully got semaphore.
890 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
891 }
892}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000893
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000894
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000895#ifndef TIMEVAL_TO_TIMESPEC
896#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
897 (ts)->tv_sec = (tv)->tv_sec; \
898 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
899} while (false)
900#endif
901
902
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000903bool LinuxSemaphore::Wait(int timeout) {
904 const long kOneSecondMicros = 1000000; // NOLINT
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000905
906 // Split timeout into second and nanosecond parts.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000907 struct timeval delta;
908 delta.tv_usec = timeout % kOneSecondMicros;
909 delta.tv_sec = timeout / kOneSecondMicros;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000910
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000911 struct timeval current_time;
912 // Get the current time.
913 if (gettimeofday(&current_time, NULL) == -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000914 return false;
915 }
916
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000917 // Calculate time for end of timeout.
918 struct timeval end_time;
919 timeradd(&current_time, &delta, &end_time);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000920
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000921 struct timespec ts;
922 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000923 // Wait for semaphore signalled or timeout.
924 while (true) {
925 int result = sem_timedwait(&sem_, &ts);
926 if (result == 0) return true; // Successfully got semaphore.
927 if (result > 0) {
928 // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
929 errno = result;
930 result = -1;
931 }
932 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
933 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
934 }
935}
936
937
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000938Semaphore* OS::CreateSemaphore(int count) {
939 return new LinuxSemaphore(count);
940}
941
ager@chromium.org381abbb2009-02-25 13:23:22 +0000942
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000943#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
944// Android runs a fairly new Linux kernel, so signal info is there,
945// but the C library doesn't have the structs defined.
946
947struct sigcontext {
948 uint32_t trap_no;
949 uint32_t error_code;
950 uint32_t oldmask;
951 uint32_t gregs[16];
952 uint32_t arm_cpsr;
953 uint32_t fault_address;
954};
955typedef uint32_t __sigset_t;
956typedef struct sigcontext mcontext_t;
957typedef struct ucontext {
958 uint32_t uc_flags;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000959 struct ucontext* uc_link;
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000960 stack_t uc_stack;
961 mcontext_t uc_mcontext;
962 __sigset_t uc_sigmask;
963} ucontext_t;
964enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
965
966#endif
967
968
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000969static int GetThreadID() {
970 // Glibc doesn't provide a wrapper for gettid(2).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000971#if defined(ANDROID)
972 return syscall(__NR_gettid);
973#else
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000974 return syscall(SYS_gettid);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000975#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000976}
977
978
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000979static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000980#ifndef V8_HOST_ARCH_MIPS
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000981 USE(info);
982 if (signal != SIGPROF) return;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000983 Isolate* isolate = Isolate::UncheckedCurrent();
984 if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
985 // We require a fully initialized and entered isolate.
986 return;
987 }
vitalyr@chromium.org0ec56d62011-04-15 22:22:08 +0000988 if (v8::Locker::IsActive() &&
989 !isolate->thread_manager()->IsLockedByCurrentThread()) {
990 return;
991 }
992
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000993 Sampler* sampler = isolate->logger()->sampler();
994 if (sampler == NULL || !sampler->IsActive()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000995
lrn@chromium.org25156de2010-04-06 13:10:27 +0000996 TickSample sample_obj;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000997 TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
ager@chromium.org357bf652010-04-12 11:30:10 +0000998 if (sample == NULL) sample = &sample_obj;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000999
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001000 // Extracting the sample from the context is extremely machine dependent.
1001 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
1002 mcontext_t& mcontext = ucontext->uc_mcontext;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001003 sample->state = isolate->current_vm_state();
ager@chromium.org9085a012009-05-11 19:22:57 +00001004#if V8_HOST_ARCH_IA32
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001005 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
1006 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
1007 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
ager@chromium.org9085a012009-05-11 19:22:57 +00001008#elif V8_HOST_ARCH_X64
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001009 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
1010 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
1011 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
ager@chromium.org9085a012009-05-11 19:22:57 +00001012#elif V8_HOST_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001013// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
1014#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001015 sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
1016 sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
1017 sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001018#else
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001019 sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
1020 sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
1021 sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +00001022#endif
ager@chromium.org5c838252010-02-19 08:53:10 +00001023#elif V8_HOST_ARCH_MIPS
lrn@chromium.org7516f052011-03-30 08:52:27 +00001024 sample.pc = reinterpret_cast<Address>(mcontext.pc);
1025 sample.sp = reinterpret_cast<Address>(mcontext.gregs[29]);
1026 sample.fp = reinterpret_cast<Address>(mcontext.gregs[30]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001027#endif
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001028 sampler->SampleStack(sample);
1029 sampler->Tick(sample);
lrn@chromium.org25156de2010-04-06 13:10:27 +00001030#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001031}
1032
1033
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001034class Sampler::PlatformData : public Malloced {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001035 public:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001036 PlatformData() : vm_tid_(GetThreadID()) {}
1037
1038 int vm_tid() const { return vm_tid_; }
1039
1040 private:
1041 const int vm_tid_;
1042};
1043
1044
1045class SignalSender : public Thread {
1046 public:
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001047 enum SleepInterval {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001048 HALF_INTERVAL,
1049 FULL_INTERVAL
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001050 };
1051
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001052 explicit SignalSender(int interval)
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +00001053 : Thread("SignalSender"),
lrn@chromium.org303ada72010-10-27 09:33:13 +00001054 vm_tgid_(getpid()),
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001055 interval_(interval) {}
1056
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001057 static void InstallSignalHandler() {
1058 struct sigaction sa;
1059 sa.sa_sigaction = ProfilerSignalHandler;
1060 sigemptyset(&sa.sa_mask);
1061 sa.sa_flags = SA_RESTART | SA_SIGINFO;
1062 signal_handler_installed_ =
1063 (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
1064 }
1065
1066 static void RestoreSignalHandler() {
1067 if (signal_handler_installed_) {
1068 sigaction(SIGPROF, &old_signal_handler_, 0);
1069 signal_handler_installed_ = false;
1070 }
1071 }
1072
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001073 static void AddActiveSampler(Sampler* sampler) {
1074 ScopedLock lock(mutex_);
1075 SamplerRegistry::AddActiveSampler(sampler);
1076 if (instance_ == NULL) {
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001077 // Start a thread that will send SIGPROF signal to VM threads,
1078 // when CPU profiling will be enabled.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001079 instance_ = new SignalSender(sampler->interval());
1080 instance_->Start();
1081 } else {
1082 ASSERT(instance_->interval_ == sampler->interval());
1083 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001084 }
1085
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001086 static void RemoveActiveSampler(Sampler* sampler) {
1087 ScopedLock lock(mutex_);
1088 SamplerRegistry::RemoveActiveSampler(sampler);
1089 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
jkummerow@chromium.orgddda9e82011-07-06 11:27:02 +00001090 RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001091 delete instance_;
1092 instance_ = NULL;
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001093 RestoreSignalHandler();
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001094 }
1095 }
1096
1097 // Implement Thread::Run().
1098 virtual void Run() {
1099 SamplerRegistry::State state;
1100 while ((state = SamplerRegistry::GetState()) !=
1101 SamplerRegistry::HAS_NO_SAMPLERS) {
1102 bool cpu_profiling_enabled =
1103 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
1104 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
erik.corry@gmail.comd6076d92011-06-06 09:39:18 +00001105 if (cpu_profiling_enabled && !signal_handler_installed_) {
1106 InstallSignalHandler();
1107 } else if (!cpu_profiling_enabled && signal_handler_installed_) {
1108 RestoreSignalHandler();
1109 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001110 // When CPU profiling is enabled both JavaScript and C++ code is
1111 // profiled. We must not suspend.
1112 if (!cpu_profiling_enabled) {
1113 if (rate_limiter_.SuspendIfNecessary()) continue;
1114 }
1115 if (cpu_profiling_enabled && runtime_profiler_enabled) {
1116 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
1117 return;
1118 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001119 Sleep(HALF_INTERVAL);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001120 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
1121 return;
1122 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001123 Sleep(HALF_INTERVAL);
1124 } else {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001125 if (cpu_profiling_enabled) {
1126 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
1127 this)) {
1128 return;
1129 }
1130 }
1131 if (runtime_profiler_enabled) {
1132 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
1133 NULL)) {
1134 return;
1135 }
1136 }
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001137 Sleep(FULL_INTERVAL);
whesse@chromium.orgf0ac72d2010-11-08 12:47:26 +00001138 }
lrn@chromium.org303ada72010-10-27 09:33:13 +00001139 }
1140 }
1141
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001142 static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
1143 if (!sampler->IsProfiling()) return;
1144 SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
1145 sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
1146 }
1147
1148 static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
1149 if (!sampler->isolate()->IsInitialized()) return;
1150 sampler->isolate()->runtime_profiler()->NotifyTick();
1151 }
1152
1153 void SendProfilingSignal(int tid) {
karlklose@chromium.org8f806e82011-03-07 14:06:08 +00001154 if (!signal_handler_installed_) return;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001155 // Glibc doesn't provide a wrapper for tgkill(2).
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001156#if defined(ANDROID)
1157 syscall(__NR_tgkill, vm_tgid_, tid, SIGPROF);
1158#else
1159 syscall(SYS_tgkill, vm_tgid_, tid, SIGPROF);
1160#endif
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001161 }
1162
1163 void Sleep(SleepInterval full_or_half) {
1164 // Convert ms to us and subtract 100 us to compensate delays
1165 // occuring during signal delivery.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001166 useconds_t interval = interval_ * 1000 - 100;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001167 if (full_or_half == HALF_INTERVAL) interval /= 2;
1168 int result = usleep(interval);
1169#ifdef DEBUG
1170 if (result != 0 && errno != EINTR) {
1171 fprintf(stderr,
1172 "SignalSender usleep error; interval = %u, errno = %d\n",
1173 interval,
1174 errno);
1175 ASSERT(result == 0 || errno == EINTR);
1176 }
1177#endif
1178 USE(result);
1179 }
1180
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001181 const int vm_tgid_;
1182 const int interval_;
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001183 RuntimeProfilerRateLimiter rate_limiter_;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001184
1185 // Protects the process wide state below.
1186 static Mutex* mutex_;
1187 static SignalSender* instance_;
1188 static bool signal_handler_installed_;
1189 static struct sigaction old_signal_handler_;
1190
1191 DISALLOW_COPY_AND_ASSIGN(SignalSender);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192};
1193
1194
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001195Mutex* SignalSender::mutex_ = OS::CreateMutex();
1196SignalSender* SignalSender::instance_ = NULL;
1197struct sigaction SignalSender::old_signal_handler_;
1198bool SignalSender::signal_handler_installed_ = false;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001199
1200
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001201Sampler::Sampler(Isolate* isolate, int interval)
1202 : isolate_(isolate),
1203 interval_(interval),
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001204 profiling_(false),
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001205 active_(false),
1206 samples_taken_(0) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001207 data_ = new PlatformData;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001208}
1209
1210
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001211Sampler::~Sampler() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001212 ASSERT(!IsActive());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001213 delete data_;
1214}
1215
1216
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001217void Sampler::Start() {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001218 ASSERT(!IsActive());
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001219 SetActive(true);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001220 SignalSender::AddActiveSampler(this);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001221}
1222
1223
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001224void Sampler::Stop() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001225 ASSERT(IsActive());
1226 SignalSender::RemoveActiveSampler(this);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001227 SetActive(false);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001228}
1229
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001230
1231} } // namespace v8::internal