1// Copyright 2006-2011 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 Cygwin goes here. For the POSIX comaptible parts 29// the implementation is in platform-posix.cc. 30 31#include <errno.h> 32#include <pthread.h> 33#include <semaphore.h> 34#include <stdarg.h> 35#include <strings.h> // index 36#include <sys/time.h> 37#include <sys/mman.h> // mmap & munmap 38#include <unistd.h> // sysconf 39 40#undef MAP_TYPE 41 42#include "v8.h" 43 44#include "platform.h" 45#include "v8threads.h" 46#include "vm-state-inl.h" 47#include "win32-headers.h" 48 49namespace v8 { 50namespace internal { 51 52// 0 is never a valid thread id 53static const pthread_t kNoThread = (pthread_t) 0; 54 55 56double ceiling(double x) { 57 return ceil(x); 58} 59 60 61static Mutex* limit_mutex = NULL; 62 63 64void OS::Setup() { 65 // Seed the random number generator. 66 // Convert the current time to a 64-bit integer first, before converting it 67 // to an unsigned. Going directly can cause an overflow and the seed to be 68 // set to all ones. The seed will be identical for different instances that 69 // call this setup code within the same millisecond. 70 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); 71 srandom(static_cast<unsigned int>(seed)); 72 limit_mutex = CreateMutex(); 73} 74 75 76uint64_t OS::CpuFeaturesImpliedByPlatform() { 77 return 0; // Nothing special about Cygwin. 78} 79 80 81int OS::ActivationFrameAlignment() { 82 // With gcc 4.4 the tree vectorization optimizer can generate code 83 // that requires 16 byte alignment such as movdqa on x86. 84 return 16; 85} 86 87 88void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) { 89 __asm__ __volatile__("" : : : "memory"); 90 // An x86 store acts as a release barrier. 91 *ptr = value; 92} 93 94const char* OS::LocalTimezone(double time) { 95 if (isnan(time)) return ""; 96 time_t tv = static_cast<time_t>(floor(time/msPerSecond)); 97 struct tm* t = localtime(&tv); 98 if (NULL == t) return ""; 99 return tzname[0]; // The location of the timezone string on Cygwin. 100} 101 102 103double OS::LocalTimeOffset() { 104 // On Cygwin, struct tm does not contain a tm_gmtoff field. 105 time_t utc = time(NULL); 106 ASSERT(utc != -1); 107 struct tm* loc = localtime(&utc); 108 ASSERT(loc != NULL); 109 // time - localtime includes any daylight savings offset, so subtract it. 110 return static_cast<double>((mktime(loc) - utc) * msPerSecond - 111 (loc->tm_isdst > 0 ? 3600 * msPerSecond : 0)); 112} 113 114 115// We keep the lowest and highest addresses mapped as a quick way of 116// determining that pointers are outside the heap (used mostly in assertions 117// and verification). The estimate is conservative, ie, not all addresses in 118// 'allocated' space are actually allocated to our heap. The range is 119// [lowest, highest), inclusive on the low and and exclusive on the high end. 120static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); 121static void* highest_ever_allocated = reinterpret_cast<void*>(0); 122 123 124static void UpdateAllocatedSpaceLimits(void* address, int size) { 125 ASSERT(limit_mutex != NULL); 126 ScopedLock lock(limit_mutex); 127 128 lowest_ever_allocated = Min(lowest_ever_allocated, address); 129 highest_ever_allocated = 130 Max(highest_ever_allocated, 131 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); 132} 133 134 135bool OS::IsOutsideAllocatedSpace(void* address) { 136 return address < lowest_ever_allocated || address >= highest_ever_allocated; 137} 138 139 140size_t OS::AllocateAlignment() { 141 return sysconf(_SC_PAGESIZE); 142} 143 144 145void* OS::Allocate(const size_t requested, 146 size_t* allocated, 147 bool is_executable) { 148 const size_t msize = RoundUp(requested, sysconf(_SC_PAGESIZE)); 149 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 150 void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 151 if (mbase == MAP_FAILED) { 152 LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed")); 153 return NULL; 154 } 155 *allocated = msize; 156 UpdateAllocatedSpaceLimits(mbase, msize); 157 return mbase; 158} 159 160 161void OS::Free(void* address, const size_t size) { 162 // TODO(1240712): munmap has a return value which is ignored here. 163 int result = munmap(address, size); 164 USE(result); 165 ASSERT(result == 0); 166} 167 168 169#ifdef ENABLE_HEAP_PROTECTION 170 171void OS::Protect(void* address, size_t size) { 172 // TODO(1240712): mprotect has a return value which is ignored here. 173 mprotect(address, size, PROT_READ); 174} 175 176 177void OS::Unprotect(void* address, size_t size, bool is_executable) { 178 // TODO(1240712): mprotect has a return value which is ignored here. 179 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); 180 mprotect(address, size, prot); 181} 182 183#endif 184 185 186void OS::Sleep(int milliseconds) { 187 unsigned int ms = static_cast<unsigned int>(milliseconds); 188 usleep(1000 * ms); 189} 190 191 192void OS::Abort() { 193 // Redirect to std abort to signal abnormal program termination. 194 abort(); 195} 196 197 198void OS::DebugBreak() { 199 asm("int $3"); 200} 201 202 203class PosixMemoryMappedFile : public OS::MemoryMappedFile { 204 public: 205 PosixMemoryMappedFile(FILE* file, void* memory, int size) 206 : file_(file), memory_(memory), size_(size) { } 207 virtual ~PosixMemoryMappedFile(); 208 virtual void* memory() { return memory_; } 209 virtual int size() { return size_; } 210 private: 211 FILE* file_; 212 void* memory_; 213 int size_; 214}; 215 216 217OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) { 218 FILE* file = fopen(name, "r+"); 219 if (file == NULL) return NULL; 220 221 fseek(file, 0, SEEK_END); 222 int size = ftell(file); 223 224 void* memory = 225 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 226 return new PosixMemoryMappedFile(file, memory, size); 227} 228 229 230OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size, 231 void* initial) { 232 FILE* file = fopen(name, "w+"); 233 if (file == NULL) return NULL; 234 int result = fwrite(initial, size, 1, file); 235 if (result < 1) { 236 fclose(file); 237 return NULL; 238 } 239 void* memory = 240 mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0); 241 return new PosixMemoryMappedFile(file, memory, size); 242} 243 244 245PosixMemoryMappedFile::~PosixMemoryMappedFile() { 246 if (memory_) munmap(memory_, size_); 247 fclose(file_); 248} 249 250 251void OS::LogSharedLibraryAddresses() { 252#ifdef ENABLE_LOGGING_AND_PROFILING 253 // This function assumes that the layout of the file is as follows: 254 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] 255 // If we encounter an unexpected situation we abort scanning further entries. 256 FILE* fp = fopen("/proc/self/maps", "r"); 257 if (fp == NULL) return; 258 259 // Allocate enough room to be able to store a full file name. 260 const int kLibNameLen = FILENAME_MAX + 1; 261 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); 262 263 i::Isolate* isolate = ISOLATE; 264 // This loop will terminate once the scanning hits an EOF. 265 while (true) { 266 uintptr_t start, end; 267 char attr_r, attr_w, attr_x, attr_p; 268 // Parse the addresses and permission bits at the beginning of the line. 269 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; 270 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; 271 272 int c; 273 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { 274 // Found a read-only executable entry. Skip characters until we reach 275 // the beginning of the filename or the end of the line. 276 do { 277 c = getc(fp); 278 } while ((c != EOF) && (c != '\n') && (c != '/')); 279 if (c == EOF) break; // EOF: Was unexpected, just exit. 280 281 // Process the filename if found. 282 if (c == '/') { 283 ungetc(c, fp); // Push the '/' back into the stream to be read below. 284 285 // Read to the end of the line. Exit if the read fails. 286 if (fgets(lib_name, kLibNameLen, fp) == NULL) break; 287 288 // Drop the newline character read by fgets. We do not need to check 289 // for a zero-length string because we know that we at least read the 290 // '/' character. 291 lib_name[strlen(lib_name) - 1] = '\0'; 292 } else { 293 // No library name found, just record the raw address range. 294 snprintf(lib_name, kLibNameLen, 295 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); 296 } 297 LOG(isolate, SharedLibraryEvent(lib_name, start, end)); 298 } else { 299 // Entry not describing executable data. Skip to end of line to setup 300 // reading the next entry. 301 do { 302 c = getc(fp); 303 } while ((c != EOF) && (c != '\n')); 304 if (c == EOF) break; 305 } 306 } 307 free(lib_name); 308 fclose(fp); 309#endif 310} 311 312 313void OS::SignalCodeMovingGC() { 314 // Nothing to do on Cygwin. 315} 316 317 318int OS::StackWalk(Vector<OS::StackFrame> frames) { 319 // Not supported on Cygwin. 320 return 0; 321} 322 323 324// The VirtualMemory implementation is taken from platform-win32.cc. 325// The mmap-based virtual memory implementation as it is used on most posix 326// platforms does not work well because Cygwin does not support MAP_FIXED. 327// This causes VirtualMemory::Commit to not always commit the memory region 328// specified. 329 330bool VirtualMemory::IsReserved() { 331 return address_ != NULL; 332} 333 334 335VirtualMemory::VirtualMemory(size_t size) { 336 address_ = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); 337 size_ = size; 338} 339 340 341VirtualMemory::~VirtualMemory() { 342 if (IsReserved()) { 343 if (0 == VirtualFree(address(), 0, MEM_RELEASE)) address_ = NULL; 344 } 345} 346 347 348bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { 349 int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 350 if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) { 351 return false; 352 } 353 354 UpdateAllocatedSpaceLimits(address, static_cast<int>(size)); 355 return true; 356} 357 358 359bool VirtualMemory::Uncommit(void* address, size_t size) { 360 ASSERT(IsReserved()); 361 return VirtualFree(address, size, MEM_DECOMMIT) != false; 362} 363 364 365class Thread::PlatformData : public Malloced { 366 public: 367 PlatformData() : thread_(kNoThread) {} 368 pthread_t thread_; // Thread handle for pthread. 369}; 370 371 372 373 374Thread::Thread(Isolate* isolate, const Options& options) 375 : data_(new PlatformData), 376 isolate_(isolate), 377 stack_size_(options.stack_size) { 378 set_name(options.name); 379} 380 381 382Thread::Thread(Isolate* isolate, const char* name) 383 : data_(new PlatformData), 384 isolate_(isolate), 385 stack_size_(0) { 386 set_name(name); 387} 388 389 390Thread::~Thread() { 391 delete data_; 392} 393 394 395static void* ThreadEntry(void* arg) { 396 Thread* thread = reinterpret_cast<Thread*>(arg); 397 // This is also initialized by the first argument to pthread_create() but we 398 // don't know which thread will run first (the original thread or the new 399 // one) so we initialize it here too. 400 thread->data()->thread_ = pthread_self(); 401 ASSERT(thread->data()->thread_ != kNoThread); 402 Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate()); 403 thread->Run(); 404 return NULL; 405} 406 407 408void Thread::set_name(const char* name) { 409 strncpy(name_, name, sizeof(name_)); 410 name_[sizeof(name_) - 1] = '\0'; 411} 412 413 414void Thread::Start() { 415 pthread_attr_t* attr_ptr = NULL; 416 pthread_attr_t attr; 417 if (stack_size_ > 0) { 418 pthread_attr_init(&attr); 419 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_)); 420 attr_ptr = &attr; 421 } 422 pthread_create(&data_->thread_, attr_ptr, ThreadEntry, this); 423 ASSERT(data_->thread_ != kNoThread); 424} 425 426 427void Thread::Join() { 428 pthread_join(data_->thread_, NULL); 429} 430 431 432static inline Thread::LocalStorageKey PthreadKeyToLocalKey( 433 pthread_key_t pthread_key) { 434 // We need to cast pthread_key_t to Thread::LocalStorageKey in two steps 435 // because pthread_key_t is a pointer type on Cygwin. This will probably not 436 // work on 64-bit platforms, but Cygwin doesn't support 64-bit anyway. 437 STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t)); 438 intptr_t ptr_key = reinterpret_cast<intptr_t>(pthread_key); 439 return static_cast<Thread::LocalStorageKey>(ptr_key); 440} 441 442 443static inline pthread_key_t LocalKeyToPthreadKey( 444 Thread::LocalStorageKey local_key) { 445 STATIC_ASSERT(sizeof(Thread::LocalStorageKey) == sizeof(pthread_key_t)); 446 intptr_t ptr_key = static_cast<intptr_t>(local_key); 447 return reinterpret_cast<pthread_key_t>(ptr_key); 448} 449 450 451Thread::LocalStorageKey Thread::CreateThreadLocalKey() { 452 pthread_key_t key; 453 int result = pthread_key_create(&key, NULL); 454 USE(result); 455 ASSERT(result == 0); 456 return PthreadKeyToLocalKey(key); 457} 458 459 460void Thread::DeleteThreadLocalKey(LocalStorageKey key) { 461 pthread_key_t pthread_key = LocalKeyToPthreadKey(key); 462 int result = pthread_key_delete(pthread_key); 463 USE(result); 464 ASSERT(result == 0); 465} 466 467 468void* Thread::GetThreadLocal(LocalStorageKey key) { 469 pthread_key_t pthread_key = LocalKeyToPthreadKey(key); 470 return pthread_getspecific(pthread_key); 471} 472 473 474void Thread::SetThreadLocal(LocalStorageKey key, void* value) { 475 pthread_key_t pthread_key = LocalKeyToPthreadKey(key); 476 pthread_setspecific(pthread_key, value); 477} 478 479 480void Thread::YieldCPU() { 481 sched_yield(); 482} 483 484 485class CygwinMutex : public Mutex { 486 public: 487 488 CygwinMutex() { 489 pthread_mutexattr_t attrs; 490 memset(&attrs, 0, sizeof(attrs)); 491 492 int result = pthread_mutexattr_init(&attrs); 493 ASSERT(result == 0); 494 result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE); 495 ASSERT(result == 0); 496 result = pthread_mutex_init(&mutex_, &attrs); 497 ASSERT(result == 0); 498 } 499 500 virtual ~CygwinMutex() { pthread_mutex_destroy(&mutex_); } 501 502 virtual int Lock() { 503 int result = pthread_mutex_lock(&mutex_); 504 return result; 505 } 506 507 virtual int Unlock() { 508 int result = pthread_mutex_unlock(&mutex_); 509 return result; 510 } 511 512 virtual bool TryLock() { 513 int result = pthread_mutex_trylock(&mutex_); 514 // Return false if the lock is busy and locking failed. 515 if (result == EBUSY) { 516 return false; 517 } 518 ASSERT(result == 0); // Verify no other errors. 519 return true; 520 } 521 522 private: 523 pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms. 524}; 525 526 527Mutex* OS::CreateMutex() { 528 return new CygwinMutex(); 529} 530 531 532class CygwinSemaphore : public Semaphore { 533 public: 534 explicit CygwinSemaphore(int count) { sem_init(&sem_, 0, count); } 535 virtual ~CygwinSemaphore() { sem_destroy(&sem_); } 536 537 virtual void Wait(); 538 virtual bool Wait(int timeout); 539 virtual void Signal() { sem_post(&sem_); } 540 private: 541 sem_t sem_; 542}; 543 544 545void CygwinSemaphore::Wait() { 546 while (true) { 547 int result = sem_wait(&sem_); 548 if (result == 0) return; // Successfully got semaphore. 549 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 550 } 551} 552 553 554#ifndef TIMEVAL_TO_TIMESPEC 555#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \ 556 (ts)->tv_sec = (tv)->tv_sec; \ 557 (ts)->tv_nsec = (tv)->tv_usec * 1000; \ 558} while (false) 559#endif 560 561 562bool CygwinSemaphore::Wait(int timeout) { 563 const long kOneSecondMicros = 1000000; // NOLINT 564 565 // Split timeout into second and nanosecond parts. 566 struct timeval delta; 567 delta.tv_usec = timeout % kOneSecondMicros; 568 delta.tv_sec = timeout / kOneSecondMicros; 569 570 struct timeval current_time; 571 // Get the current time. 572 if (gettimeofday(¤t_time, NULL) == -1) { 573 return false; 574 } 575 576 // Calculate time for end of timeout. 577 struct timeval end_time; 578 timeradd(¤t_time, &delta, &end_time); 579 580 struct timespec ts; 581 TIMEVAL_TO_TIMESPEC(&end_time, &ts); 582 // Wait for semaphore signalled or timeout. 583 while (true) { 584 int result = sem_timedwait(&sem_, &ts); 585 if (result == 0) return true; // Successfully got semaphore. 586 if (result == -1 && errno == ETIMEDOUT) return false; // Timeout. 587 CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup. 588 } 589} 590 591 592Semaphore* OS::CreateSemaphore(int count) { 593 return new CygwinSemaphore(count); 594} 595 596 597#ifdef ENABLE_LOGGING_AND_PROFILING 598 599// ---------------------------------------------------------------------------- 600// Cygwin profiler support. 601// 602// On Cygwin we use the same sampler implementation as on win32. 603 604class Sampler::PlatformData : public Malloced { 605 public: 606 // Get a handle to the calling thread. This is the thread that we are 607 // going to profile. We need to make a copy of the handle because we are 608 // going to use it in the sampler thread. Using GetThreadHandle() will 609 // not work in this case. We're using OpenThread because DuplicateHandle 610 // for some reason doesn't work in Chrome's sandbox. 611 PlatformData() : profiled_thread_(OpenThread(THREAD_GET_CONTEXT | 612 THREAD_SUSPEND_RESUME | 613 THREAD_QUERY_INFORMATION, 614 false, 615 GetCurrentThreadId())) {} 616 617 ~PlatformData() { 618 if (profiled_thread_ != NULL) { 619 CloseHandle(profiled_thread_); 620 profiled_thread_ = NULL; 621 } 622 } 623 624 HANDLE profiled_thread() { return profiled_thread_; } 625 626 private: 627 HANDLE profiled_thread_; 628}; 629 630 631class SamplerThread : public Thread { 632 public: 633 explicit SamplerThread(int interval) 634 : Thread(NULL, "SamplerThread"), 635 interval_(interval) {} 636 637 static void AddActiveSampler(Sampler* sampler) { 638 ScopedLock lock(mutex_); 639 SamplerRegistry::AddActiveSampler(sampler); 640 if (instance_ == NULL) { 641 instance_ = new SamplerThread(sampler->interval()); 642 instance_->Start(); 643 } else { 644 ASSERT(instance_->interval_ == sampler->interval()); 645 } 646 } 647 648 static void RemoveActiveSampler(Sampler* sampler) { 649 ScopedLock lock(mutex_); 650 SamplerRegistry::RemoveActiveSampler(sampler); 651 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) { 652 RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown(); 653 instance_->Join(); 654 delete instance_; 655 instance_ = NULL; 656 } 657 } 658 659 // Implement Thread::Run(). 660 virtual void Run() { 661 SamplerRegistry::State state; 662 while ((state = SamplerRegistry::GetState()) != 663 SamplerRegistry::HAS_NO_SAMPLERS) { 664 bool cpu_profiling_enabled = 665 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS); 666 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled(); 667 // When CPU profiling is enabled both JavaScript and C++ code is 668 // profiled. We must not suspend. 669 if (!cpu_profiling_enabled) { 670 if (rate_limiter_.SuspendIfNecessary()) continue; 671 } 672 if (cpu_profiling_enabled) { 673 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) { 674 return; 675 } 676 } 677 if (runtime_profiler_enabled) { 678 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) { 679 return; 680 } 681 } 682 OS::Sleep(interval_); 683 } 684 } 685 686 static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) { 687 if (!sampler->isolate()->IsInitialized()) return; 688 if (!sampler->IsProfiling()) return; 689 SamplerThread* sampler_thread = 690 reinterpret_cast<SamplerThread*>(raw_sampler_thread); 691 sampler_thread->SampleContext(sampler); 692 } 693 694 static void DoRuntimeProfile(Sampler* sampler, void* ignored) { 695 if (!sampler->isolate()->IsInitialized()) return; 696 sampler->isolate()->runtime_profiler()->NotifyTick(); 697 } 698 699 void SampleContext(Sampler* sampler) { 700 HANDLE profiled_thread = sampler->platform_data()->profiled_thread(); 701 if (profiled_thread == NULL) return; 702 703 // Context used for sampling the register state of the profiled thread. 704 CONTEXT context; 705 memset(&context, 0, sizeof(context)); 706 707 TickSample sample_obj; 708 TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate()); 709 if (sample == NULL) sample = &sample_obj; 710 711 static const DWORD kSuspendFailed = static_cast<DWORD>(-1); 712 if (SuspendThread(profiled_thread) == kSuspendFailed) return; 713 sample->state = sampler->isolate()->current_vm_state(); 714 715 context.ContextFlags = CONTEXT_FULL; 716 if (GetThreadContext(profiled_thread, &context) != 0) { 717#if V8_HOST_ARCH_X64 718 sample->pc = reinterpret_cast<Address>(context.Rip); 719 sample->sp = reinterpret_cast<Address>(context.Rsp); 720 sample->fp = reinterpret_cast<Address>(context.Rbp); 721#else 722 sample->pc = reinterpret_cast<Address>(context.Eip); 723 sample->sp = reinterpret_cast<Address>(context.Esp); 724 sample->fp = reinterpret_cast<Address>(context.Ebp); 725#endif 726 sampler->SampleStack(sample); 727 sampler->Tick(sample); 728 } 729 ResumeThread(profiled_thread); 730 } 731 732 const int interval_; 733 RuntimeProfilerRateLimiter rate_limiter_; 734 735 // Protects the process wide state below. 736 static Mutex* mutex_; 737 static SamplerThread* instance_; 738 739 DISALLOW_COPY_AND_ASSIGN(SamplerThread); 740}; 741 742 743Mutex* SamplerThread::mutex_ = OS::CreateMutex(); 744SamplerThread* SamplerThread::instance_ = NULL; 745 746 747Sampler::Sampler(Isolate* isolate, int interval) 748 : isolate_(isolate), 749 interval_(interval), 750 profiling_(false), 751 active_(false), 752 samples_taken_(0) { 753 data_ = new PlatformData; 754} 755 756 757Sampler::~Sampler() { 758 ASSERT(!IsActive()); 759 delete data_; 760} 761 762 763void Sampler::Start() { 764 ASSERT(!IsActive()); 765 SetActive(true); 766 SamplerThread::AddActiveSampler(this); 767} 768 769 770void Sampler::Stop() { 771 ASSERT(IsActive()); 772 SamplerThread::RemoveActiveSampler(this); 773 SetActive(false); 774} 775 776#endif // ENABLE_LOGGING_AND_PROFILING 777 778} } // namespace v8::internal 779 780