platform-linux.cc revision d0582a6c46733687d045e4188a1bcd0123c758a1
1// 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> 36#include <sys/types.h> 37#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 43#include <sys/mman.h> // mmap & munmap 44#include <sys/stat.h> // open 45#include <fcntl.h> // open 46#include <unistd.h> // sysconf 47#ifdef __GLIBC__ 48#include <execinfo.h> // backtrace, backtrace_symbols 49#endif // def __GLIBC__ 50#include <strings.h> // index 51#include <errno.h> 52#include <stdarg.h> 53 54#undef MAP_TYPE 55 56#include "v8.h" 57 58#include "platform.h" 59#include "top.h" 60#include "v8threads.h" 61 62 63namespace v8 { 64namespace internal { 65 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. 78 // 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)); 84} 85 86 87uint64_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 98#ifdef __arm__ 99bool OS::ArmCpuHasFeature(CpuFeature feature) { 100 const char* search_string = NULL; 101 const char* file_name = "/proc/cpuinfo"; 102 // Simple detection of VFP at runtime for Linux. 103 // It is based on /proc/cpuinfo, which reveals hardware configuration 104 // to user-space applications. According to ARM (mid 2009), no similar 105 // facility is universally available on the ARM architectures, 106 // so it's up to individual OSes to provide such. 107 // 108 // This is written as a straight shot one pass parser 109 // and not using STL string and ifstream because, 110 // on Linux, it's reading from a (non-mmap-able) 111 // character special device. 112 switch (feature) { 113 case VFP3: 114 search_string = "vfp"; 115 break; 116 default: 117 UNREACHABLE(); 118 } 119 120 FILE* f = NULL; 121 const char* what = search_string; 122 123 if (NULL == (f = fopen(file_name, "r"))) 124 return false; 125 126 int k; 127 while (EOF != (k = fgetc(f))) { 128 if (k == *what) { 129 ++what; 130 while ((*what != '\0') && (*what == fgetc(f))) { 131 ++what; 132 } 133 if (*what == '\0') { 134 fclose(f); 135 return true; 136 } else { 137 what = search_string; 138 } 139 } 140 } 141 fclose(f); 142 143 // Did not find string in the proc file. 144 return false; 145} 146#endif // def __arm__ 147 148 149int OS::ActivationFrameAlignment() { 150#ifdef V8_TARGET_ARCH_ARM 151 // On EABI ARM targets this is required for fp correctness in the 152 // runtime system. 153 return 8; 154#else 155 // With gcc 4.4 the tree vectorization optimiser can generate code 156 // that requires 16 byte alignment such as movdqa on x86. 157 return 16; 158#endif 159} 160 161 162// We keep the lowest and highest addresses mapped as a quick way of 163// determining that pointers are outside the heap (used mostly in assertions 164// and verification). The estimate is conservative, ie, not all addresses in 165// 'allocated' space are actually allocated to our heap. The range is 166// [lowest, highest), inclusive on the low and and exclusive on the high end. 167static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); 168static void* highest_ever_allocated = reinterpret_cast<void*>(0); 169 170 171static void UpdateAllocatedSpaceLimits(void* address, int size) { 172 lowest_ever_allocated = Min(lowest_ever_allocated, address); 173 highest_ever_allocated = 174 Max(highest_ever_allocated, 175 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); 176} 177 178 179bool OS::IsOutsideAllocatedSpace(void* address) { 180 return address < lowest_ever_allocated || address >= highest_ever_allocated; 181} 182 183 184size_t OS::AllocateAlignment() { 185 return sysconf(_SC_PAGESIZE); 186} 187 188 189void* OS::Allocate(const size_t requested, 190 size_t* allocated, 191 bool is_executable) { 192 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); 193 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 194 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 195 if (mbase == MAP_FAILED) { 196 LOG(StringEvent("OS::Allocate", "mmap failed")); 197 return NULL; 198 } 199 *allocated = msize; 200 UpdateAllocatedSpaceLimits(mbase, msize); 201 return mbase; 202} 203 204 205void OS::Free(void* address, const size_t size) { 206 // TODO(1240712): munmap has a return value which is ignored here. 207 int result = munmap(address, size); 208 USE(result); 209 ASSERT(result == 0); 210} 211 212 213#ifdef ENABLE_HEAP_PROTECTION 214 215void OS::Protect(void* address, size_t size) { 216 // TODO(1240712): mprotect has a return value which is ignored here. 217 mprotect(address, size, PROT_READ); 218} 219 220 221void OS::Unprotect(void* address, size_t size, bool is_executable) { 222 // TODO(1240712): mprotect has a return value which is ignored here. 223 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 224 mprotect(address, size, prot); 225} 226 227#endif 228 229 230void OS::Sleep(int milliseconds) { 231 unsigned int ms = static_cast<unsigned int>(milliseconds); 232 usleep(1000 * ms); 233} 234 235 236void OS::Abort() { 237 // Redirect to std abort to signal abnormal program termination. 238 abort(); 239} 240 241 242void OS::DebugBreak() { 243// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x, 244// which is the architecture of generated code). 245#if defined(__arm__) || defined(__thumb__) 246 asm("bkpt 0"); 247#else 248 asm("int $3"); 249#endif 250} 251 252 253class PosixMemoryMappedFile : public OS::MemoryMappedFile { 254 public: 255 PosixMemoryMappedFile(FILE* file, void* memory, int size) 256 : file_(file), memory_(memory), size_(size) { } 257 virtual ~PosixMemoryMappedFile(); 258 virtual void* memory() { return memory_; } 259 private: 260 FILE* file_; 261 void* memory_; 262 int size_; 263}; 264 265 266OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 267 void* initial) { 268 FILE* file = fopen(name, "w+"); 269 if (file == NULL) return NULL; 270 int result = fwrite(initial, size, 1, file); 271 if (result < 1) { 272 fclose(file); 273 return NULL; 274 } 275 void* memory = 276 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 277 return new PosixMemoryMappedFile(file, memory, size); 278} 279 280 281PosixMemoryMappedFile::~PosixMemoryMappedFile() { 282 if (memory_) munmap(memory_, size_); 283 fclose(file_); 284} 285 286 287void OS::LogSharedLibraryAddresses() { 288#ifdef ENABLE_LOGGING_AND_PROFILING 289 // This function assumes that the layout of the file is as follows: 290 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] 291 // If we encounter an unexpected situation we abort scanning further entries. 292 FILE* fp = fopen("/proc/self/maps", "r"); 293 if (fp == NULL) return; 294 295 // Allocate enough room to be able to store a full file name. 296 const int kLibNameLen = FILENAME_MAX + 1; 297 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); 298 299 // This loop will terminate once the scanning hits an EOF. 300 while (true) { 301 uintptr_t start, end; 302 char attr_r, attr_w, attr_x, attr_p; 303 // Parse the addresses and permission bits at the beginning of the line. 304 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; 305 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; 306 307 int c; 308 if (attr_r == 'r' && attr_x == 'x') { 309 // Found a readable and executable entry. Skip characters until we reach 310 // the beginning of the filename or the end of the line. 311 do { 312 c = getc(fp); 313 } while ((c != EOF) && (c != '\n') && (c != '/')); 314 if (c == EOF) break; // EOF: Was unexpected, just exit. 315 316 // Process the filename if found. 317 if (c == '/') { 318 ungetc(c, fp); // Push the '/' back into the stream to be read below. 319 320 // Read to the end of the line. Exit if the read fails. 321 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; 322 323 // Drop the newline character read by fgets. We do not need to check 324 // for a zero-length string because we know that we at least read the 325 // '/' character. 326 lib_name[strlen(lib_name) - 1] = '\0'; 327 } else { 328 // No library name found, just record the raw address range. 329 snprintf(lib_name, kLibNameLen, 330 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); 331 } 332 LOG(SharedLibraryEvent(lib_name, start, end)); 333 } else { 334 // Entry not describing executable data. Skip to end of line to setup 335 // reading the next entry. 336 do { 337 c = getc(fp); 338 } while ((c != EOF) && (c != '\n')); 339 if (c == EOF) break; 340 } 341 } 342 free(lib_name); 343 fclose(fp); 344#endif 345} 346 347 348int OS::StackWalk(Vector<OS::StackFrame> frames) { 349 // backtrace is a glibc extension. 350#ifdef __GLIBC__ 351 int frames_size = frames.length(); 352 void** addresses = NewArray<void*>(frames_size); 353 354 int frames_count = backtrace(addresses, frames_size); 355 356 char** symbols; 357 symbols = backtrace_symbols(addresses, frames_count); 358 if (symbols == NULL) { 359 DeleteArray(addresses); 360 return kStackWalkError; 361 } 362 363 for (int i = 0; i < frames_count; i++) { 364 frames[i].address = addresses[i]; 365 // Format a text representation of the frame based on the information 366 // available. 367 SNPrintF(MutableCStrVector(frames[i].text, kStackWalkMaxTextLen), 368 "%s", 369 symbols[i]); 370 // Make sure line termination is in place. 371 frames[i].text[kStackWalkMaxTextLen - 1] = '\0'; 372 } 373 374 DeleteArray(addresses); 375 free(symbols); 376 377 return frames_count; 378#else // ndef __GLIBC__ 379 return 0; 380#endif // ndef __GLIBC__ 381} 382 383 384// Constants used for mmap. 385static const int kMmapFd = -1; 386static const int kMmapFdOffset = 0; 387 388 389VirtualMemory::VirtualMemory(size_t size) { 390 address_ = mmap(NULL, size, PROT_NONE, 391 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, 392 kMmapFd, kMmapFdOffset); 393 size_ = size; 394} 395 396 397VirtualMemory::~VirtualMemory() { 398 if (IsReserved()) { 399 if (0 == munmap(address(), size())) address_ = MAP_FAILED; 400 } 401} 402 403 404bool VirtualMemory::IsReserved() { 405 return address_ != MAP_FAILED; 406} 407 408 409bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 410 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 411 if (MAP_FAILED == mmap(address, size, prot, 412 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 413 kMmapFd, kMmapFdOffset)) { 414 return false; 415 } 416 417 UpdateAllocatedSpaceLimits(address, size); 418 return true; 419} 420 421 422bool VirtualMemory::Uncommit(void* address, size_t size) { 423 return mmap(address, size, PROT_NONE, 424 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_FIXED, 425 kMmapFd, kMmapFdOffset) != MAP_FAILED; 426} 427 428 429class ThreadHandle::PlatformData : public Malloced { 430 public: 431 explicit PlatformData(ThreadHandle::Kind kind) { 432 Initialize(kind); 433 } 434 435 void Initialize(ThreadHandle::Kind kind) { 436 switch (kind) { 437 case ThreadHandle::SELF: thread_ = pthread_self(); break; 438 case ThreadHandle::INVALID: thread_ = kNoThread; break; 439 } 440 } 441 442 pthread_t thread_; // Thread handle for pthread. 443}; 444 445 446ThreadHandle::ThreadHandle(Kind kind) { 447 data_ = new PlatformData(kind); 448} 449 450 451void ThreadHandle::Initialize(ThreadHandle::Kind kind) { 452 data_->Initialize(kind); 453} 454 455 456ThreadHandle::~ThreadHandle() { 457 delete data_; 458} 459 460 461bool ThreadHandle::IsSelf() const { 462 return pthread_equal(data_->thread_, pthread_self()); 463} 464 465 466bool ThreadHandle::IsValid() const { 467 return data_->thread_ != kNoThread; 468} 469 470 471Thread::Thread() : ThreadHandle(ThreadHandle::INVALID) { 472} 473 474 475Thread::~Thread() { 476} 477 478 479static void* ThreadEntry(void* arg) { 480 Thread* thread = reinterpret_cast<Thread*>(arg); 481 // This is also initialized by the first argument to pthread_create() but we 482 // don't know which thread will run first (the original thread or the new 483 // one) so we initialize it here too. 484 thread->thread_handle_data()->thread_ = pthread_self(); 485 ASSERT(thread->IsValid()); 486 thread->Run(); 487 return NULL; 488} 489 490 491void Thread::Start() { 492 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this); 493 ASSERT(IsValid()); 494} 495 496 497void Thread::Join() { 498 pthread_join(thread_handle_data()->thread_, NULL); 499} 500 501 502Thread::LocalStorageKey Thread::CreateThreadLocalKey() { 503 pthread_key_t key; 504 int result = pthread_key_create(&key, NULL); 505 USE(result); 506 ASSERT(result == 0); 507 return static_cast<LocalStorageKey>(key); 508} 509 510 511void Thread::DeleteThreadLocalKey(LocalStorageKey key) { 512 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); 513 int result = pthread_key_delete(pthread_key); 514 USE(result); 515 ASSERT(result == 0); 516} 517 518 519void* Thread::GetThreadLocal(LocalStorageKey key) { 520 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); 521 return pthread_getspecific(pthread_key); 522} 523 524 525void Thread::SetThreadLocal(LocalStorageKey key, void* value) { 526 pthread_key_t pthread_key = static_cast<pthread_key_t>(key); 527 pthread_setspecific(pthread_key, value); 528} 529 530 531void Thread::YieldCPU() { 532 sched_yield(); 533} 534 535 536class LinuxMutex : public Mutex { 537 public: 538 539 LinuxMutex() { 540 pthread_mutexattr_t attrs; 541 int result = pthread_mutexattr_init(&attrs); 542 ASSERT(result == 0); 543 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE); 544 ASSERT(result == 0); 545 result = pthread_mutex_init(&mutex_, &attrs); 546 ASSERT(result == 0); 547 } 548 549 virtual ~LinuxMutex() { pthread_mutex_destroy(&mutex_); } 550 551 virtual int Lock() { 552 int result = pthread_mutex_lock(&mutex_); 553 return result; 554 } 555 556 virtual int Unlock() { 557 int result = pthread_mutex_unlock(&mutex_); 558 return result; 559 } 560 561 private: 562 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms. 563}; 564 565 566Mutex* OS::CreateMutex() { 567 return new LinuxMutex(); 568} 569 570 571class LinuxSemaphore : public Semaphore { 572 public: 573 explicit LinuxSemaphore(int count) { sem_init(&sem_, 0, count); } 574 virtual ~LinuxSemaphore() { sem_destroy(&sem_); } 575 576 virtual void Wait(); 577 virtual bool Wait(int timeout); 578 virtual void Signal() { sem_post(&sem_); } 579 private: 580 sem_t sem_; 581}; 582 583 584void LinuxSemaphore::Wait() { 585 while (true) { 586 int result = sem_wait(&sem_); 587 if (result == 0) return; // Successfully got semaphore. 588 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 589 } 590} 591 592 593#ifndef TIMEVAL_TO_TIMESPEC 594#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ 595 (ts)->tv_sec = (tv)->tv_sec; \ 596 (ts)->tv_nsec = (tv)->tv_usec * 1000; \ 597} while (false) 598#endif 599 600 601bool LinuxSemaphore::Wait(int timeout) { 602 const long kOneSecondMicros = 1000000; // NOLINT 603 604 // Split timeout into second and nanosecond parts. 605 struct timeval delta; 606 delta.tv_usec = timeout % kOneSecondMicros; 607 delta.tv_sec = timeout / kOneSecondMicros; 608 609 struct timeval current_time; 610 // Get the current time. 611 if (gettimeofday(¤t_time, NULL) == -1) { 612 return false; 613 } 614 615 // Calculate time for end of timeout. 616 struct timeval end_time; 617 timeradd(¤t_time, &delta, &end_time); 618 619 struct timespec ts; 620 TIMEVAL_TO_TIMESPEC(&end_time, &ts); 621 // Wait for semaphore signalled or timeout. 622 while (true) { 623 int result = sem_timedwait(&sem_, &ts); 624 if (result == 0) return true; // Successfully got semaphore. 625 if (result > 0) { 626 // For glibc prior to 2.3.4 sem_timedwait returns the error instead of -1. 627 errno = result; 628 result = -1; 629 } 630 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout. 631 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 632 } 633} 634 635 636Semaphore* OS::CreateSemaphore(int count) { 637 return new LinuxSemaphore(count); 638} 639 640 641#ifdef ENABLE_LOGGING_AND_PROFILING 642 643static Sampler* active_sampler_ = NULL; 644static pthread_t vm_thread_ = 0; 645 646 647#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__)) 648// Android runs a fairly new Linux kernel, so signal info is there, 649// but the C library doesn't have the structs defined. 650 651struct sigcontext { 652 uint32_t trap_no; 653 uint32_t error_code; 654 uint32_t oldmask; 655 uint32_t gregs[16]; 656 uint32_t arm_cpsr; 657 uint32_t fault_address; 658}; 659typedef uint32_t __sigset_t; 660typedef struct sigcontext mcontext_t; 661typedef struct ucontext { 662 uint32_t uc_flags; 663 struct ucontext* uc_link; 664 stack_t uc_stack; 665 mcontext_t uc_mcontext; 666 __sigset_t uc_sigmask; 667} ucontext_t; 668enum ArmRegisters {R15 = 15, R13 = 13, R11 = 11}; 669 670#endif 671 672 673// A function that determines if a signal handler is called in the context 674// of a VM thread. 675// 676// The problem is that SIGPROF signal can be delivered to an arbitrary thread 677// (see http://code.google.com/p/google-perftools/issues/detail?id=106#c2) 678// So, if the signal is being handled in the context of a non-VM thread, 679// it means that the VM thread is running, and trying to sample its stack can 680// cause a crash. 681static inline bool IsVmThread() { 682 // In the case of a single VM thread, this check is enough. 683 if (pthread_equal(pthread_self(), vm_thread_)) return true; 684 // If there are multiple threads that use VM, they must have a thread id 685 // stored in TLS. To verify that the thread is really executing VM, 686 // we check Top's data. Having that ThreadManager::RestoreThread first 687 // restores ThreadLocalTop from TLS, and only then erases the TLS value, 688 // reading Top::thread_id() should not be affected by races. 689 if (ThreadManager::HasId() && !ThreadManager::IsArchived() && 690 ThreadManager::CurrentId() == Top::thread_id()) { 691 return true; 692 } 693 return false; 694} 695 696 697static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { 698 USE(info); 699 if (signal != SIGPROF) return; 700 if (active_sampler_ == NULL) return; 701 702 TickSample sample; 703 704 // If profiling, we extract the current pc and sp. 705 if (active_sampler_->IsProfiling()) { 706 // Extracting the sample from the context is extremely machine dependent. 707 ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); 708 mcontext_t& mcontext = ucontext->uc_mcontext; 709#if V8_HOST_ARCH_IA32 710 sample.pc = mcontext.gregs[REG_EIP]; 711 sample.sp = mcontext.gregs[REG_ESP]; 712 sample.fp = mcontext.gregs[REG_EBP]; 713#elif V8_HOST_ARCH_X64 714 sample.pc = mcontext.gregs[REG_RIP]; 715 sample.sp = mcontext.gregs[REG_RSP]; 716 sample.fp = mcontext.gregs[REG_RBP]; 717#elif V8_HOST_ARCH_ARM 718// An undefined macro evaluates to 0, so this applies to Android's Bionic also. 719#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) 720 sample.pc = mcontext.gregs[R15]; 721 sample.sp = mcontext.gregs[R13]; 722 sample.fp = mcontext.gregs[R11]; 723#else 724 sample.pc = mcontext.arm_pc; 725 sample.sp = mcontext.arm_sp; 726 sample.fp = mcontext.arm_fp; 727#endif 728#endif 729 if (IsVmThread()) 730 active_sampler_->SampleStack(&sample); 731 } 732 733 // We always sample the VM state. 734 sample.state = Logger::state(); 735 736 active_sampler_->Tick(&sample); 737} 738 739 740class Sampler::PlatformData : public Malloced { 741 public: 742 PlatformData() { 743 signal_handler_installed_ = false; 744 } 745 746 bool signal_handler_installed_; 747 struct sigaction old_signal_handler_; 748 struct itimerval old_timer_value_; 749}; 750 751 752Sampler::Sampler(int interval, bool profiling) 753 : interval_(interval), profiling_(profiling), active_(false) { 754 data_ = new PlatformData(); 755} 756 757 758Sampler::~Sampler() { 759 delete data_; 760} 761 762 763void Sampler::Start() { 764 // There can only be one active sampler at the time on POSIX 765 // platforms. 766 if (active_sampler_ != NULL) return; 767 768 vm_thread_ = pthread_self(); 769 770 // Request profiling signals. 771 struct sigaction sa; 772 sa.sa_sigaction = ProfilerSignalHandler; 773 sigemptyset(&sa.sa_mask); 774 sa.sa_flags = SA_SIGINFO; 775 if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; 776 data_->signal_handler_installed_ = true; 777 778 // Set the itimer to generate a tick for each interval. 779 itimerval itimer; 780 itimer.it_interval.tv_sec = interval_ / 1000; 781 itimer.it_interval.tv_usec = (interval_ % 1000) * 1000; 782 itimer.it_value.tv_sec = itimer.it_interval.tv_sec; 783 itimer.it_value.tv_usec = itimer.it_interval.tv_usec; 784 setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_); 785 786 // Set this sampler as the active sampler. 787 active_sampler_ = this; 788 active_ = true; 789} 790 791 792void Sampler::Stop() { 793 // Restore old signal handler 794 if (data_->signal_handler_installed_) { 795 setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL); 796 sigaction(SIGPROF, &data_->old_signal_handler_, 0); 797 data_->signal_handler_installed_ = false; 798 } 799 800 // This sampler is no longer the active sampler. 801 active_sampler_ = NULL; 802 active_ = false; 803} 804 805 806#endif // ENABLE_LOGGING_AND_PROFILING 807 808} } // namespace v8::internal 809