| OLD | NEW |
| 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2008 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 50 #include <stdarg.h> | 50 #include <stdarg.h> |
| 51 #include <stdlib.h> | 51 #include <stdlib.h> |
| 52 | 52 |
| 53 #include <errno.h> | 53 #include <errno.h> |
| 54 | 54 |
| 55 #undef MAP_TYPE | 55 #undef MAP_TYPE |
| 56 | 56 |
| 57 #include "v8.h" | 57 #include "v8.h" |
| 58 | 58 |
| 59 #include "platform.h" | 59 #include "platform.h" |
| 60 #include "vm-state-inl.h" |
| 60 | 61 |
| 61 // Manually define these here as weak imports, rather than including execinfo.h. | 62 // Manually define these here as weak imports, rather than including execinfo.h. |
| 62 // This lets us launch on 10.4 which does not have these calls. | 63 // This lets us launch on 10.4 which does not have these calls. |
| 63 extern "C" { | 64 extern "C" { |
| 64 extern int backtrace(void**, int) __attribute__((weak_import)); | 65 extern int backtrace(void**, int) __attribute__((weak_import)); |
| 65 extern char** backtrace_symbols(void* const*, int) | 66 extern char** backtrace_symbols(void* const*, int) |
| 66 __attribute__((weak_import)); | 67 __attribute__((weak_import)); |
| 67 extern void backtrace_symbols_fd(void* const*, int, int) | 68 extern void backtrace_symbols_fd(void* const*, int, int) |
| 68 __attribute__((weak_import)); | 69 __attribute__((weak_import)); |
| 69 } | 70 } |
| (...skipping 416 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 486 class MacOSMutex : public Mutex { | 487 class MacOSMutex : public Mutex { |
| 487 public: | 488 public: |
| 488 | 489 |
| 489 MacOSMutex() { | 490 MacOSMutex() { |
| 490 pthread_mutexattr_t attr; | 491 pthread_mutexattr_t attr; |
| 491 pthread_mutexattr_init(&attr); | 492 pthread_mutexattr_init(&attr); |
| 492 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); | 493 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); |
| 493 pthread_mutex_init(&mutex_, &attr); | 494 pthread_mutex_init(&mutex_, &attr); |
| 494 } | 495 } |
| 495 | 496 |
| 496 ~MacOSMutex() { pthread_mutex_destroy(&mutex_); } | 497 virtual ~MacOSMutex() { pthread_mutex_destroy(&mutex_); } |
| 497 | 498 |
| 498 int Lock() { return pthread_mutex_lock(&mutex_); } | 499 virtual int Lock() { return pthread_mutex_lock(&mutex_); } |
| 500 virtual int Unlock() { return pthread_mutex_unlock(&mutex_); } |
| 499 | 501 |
| 500 int Unlock() { return pthread_mutex_unlock(&mutex_); } | 502 virtual bool TryLock() { |
| 503 int result = pthread_mutex_trylock(&mutex_); |
| 504 // Return false if the lock is busy and locking failed. |
| 505 if (result == EBUSY) { |
| 506 return false; |
| 507 } |
| 508 ASSERT(result == 0); // Verify no other errors. |
| 509 return true; |
| 510 } |
| 501 | 511 |
| 502 private: | 512 private: |
| 503 pthread_mutex_t mutex_; | 513 pthread_mutex_t mutex_; |
| 504 }; | 514 }; |
| 505 | 515 |
| 506 | 516 |
| 507 Mutex* OS::CreateMutex() { | 517 Mutex* OS::CreateMutex() { |
| 508 return new MacOSMutex(); | 518 return new MacOSMutex(); |
| 509 } | 519 } |
| 510 | 520 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 543 | 553 |
| 544 Semaphore* OS::CreateSemaphore(int count) { | 554 Semaphore* OS::CreateSemaphore(int count) { |
| 545 return new MacOSSemaphore(count); | 555 return new MacOSSemaphore(count); |
| 546 } | 556 } |
| 547 | 557 |
| 548 | 558 |
| 549 #ifdef ENABLE_LOGGING_AND_PROFILING | 559 #ifdef ENABLE_LOGGING_AND_PROFILING |
| 550 | 560 |
| 551 class Sampler::PlatformData : public Malloced { | 561 class Sampler::PlatformData : public Malloced { |
| 552 public: | 562 public: |
| 553 explicit PlatformData(Sampler* sampler) | 563 PlatformData() : profiled_thread_(mach_thread_self()) {} |
| 554 : sampler_(sampler), | 564 |
| 555 task_self_(mach_task_self()), | 565 ~PlatformData() { |
| 556 profiled_thread_(0), | 566 // Deallocate Mach port for thread. |
| 557 sampler_thread_(0) { | 567 mach_port_deallocate(mach_task_self(), profiled_thread_); |
| 558 } | 568 } |
| 559 | 569 |
| 560 Sampler* sampler_; | 570 thread_act_t profiled_thread() { return profiled_thread_; } |
| 571 |
| 572 private: |
| 561 // Note: for profiled_thread_ Mach primitives are used instead of PThread's | 573 // Note: for profiled_thread_ Mach primitives are used instead of PThread's |
| 562 // because the latter doesn't provide thread manipulation primitives required. | 574 // because the latter doesn't provide thread manipulation primitives required. |
| 563 // For details, consult "Mac OS X Internals" book, Section 7.3. | 575 // For details, consult "Mac OS X Internals" book, Section 7.3. |
| 564 mach_port_t task_self_; | |
| 565 thread_act_t profiled_thread_; | 576 thread_act_t profiled_thread_; |
| 566 pthread_t sampler_thread_; | 577 }; |
| 567 | 578 |
| 568 // Sampler thread handler. | 579 class SamplerThread : public Thread { |
| 569 void Runner() { | 580 public: |
| 570 // Loop until the sampler is disengaged, keeping the specified | 581 explicit SamplerThread(int interval) : Thread(NULL), interval_(interval) {} |
| 571 // sampling frequency. | |
| 572 for ( ; sampler_->IsActive(); OS::Sleep(sampler_->interval_)) { | |
| 573 TickSample sample_obj; | |
| 574 TickSample* sample = CpuProfiler::TickSampleEvent(); | |
| 575 if (sample == NULL) sample = &sample_obj; | |
| 576 | 582 |
| 577 // If the sampler runs in sync with the JS thread, we try to | 583 static void AddActiveSampler(Sampler* sampler) { |
| 578 // suspend it. If we fail, we skip the current sample. | 584 ScopedLock lock(mutex_); |
| 579 if (sampler_->IsSynchronous()) { | 585 SamplerRegistry::AddActiveSampler(sampler); |
| 580 if (KERN_SUCCESS != thread_suspend(profiled_thread_)) continue; | 586 if (instance_ == NULL) { |
| 587 instance_ = new SamplerThread(sampler->interval()); |
| 588 instance_->Start(); |
| 589 } else { |
| 590 ASSERT(instance_->interval_ == sampler->interval()); |
| 591 } |
| 592 } |
| 593 |
| 594 static void RemoveActiveSampler(Sampler* sampler) { |
| 595 ScopedLock lock(mutex_); |
| 596 SamplerRegistry::RemoveActiveSampler(sampler); |
| 597 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) { |
| 598 RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown(); |
| 599 instance_->Join(); |
| 600 delete instance_; |
| 601 instance_ = NULL; |
| 602 } |
| 603 } |
| 604 |
| 605 // Implement Thread::Run(). |
| 606 virtual void Run() { |
| 607 SamplerRegistry::State state = SamplerRegistry::GetState(); |
| 608 while (state != SamplerRegistry::HAS_NO_SAMPLERS) { |
| 609 bool cpu_profiling_enabled = |
| 610 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS); |
| 611 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled(); |
| 612 // When CPU profiling is enabled both JavaScript and C++ code is |
| 613 // profiled. We must not suspend. |
| 614 if (!cpu_profiling_enabled) { |
| 615 if (rate_limiter_.SuspendIfNecessary()) continue; |
| 581 } | 616 } |
| 617 if (cpu_profiling_enabled) { |
| 618 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) { |
| 619 return; |
| 620 } |
| 621 } |
| 622 if (runtime_profiler_enabled) { |
| 623 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) { |
| 624 return; |
| 625 } |
| 626 } |
| 627 OS::Sleep(interval_); |
| 628 } |
| 629 } |
| 582 | 630 |
| 583 // We always sample the VM state. | 631 static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) { |
| 584 sample->state = VMState::current_state(); | 632 if (!sampler->IsProfiling()) return; |
| 633 SamplerThread* sampler_thread = |
| 634 reinterpret_cast<SamplerThread*>(raw_sampler_thread); |
| 635 sampler_thread->SampleContext(sampler); |
| 636 } |
| 585 | 637 |
| 586 // If profiling, we record the pc and sp of the profiled thread. | 638 static void DoRuntimeProfile(Sampler* sampler, void* ignored) { |
| 587 if (sampler_->IsProfiling()) { | 639 if (!sampler->isolate()->IsInitialized()) return; |
| 640 sampler->isolate()->runtime_profiler()->NotifyTick(); |
| 641 } |
| 642 |
| 643 void SampleContext(Sampler* sampler) { |
| 644 thread_act_t profiled_thread = sampler->platform_data()->profiled_thread(); |
| 645 TickSample sample_obj; |
| 646 TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate()); |
| 647 if (sample == NULL) sample = &sample_obj; |
| 648 |
| 649 if (KERN_SUCCESS != thread_suspend(profiled_thread)) return; |
| 650 |
| 588 #if V8_HOST_ARCH_X64 | 651 #if V8_HOST_ARCH_X64 |
| 589 thread_state_flavor_t flavor = x86_THREAD_STATE64; | 652 thread_state_flavor_t flavor = x86_THREAD_STATE64; |
| 590 x86_thread_state64_t state; | 653 x86_thread_state64_t state; |
| 591 mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; | 654 mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; |
| 592 #if __DARWIN_UNIX03 | 655 #if __DARWIN_UNIX03 |
| 593 #define REGISTER_FIELD(name) __r ## name | 656 #define REGISTER_FIELD(name) __r ## name |
| 594 #else | 657 #else |
| 595 #define REGISTER_FIELD(name) r ## name | 658 #define REGISTER_FIELD(name) r ## name |
| 596 #endif // __DARWIN_UNIX03 | 659 #endif // __DARWIN_UNIX03 |
| 597 #elif V8_HOST_ARCH_IA32 | 660 #elif V8_HOST_ARCH_IA32 |
| 598 thread_state_flavor_t flavor = i386_THREAD_STATE; | 661 thread_state_flavor_t flavor = i386_THREAD_STATE; |
| 599 i386_thread_state_t state; | 662 i386_thread_state_t state; |
| 600 mach_msg_type_number_t count = i386_THREAD_STATE_COUNT; | 663 mach_msg_type_number_t count = i386_THREAD_STATE_COUNT; |
| 601 #if __DARWIN_UNIX03 | 664 #if __DARWIN_UNIX03 |
| 602 #define REGISTER_FIELD(name) __e ## name | 665 #define REGISTER_FIELD(name) __e ## name |
| 603 #else | 666 #else |
| 604 #define REGISTER_FIELD(name) e ## name | 667 #define REGISTER_FIELD(name) e ## name |
| 605 #endif // __DARWIN_UNIX03 | 668 #endif // __DARWIN_UNIX03 |
| 606 #else | 669 #else |
| 607 #error Unsupported Mac OS X host architecture. | 670 #error Unsupported Mac OS X host architecture. |
| 608 #endif // V8_HOST_ARCH | 671 #endif // V8_HOST_ARCH |
| 609 | 672 |
| 610 if (thread_get_state(profiled_thread_, | 673 if (thread_get_state(profiled_thread, |
| 611 flavor, | 674 flavor, |
| 612 reinterpret_cast<natural_t*>(&state), | 675 reinterpret_cast<natural_t*>(&state), |
| 613 &count) == KERN_SUCCESS) { | 676 &count) == KERN_SUCCESS) { |
| 614 sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip)); | 677 sample->state = sampler->isolate()->current_vm_state(); |
| 615 sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp)); | 678 sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip)); |
| 616 sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp)); | 679 sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp)); |
| 617 sampler_->SampleStack(sample); | 680 sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp)); |
| 618 } | 681 sampler->SampleStack(sample); |
| 619 } | 682 sampler->Tick(sample); |
| 683 } |
| 684 thread_resume(profiled_thread); |
| 685 } |
| 620 | 686 |
| 621 // Invoke tick handler with program counter and stack pointer. | 687 const int interval_; |
| 622 sampler_->Tick(sample); | 688 RuntimeProfilerRateLimiter rate_limiter_; |
| 623 | 689 |
| 624 // If the sampler runs in sync with the JS thread, we have to | 690 // Protects the process wide state below. |
| 625 // remember to resume it. | 691 static Mutex* mutex_; |
| 626 if (sampler_->IsSynchronous()) thread_resume(profiled_thread_); | 692 static SamplerThread* instance_; |
| 627 } | 693 |
| 628 } | 694 DISALLOW_COPY_AND_ASSIGN(SamplerThread); |
| 629 }; | 695 }; |
| 630 | 696 |
| 631 #undef REGISTER_FIELD | 697 #undef REGISTER_FIELD |
| 632 | 698 |
| 633 | 699 |
| 634 // Entry point for sampler thread. | 700 Mutex* SamplerThread::mutex_ = OS::CreateMutex(); |
| 635 static void* SamplerEntry(void* arg) { | 701 SamplerThread* SamplerThread::instance_ = NULL; |
| 636 Sampler::PlatformData* data = | |
| 637 reinterpret_cast<Sampler::PlatformData*>(arg); | |
| 638 Thread::SetThreadLocal(Isolate::isolate_key(), data->sampler_->isolate()); | |
| 639 data->Runner(); | |
| 640 return 0; | |
| 641 } | |
| 642 | 702 |
| 643 | 703 |
| 644 Sampler::Sampler(Isolate* isolate, int interval, bool profiling) | 704 Sampler::Sampler(Isolate* isolate, int interval) |
| 645 : isolate_(isolate), | 705 : isolate_(isolate), |
| 646 interval_(interval), | 706 interval_(interval), |
| 647 synchronous_(profiling), | 707 profiling_(false), |
| 648 profiling_(profiling), | |
| 649 active_(false), | 708 active_(false), |
| 650 samples_taken_(0) { | 709 samples_taken_(0) { |
| 651 data_ = new PlatformData(this); | 710 data_ = new PlatformData; |
| 652 } | 711 } |
| 653 | 712 |
| 654 | 713 |
| 655 Sampler::~Sampler() { | 714 Sampler::~Sampler() { |
| 715 ASSERT(!IsActive()); |
| 656 delete data_; | 716 delete data_; |
| 657 } | 717 } |
| 658 | 718 |
| 659 | 719 |
| 660 void Sampler::Start() { | 720 void Sampler::Start() { |
| 661 // If we are starting a synchronous sampler, we need to be able to | 721 ASSERT(!IsActive()); |
| 662 // access the calling thread. | 722 SetActive(true); |
| 663 if (IsSynchronous()) { | 723 SamplerThread::AddActiveSampler(this); |
| 664 data_->profiled_thread_ = mach_thread_self(); | |
| 665 } | |
| 666 | |
| 667 // Create sampler thread with high priority. | |
| 668 // According to POSIX spec, when SCHED_FIFO policy is used, a thread | |
| 669 // runs until it exits or blocks. | |
| 670 pthread_attr_t sched_attr; | |
| 671 sched_param fifo_param; | |
| 672 pthread_attr_init(&sched_attr); | |
| 673 pthread_attr_setinheritsched(&sched_attr, PTHREAD_EXPLICIT_SCHED); | |
| 674 pthread_attr_setschedpolicy(&sched_attr, SCHED_FIFO); | |
| 675 fifo_param.sched_priority = sched_get_priority_max(SCHED_FIFO); | |
| 676 pthread_attr_setschedparam(&sched_attr, &fifo_param); | |
| 677 | |
| 678 active_ = true; | |
| 679 pthread_create(&data_->sampler_thread_, &sched_attr, SamplerEntry, data_); | |
| 680 } | 724 } |
| 681 | 725 |
| 682 | 726 |
| 683 void Sampler::Stop() { | 727 void Sampler::Stop() { |
| 684 // Seting active to false triggers termination of the sampler | 728 ASSERT(IsActive()); |
| 685 // thread. | 729 SamplerThread::RemoveActiveSampler(this); |
| 686 active_ = false; | 730 SetActive(false); |
| 687 | |
| 688 // Wait for sampler thread to terminate. | |
| 689 pthread_join(data_->sampler_thread_, NULL); | |
| 690 | |
| 691 // Deallocate Mach port for thread. | |
| 692 if (IsSynchronous()) { | |
| 693 mach_port_deallocate(data_->task_self_, data_->profiled_thread_); | |
| 694 } | |
| 695 } | 731 } |
| 696 | 732 |
| 697 #endif // ENABLE_LOGGING_AND_PROFILING | 733 #endif // ENABLE_LOGGING_AND_PROFILING |
| 698 | 734 |
| 699 } } // namespace v8::internal | 735 } } // namespace v8::internal |
| OLD | NEW |