blob: 9ce0be011bd46164f9308672e02e964714f26bac [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000028// Platform specific code for Linux goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000030
31#include <pthread.h>
32#include <semaphore.h>
33#include <signal.h>
34#include <sys/time.h>
35#include <sys/resource.h>
ager@chromium.org381abbb2009-02-25 13:23:22 +000036#include <sys/types.h>
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037#include <stdlib.h>
38
39// Ubuntu Dapper requires memory pages to be marked as
40// executable. Otherwise, OS raises an exception when executing code
41// in that page.
42#include <sys/types.h> // mmap & munmap
ager@chromium.org236ad962008-09-25 09:45:57 +000043#include <sys/mman.h> // mmap & munmap
44#include <sys/stat.h> // open
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000045#include <fcntl.h> // open
46#include <unistd.h> // sysconf
47#ifdef __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000048#include <execinfo.h> // backtrace, backtrace_symbols
ager@chromium.orgbb29dc92009-03-24 13:25:23 +000049#endif // def __GLIBC__
ager@chromium.org236ad962008-09-25 09:45:57 +000050#include <strings.h> // index
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051#include <errno.h>
52#include <stdarg.h>
53
54#undef MAP_TYPE
55
56#include "v8.h"
57
58#include "platform.h"
ager@chromium.orga1645e22009-09-09 19:27:10 +000059#include "top.h"
60#include "v8threads.h"
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000061
62
kasperl@chromium.org71affb52009-05-26 05:44:31 +000063namespace v8 {
64namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000065
66// 0 is never a valid thread id on Linux since tids and pids share a
67// name space and pid 0 is reserved (see man 2 kill).
68static const pthread_t kNoThread = (pthread_t) 0;
69
70
71double ceiling(double x) {
72 return ceil(x);
73}
74
75
76void OS::Setup() {
77 // Seed the random number generator.
ager@chromium.org9258b6b2008-09-11 09:11:10 +000078 // Convert the current time to a 64-bit integer first, before converting it
79 // to an unsigned. Going directly can cause an overflow and the seed to be
80 // set to all ones. The seed will be identical for different instances that
81 // call this setup code within the same millisecond.
82 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
83 srandom(static_cast<unsigned int>(seed));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000084}
85
86
ager@chromium.orgc4c92722009-11-18 14:12:51 +000087uint64_t OS::CpuFeaturesImpliedByPlatform() {
88#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
89 // Here gcc is telling us that we are on an ARM and gcc is assuming that we
90 // have VFP3 instructions. If gcc can assume it then so can we.
91 return 1u << VFP3;
92#else
93 return 0; // Linux runs on anything.
94#endif
95}
96
97
ager@chromium.org236ad962008-09-25 09:45:57 +000098double OS::nan_value() {
99 return NAN;
100}
101
102
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000103#ifdef __arm__
104bool OS::ArmCpuHasFeature(CpuFeature feature) {
105 const char* search_string = NULL;
106 const char* file_name = "/proc/cpuinfo";
107 // Simple detection of VFP at runtime for Linux.
108 // It is based on /proc/cpuinfo, which reveals hardware configuration
109 // to user-space applications. According to ARM (mid 2009), no similar
110 // facility is universally available on the ARM architectures,
111 // so it's up to individual OSes to provide such.
112 //
113 // This is written as a straight shot one pass parser
114 // and not using STL string and ifstream because,
115 // on Linux, it's reading from a (non-mmap-able)
116 // character special device.
117 switch (feature) {
118 case VFP3:
119 search_string = "vfp";
120 break;
121 default:
122 UNREACHABLE();
123 }
124
125 FILE* f = NULL;
126 const char* what = search_string;
127
128 if (NULL == (f = fopen(file_name, "r")))
129 return false;
130
131 int k;
132 while (EOF != (k = fgetc(f))) {
133 if (k == *what) {
134 ++what;
135 while ((*what != '\0') && (*what == fgetc(f))) {
136 ++what;
137 }
138 if (*what == '\0') {
139 fclose(f);
140 return true;
141 } else {
142 what = search_string;
143 }
144 }
145 }
146 fclose(f);
147
148 // Did not find string in the proc file.
149 return false;
150}
151#endif // def __arm__
152
153
ager@chromium.org236ad962008-09-25 09:45:57 +0000154int OS::ActivationFrameAlignment() {
ager@chromium.orge2902be2009-06-08 12:21:35 +0000155#ifdef V8_TARGET_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000156 // On EABI ARM targets this is required for fp correctness in the
157 // runtime system.
ager@chromium.org3a6061e2009-03-12 14:24:36 +0000158 return 8;
ager@chromium.orge2902be2009-06-08 12:21:35 +0000159#else
160 // With gcc 4.4 the tree vectorization optimiser can generate code
161 // that requires 16 byte alignment such as movdqa on x86.
162 return 16;
163#endif
ager@chromium.org236ad962008-09-25 09:45:57 +0000164}
165
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000166
167// We keep the lowest and highest addresses mapped as a quick way of
168// determining that pointers are outside the heap (used mostly in assertions
169// and verification). The estimate is conservative, ie, not all addresses in
170// 'allocated' space are actually allocated to our heap. The range is
171// [lowest, highest), inclusive on the low and and exclusive on the high end.
172static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
173static void* highest_ever_allocated = reinterpret_cast<void*>(0);
174
175
176static void UpdateAllocatedSpaceLimits(void* address, int size) {
177 lowest_ever_allocated = Min(lowest_ever_allocated, address);
178 highest_ever_allocated =
179 Max(highest_ever_allocated,
180 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
181}
182
183
184bool OS::IsOutsideAllocatedSpace(void* address) {
185 return address < lowest_ever_allocated || address >= highest_ever_allocated;
186}
187
188
189size_t OS::AllocateAlignment() {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000190 return sysconf(_SC_PAGESIZE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000191}
192
193
kasper.lund7276f142008-07-30 08:49:36 +0000194void* OS::Allocate(const size_t requested,
195 size_t* allocated,
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000196 bool is_executable) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000197 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000198 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
kasper.lund7276f142008-07-30 08:49:36 +0000199 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000200 if (mbase == MAP_FAILED) {
201 LOG(StringEvent("OS::Allocate", "mmap failed"));
202 return NULL;
203 }
204 *allocated = msize;
205 UpdateAllocatedSpaceLimits(mbase, msize);
206 return mbase;
207}
208
209
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000210void OS::Free(void* address, const size_t size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000211 // TODO(1240712): munmap has a return value which is ignored here.
ager@chromium.orga1645e22009-09-09 19:27:10 +0000212 int result = munmap(address, size);
213 USE(result);
214 ASSERT(result == 0);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000215}
216
217
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000218#ifdef ENABLE_HEAP_PROTECTION
219
220void OS::Protect(void* address, size_t size) {
221 // TODO(1240712): mprotect has a return value which is ignored here.
222 mprotect(address, size, PROT_READ);
223}
224
225
226void OS::Unprotect(void* address, size_t size, bool is_executable) {
227 // TODO(1240712): mprotect has a return value which is ignored here.
228 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
229 mprotect(address, size, prot);
230}
231
232#endif
233
234
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000235void OS::Sleep(int milliseconds) {
236 unsigned int ms = static_cast<unsigned int>(milliseconds);
237 usleep(1000 * ms);
238}
239
240
241void OS::Abort() {
242 // Redirect to std abort to signal abnormal program termination.
243 abort();
244}
245
246
kasper.lund7276f142008-07-30 08:49:36 +0000247void OS::DebugBreak() {
ager@chromium.org5ec48922009-05-05 07:25:34 +0000248// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
249// which is the architecture of generated code).
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000250#if defined(__arm__) || defined(__thumb__)
kasper.lund7276f142008-07-30 08:49:36 +0000251 asm("bkpt 0");
252#else
253 asm("int $3");
254#endif
255}
256
257
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000258class PosixMemoryMappedFile : public OS::MemoryMappedFile {
259 public:
260 PosixMemoryMappedFile(FILE* file, void* memory, int size)
261 : file_(file), memory_(memory), size_(size) { }
262 virtual ~PosixMemoryMappedFile();
263 virtual void* memory() { return memory_; }
264 private:
265 FILE* file_;
266 void* memory_;
267 int size_;
268};
269
270
271OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
272 void* initial) {
273 FILE* file = fopen(name, "w+");
274 if (file == NULL) return NULL;
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000275 int result = fwrite(initial, size, 1, file);
276 if (result < 1) {
277 fclose(file);
278 return NULL;
279 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000280 void* memory =
281 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
282 return new PosixMemoryMappedFile(file, memory, size);
283}
284
285
286PosixMemoryMappedFile::~PosixMemoryMappedFile() {
287 if (memory_) munmap(memory_, size_);
288 fclose(file_);
289}
290
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000291
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000292void OS::LogSharedLibraryAddresses() {
293#ifdef ENABLE_LOGGING_AND_PROFILING
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000294 // This function assumes that the layout of the file is as follows:
295 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
296 // If we encounter an unexpected situation we abort scanning further entries.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000297 FILE* fp = fopen("/proc/self/maps", "r");
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000298 if (fp == NULL) return;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000299
300 // Allocate enough room to be able to store a full file name.
301 const int kLibNameLen = FILENAME_MAX + 1;
302 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
303
304 // This loop will terminate once the scanning hits an EOF.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000305 while (true) {
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000306 uintptr_t start, end;
307 char attr_r, attr_w, attr_x, attr_p;
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000308 // Parse the addresses and permission bits at the beginning of the line.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000309 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
310 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 +0000311
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000312 int c;
313 if (attr_r == 'r' && attr_x == 'x') {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000314 // Found a readable and executable entry. Skip characters until we reach
315 // the beginning of the filename or the end of the line.
316 do {
317 c = getc(fp);
318 } while ((c != EOF) && (c != '\n') && (c != '/'));
319 if (c == EOF) break; // EOF: Was unexpected, just exit.
320
321 // Process the filename if found.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000322 if (c == '/') {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000323 ungetc(c, fp); // Push the '/' back into the stream to be read below.
324
325 // Read to the end of the line. Exit if the read fails.
326 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
327
328 // Drop the newline character read by fgets. We do not need to check
329 // for a zero-length string because we know that we at least read the
330 // '/' character.
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000331 lib_name[strlen(lib_name) - 1] = '\0';
332 } else {
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000333 // No library name found, just record the raw address range.
334 snprintf(lib_name, kLibNameLen,
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000335 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
336 }
337 LOG(SharedLibraryEvent(lib_name, start, end));
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000338 } else {
339 // Entry not describing executable data. Skip to end of line to setup
340 // reading the next entry.
341 do {
342 c = getc(fp);
343 } while ((c != EOF) && (c != '\n'));
344 if (c == EOF) break;
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000345 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000346 }
sgjesse@chromium.orgb9d7da12009-08-05 08:38:10 +0000347 free(lib_name);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +0000348 fclose(fp);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000349#endif
350}
351
352
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000353int OS::StackWalk(Vector<OS::StackFrame> frames) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000354 // backtrace is a glibc extension.
355#ifdef __GLIBC__
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000356 int frames_size = frames.length();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000357 void** addresses = NewArray<void*>(frames_size);
358
359 int frames_count = backtrace(addresses, frames_size);
360
361 char** symbols;
362 symbols = backtrace_symbols(addresses, frames_count);
363 if (symbols == NULL) {
364 DeleteArray(addresses);
365 return kStackWalkError;
366 }
367
368 for (int i = 0; i < frames_count; i++) {
369 frames[i].address = addresses[i];
370 // Format a text representation of the frame based on the information
371 // available.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000372 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
373 "%s",
374 symbols[i]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000375 // Make sure line termination is in place.
376 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
377 }
378
379 DeleteArray(addresses);
380 free(symbols);
381
382 return frames_count;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000383#else // ndef __GLIBC__
384 return 0;
385#endif // ndef __GLIBC__
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000386}
387
388
389// Constants used for mmap.
390static const int kMmapFd = -1;
391static const int kMmapFdOffset = 0;
392
393
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000394VirtualMemory::VirtualMemory(size_t size) {
395 address_ = mmap(NULL, size, PROT_NONE,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000396 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
397 kMmapFd, kMmapFdOffset);
398 size_ = size;
399}
400
401
402VirtualMemory::~VirtualMemory() {
403 if (IsReserved()) {
404 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
405 }
406}
407
408
409bool VirtualMemory::IsReserved() {
410 return address_ != MAP_FAILED;
411}
412
413
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000414bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
415 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
kasper.lund7276f142008-07-30 08:49:36 +0000416 if (MAP_FAILED == mmap(address, size, prot,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000417 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
418 kMmapFd, kMmapFdOffset)) {
419 return false;
420 }
421
422 UpdateAllocatedSpaceLimits(address, size);
423 return true;
424}
425
426
427bool VirtualMemory::Uncommit(void* address, size_t size) {
428 return mmap(address, size, PROT_NONE,
ager@chromium.orga1645e22009-09-09 19:27:10 +0000429 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000430 kMmapFd, kMmapFdOffset) != MAP_FAILED;
431}
432
433
434class ThreadHandle::PlatformData : public Malloced {
435 public:
436 explicit PlatformData(ThreadHandle::Kind kind) {
437 Initialize(kind);
438 }
439
440 void Initialize(ThreadHandle::Kind kind) {
441 switch (kind) {
442 case ThreadHandle::SELF: thread_ = pthread_self(); break;
443 case ThreadHandle::INVALID: thread_ = kNoThread; break;
444 }
445 }
ager@chromium.org41826e72009-03-30 13:30:57 +0000446
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000447 pthread_t thread_; // Thread handle for pthread.
448};
449
450
451ThreadHandle::ThreadHandle(Kind kind) {
452 data_ = new PlatformData(kind);
453}
454
455
456void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
457 data_->Initialize(kind);
458}
459
460
461ThreadHandle::~ThreadHandle() {
462 delete data_;
463}
464
465
466bool ThreadHandle::IsSelf() const {
467 return pthread_equal(data_->thread_, pthread_self());
468}
469
470
471bool ThreadHandle::IsValid() const {
472 return data_->thread_ != kNoThread;
473}
474
475
476Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
477}
478
479
480Thread::~Thread() {
481}
482
483
484static void* ThreadEntry(void* arg) {
485 Thread* thread = reinterpret_cast<Thread*>(arg);
486 // This is also initialized by the first argument to pthread_create() but we
487 // don't know which thread will run first (the original thread or the new
488 // one) so we initialize it here too.
489 thread->thread_handle_data()->thread_ = pthread_self();
490 ASSERT(thread->IsValid());
491 thread->Run();
492 return NULL;
493}
494
495
496void Thread::Start() {
497 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
498 ASSERT(IsValid());
499}
500
501
502void Thread::Join() {
503 pthread_join(thread_handle_data()->thread_, NULL);
504}
505
506
507Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
508 pthread_key_t key;
509 int result = pthread_key_create(&key, NULL);
510 USE(result);
511 ASSERT(result == 0);
512 return static_cast<LocalStorageKey>(key);
513}
514
515
516void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
517 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
518 int result = pthread_key_delete(pthread_key);
519 USE(result);
520 ASSERT(result == 0);
521}
522
523
524void* Thread::GetThreadLocal(LocalStorageKey key) {
525 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
526 return pthread_getspecific(pthread_key);
527}
528
529
530void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
531 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
532 pthread_setspecific(pthread_key, value);
533}
534
535
536void Thread::YieldCPU() {
537 sched_yield();
538}
539
540
541class LinuxMutex : public Mutex {
542 public:
543
544 LinuxMutex() {
545 pthread_mutexattr_t attrs;
546 int result = pthread_mutexattr_init(&attrs);
547 ASSERT(result == 0);
548 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
549 ASSERT(result == 0);
550 result = pthread_mutex_init(&mutex_, &attrs);
551 ASSERT(result == 0);
552 }
553
554 virtual ~LinuxMutex() { pthread_mutex_destroy(&mutex_); }
555
556 virtual int Lock() {
557 int result = pthread_mutex_lock(&mutex_);
558 return result;
559 }
560
561 virtual int Unlock() {
562 int result = pthread_mutex_unlock(&mutex_);
563 return result;
564 }
565
566 private:
567 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
568};
569
570
571Mutex* OS::CreateMutex() {
572 return new LinuxMutex();
573}
574
575
576class LinuxSemaphore : public Semaphore {
577 public:
578 explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); }
579 virtual ~LinuxSemaphore() { sem_destroy(&sem_); }
580
kasper.lund7276f142008-07-30 08:49:36 +0000581 virtual void Wait();
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000582 virtual bool Wait(int timeout);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000583 virtual void Signal() { sem_post(&sem_); }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000584 private:
585 sem_t sem_;
586};
587
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000588
kasper.lund7276f142008-07-30 08:49:36 +0000589void LinuxSemaphore::Wait() {
590 while (true) {
591 int result = sem_wait(&sem_);
592 if (result == 0) return; // Successfully got semaphore.
593 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
594 }
595}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000596
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000597
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000598#ifndef TIMEVAL_TO_TIMESPEC
599#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
600 (ts)->tv_sec = (tv)->tv_sec; \
601 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
602} while (false)
603#endif
604
605
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000606bool LinuxSemaphore::Wait(int timeout) {
607 const long kOneSecondMicros = 1000000; // NOLINT
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000608
609 // Split timeout into second and nanosecond parts.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000610 struct timeval delta;
611 delta.tv_usec = timeout % kOneSecondMicros;
612 delta.tv_sec = timeout / kOneSecondMicros;
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000613
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000614 struct timeval current_time;
615 // Get the current time.
616 if (gettimeofday(&current_time, NULL) == -1) {
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000617 return false;
618 }
619
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000620 // Calculate time for end of timeout.
621 struct timeval end_time;
622 timeradd(&current_time, &delta, &end_time);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000623
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000624 struct timespec ts;
625 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
ager@chromium.orgbb29dc92009-03-24 13:25:23 +0000626 // Wait for semaphore signalled or timeout.
627 while (true) {
628 int result = sem_timedwait(&sem_, &ts);
629 if (result == 0) return true; // Successfully got semaphore.
630 if (result > 0) {
631 // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
632 errno = result;
633 result = -1;
634 }
635 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
636 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
637 }
638}
639
640
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000641Semaphore* OS::CreateSemaphore(int count) {
642 return new LinuxSemaphore(count);
643}
644
ager@chromium.org381abbb2009-02-25 13:23:22 +0000645
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000646#ifdef ENABLE_LOGGING_AND_PROFILING
647
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000648static Sampler* active_sampler_ = NULL;
ager@chromium.orga1645e22009-09-09 19:27:10 +0000649static pthread_t vm_thread_ = 0;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000650
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000651
652#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
653// Android runs a fairly new Linux kernel, so signal info is there,
654// but the C library doesn't have the structs defined.
655
656struct sigcontext {
657 uint32_t trap_no;
658 uint32_t error_code;
659 uint32_t oldmask;
660 uint32_t gregs[16];
661 uint32_t arm_cpsr;
662 uint32_t fault_address;
663};
664typedef uint32_t __sigset_t;
665typedef struct sigcontext mcontext_t;
666typedef struct ucontext {
667 uint32_t uc_flags;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000668 struct ucontext* uc_link;
kasperl@chromium.orgacae3782009-04-11 09:17:08 +0000669 stack_t uc_stack;
670 mcontext_t uc_mcontext;
671 __sigset_t uc_sigmask;
672} ucontext_t;
673enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
674
675#endif
676
677
ager@chromium.orga1645e22009-09-09 19:27:10 +0000678// A function that determines if a signal handler is called in the context
679// of a VM thread.
680//
681// The problem is that SIGPROF signal can be delivered to an arbitrary thread
682// (see http://code.google.com/p/google-perftools/issues/detail?id=106#c2)
683// So, if the signal is being handled in the context of a non-VM thread,
684// it means that the VM thread is running, and trying to sample its stack can
685// cause a crash.
686static inline bool IsVmThread() {
687 // In the case of a single VM thread, this check is enough.
688 if (pthread_equal(pthread_self(), vm_thread_)) return true;
689 // If there are multiple threads that use VM, they must have a thread id
690 // stored in TLS. To verify that the thread is really executing VM,
691 // we check Top's data. Having that ThreadManager::RestoreThread first
692 // restores ThreadLocalTop from TLS, and only then erases the TLS value,
693 // reading Top::thread_id() should not be affected by races.
694 if (ThreadManager::HasId() && !ThreadManager::IsArchived() &&
695 ThreadManager::CurrentId() == Top::thread_id()) {
696 return true;
697 }
698 return false;
699}
700
701
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000702static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
703 USE(info);
704 if (signal != SIGPROF) return;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000705 if (active_sampler_ == NULL) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000706
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000707 TickSample sample;
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000708
709 // If profiling, we extract the current pc and sp.
710 if (active_sampler_->IsProfiling()) {
711 // Extracting the sample from the context is extremely machine dependent.
712 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
713 mcontext_t& mcontext = ucontext->uc_mcontext;
ager@chromium.org9085a012009-05-11 19:22:57 +0000714#if V8_HOST_ARCH_IA32
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000715 sample.pc = mcontext.gregs[REG_EIP];
716 sample.sp = mcontext.gregs[REG_ESP];
ager@chromium.org381abbb2009-02-25 13:23:22 +0000717 sample.fp = mcontext.gregs[REG_EBP];
ager@chromium.org9085a012009-05-11 19:22:57 +0000718#elif V8_HOST_ARCH_X64
719 sample.pc = mcontext.gregs[REG_RIP];
720 sample.sp = mcontext.gregs[REG_RSP];
721 sample.fp = mcontext.gregs[REG_RBP];
722#elif V8_HOST_ARCH_ARM
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000723// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
724#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
ager@chromium.org9085a012009-05-11 19:22:57 +0000725 sample.pc = mcontext.gregs[R15];
726 sample.sp = mcontext.gregs[R13];
727 sample.fp = mcontext.gregs[R11];
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000728#else
729 sample.pc = mcontext.arm_pc;
730 sample.sp = mcontext.arm_sp;
731 sample.fp = mcontext.arm_fp;
732#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000733#endif
ager@chromium.orga1645e22009-09-09 19:27:10 +0000734 if (IsVmThread())
735 active_sampler_->SampleStack(&sample);
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000736 }
737
738 // We always sample the VM state.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000739 sample.state = Logger::state();
740
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741 active_sampler_->Tick(&sample);
742}
743
744
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000745class Sampler::PlatformData : public Malloced {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000746 public:
747 PlatformData() {
748 signal_handler_installed_ = false;
749 }
750
751 bool signal_handler_installed_;
752 struct sigaction old_signal_handler_;
753 struct itimerval old_timer_value_;
754};
755
756
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000757Sampler::Sampler(int interval, bool profiling)
758 : interval_(interval), profiling_(profiling), active_(false) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000759 data_ = new PlatformData();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000760}
761
762
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000763Sampler::~Sampler() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000764 delete data_;
765}
766
767
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000768void Sampler::Start() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000769 // There can only be one active sampler at the time on POSIX
770 // platforms.
771 if (active_sampler_ != NULL) return;
772
ager@chromium.orga1645e22009-09-09 19:27:10 +0000773 vm_thread_ = pthread_self();
774
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000775 // Request profiling signals.
776 struct sigaction sa;
777 sa.sa_sigaction = ProfilerSignalHandler;
778 sigemptyset(&sa.sa_mask);
779 sa.sa_flags = SA_SIGINFO;
780 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
781 data_->signal_handler_installed_ = true;
782
783 // Set the itimer to generate a tick for each interval.
784 itimerval itimer;
785 itimer.it_interval.tv_sec = interval_ / 1000;
786 itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
787 itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
788 itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
789 setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
790
791 // Set this sampler as the active sampler.
792 active_sampler_ = this;
793 active_ = true;
794}
795
796
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +0000797void Sampler::Stop() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000798 // Restore old signal handler
799 if (data_->signal_handler_installed_) {
800 setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
801 sigaction(SIGPROF, &data_->old_signal_handler_, 0);
802 data_->signal_handler_installed_ = false;
803 }
804
805 // This sampler is no longer the active sampler.
806 active_sampler_ = NULL;
807 active_ = false;
808}
809
ager@chromium.orga1645e22009-09-09 19:27:10 +0000810
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000811#endif // ENABLE_LOGGING_AND_PROFILING
812
813} } // namespace v8::internal