blob: cb8e919ea7a6e2e18bed8204675ba7ad0104e222 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Platform specific code for Linux goes here. For the POSIX comaptible parts
29// the implementation is in platform-posix.cc.
30
31#include <pthread.h>
32#include <semaphore.h>
33#include <signal.h>
34#include <sys/time.h>
35#include <sys/resource.h>
John Reck59135872010-11-02 12:39:01 -070036#include <sys/syscall.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000037#include <sys/types.h>
38#include <stdlib.h>
39
40// Ubuntu Dapper requires memory pages to be marked as
41// executable. Otherwise, OS raises an exception when executing code
42// in that page.
43#include <sys/types.h> // mmap & munmap
44#include <sys/mman.h> // mmap & munmap
45#include <sys/stat.h> // open
46#include <fcntl.h> // open
47#include <unistd.h> // sysconf
48#ifdef __GLIBC__
49#include <execinfo.h> // backtrace, backtrace_symbols
50#endif // def __GLIBC__
51#include <strings.h> // index
52#include <errno.h>
53#include <stdarg.h>
54
55#undef MAP_TYPE
56
57#include "v8.h"
58
59#include "platform.h"
60#include "top.h"
61#include "v8threads.h"
62
63
64namespace v8 {
65namespace internal {
66
67// 0 is never a valid thread id on Linux since tids and pids share a
68// name space and pid 0 is reserved (see man 2 kill).
69static const pthread_t kNoThread = (pthread_t) 0;
70
71
72double ceiling(double x) {
73 return ceil(x);
74}
75
76
77void OS::Setup() {
78 // Seed the random number generator.
79 // Convert the current time to a 64-bit integer first, before converting it
80 // to an unsigned. Going directly can cause an overflow and the seed to be
81 // set to all ones. The seed will be identical for different instances that
82 // call this setup code within the same millisecond.
83 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
84 srandom(static_cast<unsigned int>(seed));
85}
86
87
Steve Blockd0582a62009-12-15 09:54:21 +000088uint64_t OS::CpuFeaturesImpliedByPlatform() {
89#if (defined(__VFP_FP__) && !defined(__SOFTFP__))
90 // Here gcc is telling us that we are on an ARM and gcc is assuming that we
91 // have VFP3 instructions. If gcc can assume it then so can we.
92 return 1u << VFP3;
Andrei Popescu31002712010-02-23 13:46:05 +000093#elif CAN_USE_ARMV7_INSTRUCTIONS
94 return 1u << ARMv7;
Steve Blockd0582a62009-12-15 09:54:21 +000095#else
96 return 0; // Linux runs on anything.
97#endif
Steve Blocka7e24c12009-10-30 11:49:00 +000098}
99
100
Steve Blockd0582a62009-12-15 09:54:21 +0000101#ifdef __arm__
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800102static bool CPUInfoContainsString(const char * search_string) {
Steve Blockd0582a62009-12-15 09:54:21 +0000103 const char* file_name = "/proc/cpuinfo";
Steve Blockd0582a62009-12-15 09:54:21 +0000104 // This is written as a straight shot one pass parser
105 // and not using STL string and ifstream because,
106 // on Linux, it's reading from a (non-mmap-able)
107 // character special device.
Steve Blockd0582a62009-12-15 09:54:21 +0000108 FILE* f = NULL;
109 const char* what = search_string;
110
111 if (NULL == (f = fopen(file_name, "r")))
112 return false;
113
114 int k;
115 while (EOF != (k = fgetc(f))) {
116 if (k == *what) {
117 ++what;
118 while ((*what != '\0') && (*what == fgetc(f))) {
119 ++what;
120 }
121 if (*what == '\0') {
122 fclose(f);
123 return true;
124 } else {
125 what = search_string;
126 }
127 }
128 }
129 fclose(f);
130
131 // Did not find string in the proc file.
132 return false;
133}
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800134
135bool OS::ArmCpuHasFeature(CpuFeature feature) {
136 const int max_items = 2;
137 const char* search_strings[max_items] = { NULL, NULL };
138 int search_items = 0;
139 // Simple detection of VFP at runtime for Linux.
140 // It is based on /proc/cpuinfo, which reveals hardware configuration
141 // to user-space applications. According to ARM (mid 2009), no similar
142 // facility is universally available on the ARM architectures,
143 // so it's up to individual OSes to provide such.
144 switch (feature) {
145 case VFP3:
146 search_strings[0] = "vfpv3";
147 // Some old kernels will report vfp for A8, not vfpv3, so we check for
148 // A8 explicitely. The cpuinfo file report the CPU Part which for Cortex
149 // A8 is 0xc08.
150 search_strings[1] = "0xc08";
151 search_items = 2;
152 ASSERT(search_items <= max_items);
153 break;
154 case ARMv7:
155 search_strings[0] = "ARMv7" ;
156 search_items = 1;
157 ASSERT(search_items <= max_items);
158 break;
159 default:
160 UNREACHABLE();
161 }
162
163 for (int i = 0; i < search_items; ++i) {
164 if (CPUInfoContainsString(search_strings[i])) {
165 return true;
166 }
167 }
168
169 return false;
170}
Steve Blockd0582a62009-12-15 09:54:21 +0000171#endif // def __arm__
172
173
Steve Blocka7e24c12009-10-30 11:49:00 +0000174int OS::ActivationFrameAlignment() {
175#ifdef V8_TARGET_ARCH_ARM
176 // On EABI ARM targets this is required for fp correctness in the
177 // runtime system.
178 return 8;
Andrei Popescu31002712010-02-23 13:46:05 +0000179#elif V8_TARGET_ARCH_MIPS
180 return 8;
181#endif
Steve Block6ded16b2010-05-10 14:33:55 +0100182 // With gcc 4.4 the tree vectorization optimizer can generate code
Steve Blocka7e24c12009-10-30 11:49:00 +0000183 // that requires 16 byte alignment such as movdqa on x86.
184 return 16;
Steve Blocka7e24c12009-10-30 11:49:00 +0000185}
186
187
Leon Clarkef7060e22010-06-03 12:02:55 +0100188#ifdef V8_TARGET_ARCH_ARM
189// 0xffff0fa0 is the hard coded address of a function provided by
190// the kernel which implements a memory barrier. On older
191// ARM architecture revisions (pre-v6) this may be implemented using
192// a syscall. This address is stable, and in active use (hard coded)
193// by at least glibc-2.7 and the Android C library.
194typedef void (*LinuxKernelMemoryBarrierFunc)(void);
195LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
196 (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
197#endif
198
199void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100200#if defined(V8_TARGET_ARCH_ARM) && defined(__arm__)
201 // Only use on ARM hardware.
Leon Clarkef7060e22010-06-03 12:02:55 +0100202 pLinuxKernelMemoryBarrier();
203#else
204 __asm__ __volatile__("" : : : "memory");
205 // An x86 store acts as a release barrier.
206#endif
207 *ptr = value;
208}
209
210
Leon Clarked91b9f72010-01-27 17:25:45 +0000211const char* OS::LocalTimezone(double time) {
212 if (isnan(time)) return "";
213 time_t tv = static_cast<time_t>(floor(time/msPerSecond));
214 struct tm* t = localtime(&tv);
215 if (NULL == t) return "";
216 return t->tm_zone;
217}
218
219
220double OS::LocalTimeOffset() {
221 time_t tv = time(NULL);
222 struct tm* t = localtime(&tv);
223 // tm_gmtoff includes any daylight savings offset, so subtract it.
224 return static_cast<double>(t->tm_gmtoff * msPerSecond -
225 (t->tm_isdst > 0 ? 3600 * msPerSecond : 0));
226}
227
228
Steve Blocka7e24c12009-10-30 11:49:00 +0000229// We keep the lowest and highest addresses mapped as a quick way of
230// determining that pointers are outside the heap (used mostly in assertions
231// and verification). The estimate is conservative, ie, not all addresses in
232// 'allocated' space are actually allocated to our heap. The range is
233// [lowest, highest), inclusive on the low and and exclusive on the high end.
234static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
235static void* highest_ever_allocated = reinterpret_cast<void*>(0);
236
237
238static void UpdateAllocatedSpaceLimits(void* address, int size) {
239 lowest_ever_allocated = Min(lowest_ever_allocated, address);
240 highest_ever_allocated =
241 Max(highest_ever_allocated,
242 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size));
243}
244
245
246bool OS::IsOutsideAllocatedSpace(void* address) {
247 return address < lowest_ever_allocated || address >= highest_ever_allocated;
248}
249
250
251size_t OS::AllocateAlignment() {
252 return sysconf(_SC_PAGESIZE);
253}
254
255
256void* OS::Allocate(const size_t requested,
257 size_t* allocated,
258 bool is_executable) {
Ben Murdochbb769b22010-08-11 14:56:33 +0100259 // TODO(805): Port randomization of allocated executable memory to Linux.
Steve Blocka7e24c12009-10-30 11:49:00 +0000260 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE));
261 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
262 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
263 if (mbase == MAP_FAILED) {
264 LOG(StringEvent("OS::Allocate", "mmap failed"));
265 return NULL;
266 }
267 *allocated = msize;
268 UpdateAllocatedSpaceLimits(mbase, msize);
269 return mbase;
270}
271
272
273void OS::Free(void* address, const size_t size) {
274 // TODO(1240712): munmap has a return value which is ignored here.
275 int result = munmap(address, size);
276 USE(result);
277 ASSERT(result == 0);
278}
279
280
281#ifdef ENABLE_HEAP_PROTECTION
282
283void OS::Protect(void* address, size_t size) {
284 // TODO(1240712): mprotect has a return value which is ignored here.
285 mprotect(address, size, PROT_READ);
286}
287
288
289void OS::Unprotect(void* address, size_t size, bool is_executable) {
290 // TODO(1240712): mprotect has a return value which is ignored here.
291 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
292 mprotect(address, size, prot);
293}
294
295#endif
296
297
298void OS::Sleep(int milliseconds) {
299 unsigned int ms = static_cast<unsigned int>(milliseconds);
300 usleep(1000 * ms);
301}
302
303
304void OS::Abort() {
305 // Redirect to std abort to signal abnormal program termination.
306 abort();
307}
308
309
310void OS::DebugBreak() {
311// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
312// which is the architecture of generated code).
Iain Merrick75681382010-08-19 15:07:18 +0100313#if (defined(__arm__) || defined(__thumb__))
314# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
Steve Blocka7e24c12009-10-30 11:49:00 +0000315 asm("bkpt 0");
Iain Merrick75681382010-08-19 15:07:18 +0100316# endif
Andrei Popescu31002712010-02-23 13:46:05 +0000317#elif defined(__mips__)
318 asm("break");
Steve Blocka7e24c12009-10-30 11:49:00 +0000319#else
320 asm("int $3");
321#endif
322}
323
324
325class PosixMemoryMappedFile : public OS::MemoryMappedFile {
326 public:
327 PosixMemoryMappedFile(FILE* file, void* memory, int size)
328 : file_(file), memory_(memory), size_(size) { }
329 virtual ~PosixMemoryMappedFile();
330 virtual void* memory() { return memory_; }
331 private:
332 FILE* file_;
333 void* memory_;
334 int size_;
335};
336
337
338OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
339 void* initial) {
340 FILE* file = fopen(name, "w+");
341 if (file == NULL) return NULL;
342 int result = fwrite(initial, size, 1, file);
343 if (result < 1) {
344 fclose(file);
345 return NULL;
346 }
347 void* memory =
348 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
349 return new PosixMemoryMappedFile(file, memory, size);
350}
351
352
353PosixMemoryMappedFile::~PosixMemoryMappedFile() {
354 if (memory_) munmap(memory_, size_);
355 fclose(file_);
356}
357
358
359void OS::LogSharedLibraryAddresses() {
360#ifdef ENABLE_LOGGING_AND_PROFILING
361 // This function assumes that the layout of the file is as follows:
362 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name]
363 // If we encounter an unexpected situation we abort scanning further entries.
Steve Blockd0582a62009-12-15 09:54:21 +0000364 FILE* fp = fopen("/proc/self/maps", "r");
Steve Blocka7e24c12009-10-30 11:49:00 +0000365 if (fp == NULL) return;
366
367 // Allocate enough room to be able to store a full file name.
368 const int kLibNameLen = FILENAME_MAX + 1;
369 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen));
370
371 // This loop will terminate once the scanning hits an EOF.
372 while (true) {
373 uintptr_t start, end;
374 char attr_r, attr_w, attr_x, attr_p;
375 // Parse the addresses and permission bits at the beginning of the line.
376 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break;
377 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break;
378
379 int c;
Steve Block6ded16b2010-05-10 14:33:55 +0100380 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') {
381 // Found a read-only executable entry. Skip characters until we reach
Steve Blocka7e24c12009-10-30 11:49:00 +0000382 // the beginning of the filename or the end of the line.
383 do {
384 c = getc(fp);
385 } while ((c != EOF) && (c != '\n') && (c != '/'));
386 if (c == EOF) break; // EOF: Was unexpected, just exit.
387
388 // Process the filename if found.
389 if (c == '/') {
390 ungetc(c, fp); // Push the '/' back into the stream to be read below.
391
392 // Read to the end of the line. Exit if the read fails.
393 if (fgets(lib_name, kLibNameLen, fp) == NULL) break;
394
395 // Drop the newline character read by fgets. We do not need to check
396 // for a zero-length string because we know that we at least read the
397 // '/' character.
398 lib_name[strlen(lib_name) - 1] = '\0';
399 } else {
400 // No library name found, just record the raw address range.
401 snprintf(lib_name, kLibNameLen,
402 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end);
403 }
404 LOG(SharedLibraryEvent(lib_name, start, end));
405 } else {
406 // Entry not describing executable data. Skip to end of line to setup
407 // reading the next entry.
408 do {
409 c = getc(fp);
410 } while ((c != EOF) && (c != '\n'));
411 if (c == EOF) break;
412 }
413 }
414 free(lib_name);
415 fclose(fp);
416#endif
417}
418
419
Ben Murdochf87a2032010-10-22 12:50:53 +0100420static const char kGCFakeMmap[] = "/tmp/__v8_gc__";
421
422
423void OS::SignalCodeMovingGC() {
424#ifdef ENABLE_LOGGING_AND_PROFILING
425 // Support for ll_prof.py.
426 //
427 // The Linux profiler built into the kernel logs all mmap's with
428 // PROT_EXEC so that analysis tools can properly attribute ticks. We
429 // do a mmap with a name known by ll_prof.py and immediately munmap
430 // it. This injects a GC marker into the stream of events generated
431 // by the kernel and allows us to synchronize V8 code log and the
432 // kernel log.
433 int size = sysconf(_SC_PAGESIZE);
434 FILE* f = fopen(kGCFakeMmap, "w+");
435 void* addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE,
436 fileno(f), 0);
437 ASSERT(addr != MAP_FAILED);
438 munmap(addr, size);
439 fclose(f);
440#endif
441}
442
443
Steve Blocka7e24c12009-10-30 11:49:00 +0000444int OS::StackWalk(Vector<OS::StackFrame> frames) {
445 // backtrace is a glibc extension.
446#ifdef __GLIBC__
447 int frames_size = frames.length();
Kristian Monsen25f61362010-05-21 11:50:48 +0100448 ScopedVector<void*> addresses(frames_size);
Steve Blocka7e24c12009-10-30 11:49:00 +0000449
Kristian Monsen25f61362010-05-21 11:50:48 +0100450 int frames_count = backtrace(addresses.start(), frames_size);
Steve Blocka7e24c12009-10-30 11:49:00 +0000451
Kristian Monsen25f61362010-05-21 11:50:48 +0100452 char** symbols = backtrace_symbols(addresses.start(), frames_count);
Steve Blocka7e24c12009-10-30 11:49:00 +0000453 if (symbols == NULL) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000454 return kStackWalkError;
455 }
456
457 for (int i = 0; i < frames_count; i++) {
458 frames[i].address = addresses[i];
459 // Format a text representation of the frame based on the information
460 // available.
461 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen),
462 "%s",
463 symbols[i]);
464 // Make sure line termination is in place.
465 frames[i].text[kStackWalkMaxTextLen - 1] = '\0';
466 }
467
Steve Blocka7e24c12009-10-30 11:49:00 +0000468 free(symbols);
469
470 return frames_count;
471#else // ndef __GLIBC__
472 return 0;
473#endif // ndef __GLIBC__
474}
475
476
477// Constants used for mmap.
478static const int kMmapFd = -1;
479static const int kMmapFdOffset = 0;
480
481
482VirtualMemory::VirtualMemory(size_t size) {
483 address_ = mmap(NULL, size, PROT_NONE,
484 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
485 kMmapFd, kMmapFdOffset);
486 size_ = size;
487}
488
489
490VirtualMemory::~VirtualMemory() {
491 if (IsReserved()) {
492 if (0 == munmap(address(), size())) address_ = MAP_FAILED;
493 }
494}
495
496
497bool VirtualMemory::IsReserved() {
498 return address_ != MAP_FAILED;
499}
500
501
502bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
503 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
504 if (MAP_FAILED == mmap(address, size, prot,
505 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
506 kMmapFd, kMmapFdOffset)) {
507 return false;
508 }
509
510 UpdateAllocatedSpaceLimits(address, size);
511 return true;
512}
513
514
515bool VirtualMemory::Uncommit(void* address, size_t size) {
516 return mmap(address, size, PROT_NONE,
517 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED,
518 kMmapFd, kMmapFdOffset) != MAP_FAILED;
519}
520
521
522class ThreadHandle::PlatformData : public Malloced {
523 public:
524 explicit PlatformData(ThreadHandle::Kind kind) {
525 Initialize(kind);
526 }
527
528 void Initialize(ThreadHandle::Kind kind) {
529 switch (kind) {
530 case ThreadHandle::SELF: thread_ = pthread_self(); break;
531 case ThreadHandle::INVALID: thread_ = kNoThread; break;
532 }
533 }
534
535 pthread_t thread_; // Thread handle for pthread.
536};
537
538
539ThreadHandle::ThreadHandle(Kind kind) {
540 data_ = new PlatformData(kind);
541}
542
543
544void ThreadHandle::Initialize(ThreadHandle::Kind kind) {
545 data_->Initialize(kind);
546}
547
548
549ThreadHandle::~ThreadHandle() {
550 delete data_;
551}
552
553
554bool ThreadHandle::IsSelf() const {
555 return pthread_equal(data_->thread_, pthread_self());
556}
557
558
559bool ThreadHandle::IsValid() const {
560 return data_->thread_ != kNoThread;
561}
562
563
564Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) {
565}
566
567
568Thread::~Thread() {
569}
570
571
572static void* ThreadEntry(void* arg) {
573 Thread* thread = reinterpret_cast<Thread*>(arg);
574 // This is also initialized by the first argument to pthread_create() but we
575 // don't know which thread will run first (the original thread or the new
576 // one) so we initialize it here too.
577 thread->thread_handle_data()->thread_ = pthread_self();
578 ASSERT(thread->IsValid());
579 thread->Run();
580 return NULL;
581}
582
583
584void Thread::Start() {
585 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this);
586 ASSERT(IsValid());
587}
588
589
590void Thread::Join() {
591 pthread_join(thread_handle_data()->thread_, NULL);
592}
593
594
595Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
596 pthread_key_t key;
597 int result = pthread_key_create(&key, NULL);
598 USE(result);
599 ASSERT(result == 0);
600 return static_cast<LocalStorageKey>(key);
601}
602
603
604void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
605 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
606 int result = pthread_key_delete(pthread_key);
607 USE(result);
608 ASSERT(result == 0);
609}
610
611
612void* Thread::GetThreadLocal(LocalStorageKey key) {
613 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
614 return pthread_getspecific(pthread_key);
615}
616
617
618void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
619 pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
620 pthread_setspecific(pthread_key, value);
621}
622
623
624void Thread::YieldCPU() {
625 sched_yield();
626}
627
628
629class LinuxMutex : public Mutex {
630 public:
631
632 LinuxMutex() {
633 pthread_mutexattr_t attrs;
634 int result = pthread_mutexattr_init(&attrs);
635 ASSERT(result == 0);
636 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
637 ASSERT(result == 0);
638 result = pthread_mutex_init(&mutex_, &attrs);
639 ASSERT(result == 0);
640 }
641
642 virtual ~LinuxMutex() { pthread_mutex_destroy(&mutex_); }
643
644 virtual int Lock() {
645 int result = pthread_mutex_lock(&mutex_);
646 return result;
647 }
648
649 virtual int Unlock() {
650 int result = pthread_mutex_unlock(&mutex_);
651 return result;
652 }
653
654 private:
655 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
656};
657
658
659Mutex* OS::CreateMutex() {
660 return new LinuxMutex();
661}
662
663
664class LinuxSemaphore : public Semaphore {
665 public:
666 explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); }
667 virtual ~LinuxSemaphore() { sem_destroy(&sem_); }
668
669 virtual void Wait();
670 virtual bool Wait(int timeout);
671 virtual void Signal() { sem_post(&sem_); }
672 private:
673 sem_t sem_;
674};
675
676
677void LinuxSemaphore::Wait() {
678 while (true) {
679 int result = sem_wait(&sem_);
680 if (result == 0) return; // Successfully got semaphore.
681 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
682 }
683}
684
685
686#ifndef TIMEVAL_TO_TIMESPEC
687#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
688 (ts)->tv_sec = (tv)->tv_sec; \
689 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
690} while (false)
691#endif
692
693
694bool LinuxSemaphore::Wait(int timeout) {
695 const long kOneSecondMicros = 1000000; // NOLINT
696
697 // Split timeout into second and nanosecond parts.
698 struct timeval delta;
699 delta.tv_usec = timeout % kOneSecondMicros;
700 delta.tv_sec = timeout / kOneSecondMicros;
701
702 struct timeval current_time;
703 // Get the current time.
704 if (gettimeofday(&current_time, NULL) == -1) {
705 return false;
706 }
707
708 // Calculate time for end of timeout.
709 struct timeval end_time;
710 timeradd(&current_time, &delta, &end_time);
711
712 struct timespec ts;
713 TIMEVAL_TO_TIMESPEC(&end_time, &ts);
714 // Wait for semaphore signalled or timeout.
715 while (true) {
716 int result = sem_timedwait(&sem_, &ts);
717 if (result == 0) return true; // Successfully got semaphore.
718 if (result > 0) {
719 // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1.
720 errno = result;
721 result = -1;
722 }
723 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
724 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
725 }
726}
727
728
729Semaphore* OS::CreateSemaphore(int count) {
730 return new LinuxSemaphore(count);
731}
732
733
734#ifdef ENABLE_LOGGING_AND_PROFILING
735
736static Sampler* active_sampler_ = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000737
738
739#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__))
740// Android runs a fairly new Linux kernel, so signal info is there,
741// but the C library doesn't have the structs defined.
742
743struct sigcontext {
744 uint32_t trap_no;
745 uint32_t error_code;
746 uint32_t oldmask;
747 uint32_t gregs[16];
748 uint32_t arm_cpsr;
749 uint32_t fault_address;
750};
751typedef uint32_t __sigset_t;
752typedef struct sigcontext mcontext_t;
753typedef struct ucontext {
754 uint32_t uc_flags;
Steve Blockd0582a62009-12-15 09:54:21 +0000755 struct ucontext* uc_link;
Steve Blocka7e24c12009-10-30 11:49:00 +0000756 stack_t uc_stack;
757 mcontext_t uc_mcontext;
758 __sigset_t uc_sigmask;
759} ucontext_t;
760enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11};
761
762#endif
763
764
Steve Blocka7e24c12009-10-30 11:49:00 +0000765static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000766#ifndef V8_HOST_ARCH_MIPS
Steve Blocka7e24c12009-10-30 11:49:00 +0000767 USE(info);
768 if (signal != SIGPROF) return;
769 if (active_sampler_ == NULL) return;
770
Steve Block6ded16b2010-05-10 14:33:55 +0100771 TickSample sample_obj;
772 TickSample* sample = CpuProfiler::TickSampleEvent();
773 if (sample == NULL) sample = &sample_obj;
Steve Blocka7e24c12009-10-30 11:49:00 +0000774
Steve Block6ded16b2010-05-10 14:33:55 +0100775 // We always sample the VM state.
776 sample->state = VMState::current_state();
Ben Murdochf87a2032010-10-22 12:50:53 +0100777
Steve Blocka7e24c12009-10-30 11:49:00 +0000778 // If profiling, we extract the current pc and sp.
779 if (active_sampler_->IsProfiling()) {
780 // Extracting the sample from the context is extremely machine dependent.
781 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
782 mcontext_t& mcontext = ucontext->uc_mcontext;
783#if V8_HOST_ARCH_IA32
Steve Block6ded16b2010-05-10 14:33:55 +0100784 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
785 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
786 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
Steve Blocka7e24c12009-10-30 11:49:00 +0000787#elif V8_HOST_ARCH_X64
Steve Block6ded16b2010-05-10 14:33:55 +0100788 sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
789 sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
790 sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
Steve Blocka7e24c12009-10-30 11:49:00 +0000791#elif V8_HOST_ARCH_ARM
792// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
793#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
Steve Block6ded16b2010-05-10 14:33:55 +0100794 sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
795 sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
796 sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
Steve Blocka7e24c12009-10-30 11:49:00 +0000797#else
Steve Block6ded16b2010-05-10 14:33:55 +0100798 sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
799 sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
800 sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000801#endif
Andrei Popescu31002712010-02-23 13:46:05 +0000802#elif V8_HOST_ARCH_MIPS
803 // Implement this on MIPS.
804 UNIMPLEMENTED();
Steve Blocka7e24c12009-10-30 11:49:00 +0000805#endif
Ben Murdochf87a2032010-10-22 12:50:53 +0100806 active_sampler_->SampleStack(sample);
Steve Blocka7e24c12009-10-30 11:49:00 +0000807 }
808
Steve Block6ded16b2010-05-10 14:33:55 +0100809 active_sampler_->Tick(sample);
Andrei Popescu402d9372010-02-26 13:31:12 +0000810#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000811}
812
813
814class Sampler::PlatformData : public Malloced {
815 public:
John Reck59135872010-11-02 12:39:01 -0700816 explicit PlatformData(Sampler* sampler)
817 : sampler_(sampler),
818 signal_handler_installed_(false),
819 vm_tgid_(getpid()),
820 // Glibc doesn't provide a wrapper for gettid(2).
821 vm_tid_(syscall(SYS_gettid)),
822 signal_sender_launched_(false) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000823 }
824
John Reck59135872010-11-02 12:39:01 -0700825 void SignalSender() {
826 while (sampler_->IsActive()) {
827 // Glibc doesn't provide a wrapper for tgkill(2).
828 syscall(SYS_tgkill, vm_tgid_, vm_tid_, SIGPROF);
829 // Convert ms to us and subtract 100 us to compensate delays
830 // occuring during signal delivery.
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800831 const useconds_t interval = sampler_->interval_ * 1000 - 100;
832 int result = usleep(interval);
833#ifdef DEBUG
834 if (result != 0 && errno != EINTR) {
835 fprintf(stderr,
836 "SignalSender usleep error; interval = %u, errno = %d\n",
837 interval,
838 errno);
839 ASSERT(result == 0 || errno == EINTR);
840 }
841#endif
John Reck59135872010-11-02 12:39:01 -0700842 USE(result);
843 }
844 }
845
846 Sampler* sampler_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000847 bool signal_handler_installed_;
848 struct sigaction old_signal_handler_;
John Reck59135872010-11-02 12:39:01 -0700849 int vm_tgid_;
850 int vm_tid_;
851 bool signal_sender_launched_;
852 pthread_t signal_sender_thread_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000853};
854
855
John Reck59135872010-11-02 12:39:01 -0700856static void* SenderEntry(void* arg) {
857 Sampler::PlatformData* data =
858 reinterpret_cast<Sampler::PlatformData*>(arg);
859 data->SignalSender();
860 return 0;
861}
862
863
Steve Blocka7e24c12009-10-30 11:49:00 +0000864Sampler::Sampler(int interval, bool profiling)
Ben Murdochf87a2032010-10-22 12:50:53 +0100865 : interval_(interval),
866 profiling_(profiling),
867 synchronous_(profiling),
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800868 active_(false),
869 samples_taken_(0) {
John Reck59135872010-11-02 12:39:01 -0700870 data_ = new PlatformData(this);
Steve Blocka7e24c12009-10-30 11:49:00 +0000871}
872
873
874Sampler::~Sampler() {
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800875 ASSERT(!data_->signal_sender_launched_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000876 delete data_;
877}
878
879
880void Sampler::Start() {
881 // There can only be one active sampler at the time on POSIX
882 // platforms.
883 if (active_sampler_ != NULL) return;
884
Steve Blocka7e24c12009-10-30 11:49:00 +0000885 // Request profiling signals.
886 struct sigaction sa;
887 sa.sa_sigaction = ProfilerSignalHandler;
888 sigemptyset(&sa.sa_mask);
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800889 sa.sa_flags = SA_RESTART | SA_SIGINFO;
Andrei Popescu402d9372010-02-26 13:31:12 +0000890 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000891 data_->signal_handler_installed_ = true;
892
John Reck59135872010-11-02 12:39:01 -0700893 // Start a thread that sends SIGPROF signal to VM thread.
894 // Sending the signal ourselves instead of relying on itimer provides
895 // much better accuracy.
896 active_ = true;
897 if (pthread_create(
898 &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
899 data_->signal_sender_launched_ = true;
900 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000901
902 // Set this sampler as the active sampler.
903 active_sampler_ = this;
Steve Blocka7e24c12009-10-30 11:49:00 +0000904}
905
906
907void Sampler::Stop() {
John Reck59135872010-11-02 12:39:01 -0700908 active_ = false;
909
910 // Wait for signal sender termination (it will exit after setting
911 // active_ to false).
912 if (data_->signal_sender_launched_) {
913 pthread_join(data_->signal_sender_thread_, NULL);
914 data_->signal_sender_launched_ = false;
915 }
916
Steve Blocka7e24c12009-10-30 11:49:00 +0000917 // Restore old signal handler
918 if (data_->signal_handler_installed_) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000919 sigaction(SIGPROF, &data_->old_signal_handler_, 0);
920 data_->signal_handler_installed_ = false;
921 }
922
923 // This sampler is no longer the active sampler.
924 active_sampler_ = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000925}
926
927
928#endif // ENABLE_LOGGING_AND_PROFILING
929
930} } // namespace v8::internal