| OLD | NEW |
| 1 // Copyright 2006-2011 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2011 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 24 matching lines...) Expand all Loading... |
| 35 #include <strings.h> // index | 35 #include <strings.h> // index |
| 36 #include <sys/time.h> | 36 #include <sys/time.h> |
| 37 #include <sys/mman.h> // mmap & munmap | 37 #include <sys/mman.h> // mmap & munmap |
| 38 #include <unistd.h> // sysconf | 38 #include <unistd.h> // sysconf |
| 39 | 39 |
| 40 #undef MAP_TYPE | 40 #undef MAP_TYPE |
| 41 | 41 |
| 42 #include "v8.h" | 42 #include "v8.h" |
| 43 | 43 |
| 44 #include "platform.h" | 44 #include "platform.h" |
| 45 #include "top.h" |
| 45 #include "v8threads.h" | 46 #include "v8threads.h" |
| 46 #include "vm-state-inl.h" | 47 #include "vm-state-inl.h" |
| 47 #include "win32-headers.h" | 48 #include "win32-headers.h" |
| 48 | 49 |
| 49 namespace v8 { | 50 namespace v8 { |
| 50 namespace internal { | 51 namespace internal { |
| 51 | 52 |
| 52 // 0 is never a valid thread id | 53 // 0 is never a valid thread id |
| 53 static const pthread_t kNoThread = (pthread_t) 0; | 54 static const pthread_t kNoThread = (pthread_t) 0; |
| 54 | 55 |
| 55 | 56 |
| 56 double ceiling(double x) { | 57 double ceiling(double x) { |
| 57 return ceil(x); | 58 return ceil(x); |
| 58 } | 59 } |
| 59 | 60 |
| 60 | 61 |
| 61 static Mutex* limit_mutex = NULL; | |
| 62 | |
| 63 | |
| 64 void OS::Setup() { | 62 void OS::Setup() { |
| 65 // Seed the random number generator. | 63 // Seed the random number generator. |
| 66 // Convert the current time to a 64-bit integer first, before converting it | 64 // 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 | 65 // 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 | 66 // set to all ones. The seed will be identical for different instances that |
| 69 // call this setup code within the same millisecond. | 67 // call this setup code within the same millisecond. |
| 70 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); | 68 uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis()); |
| 71 srandom(static_cast<unsigned int>(seed)); | 69 srandom(static_cast<unsigned int>(seed)); |
| 72 limit_mutex = CreateMutex(); | |
| 73 } | 70 } |
| 74 | 71 |
| 75 | 72 |
| 76 uint64_t OS::CpuFeaturesImpliedByPlatform() { | 73 uint64_t OS::CpuFeaturesImpliedByPlatform() { |
| 77 return 0; // Nothing special about Cygwin. | 74 return 0; // Nothing special about Cygwin. |
| 78 } | 75 } |
| 79 | 76 |
| 80 | 77 |
| 81 int OS::ActivationFrameAlignment() { | 78 int OS::ActivationFrameAlignment() { |
| 82 // With gcc 4.4 the tree vectorization optimizer can generate code | 79 // With gcc 4.4 the tree vectorization optimizer can generate code |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 // We keep the lowest and highest addresses mapped as a quick way of | 112 // 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 | 113 // determining that pointers are outside the heap (used mostly in assertions |
| 117 // and verification). The estimate is conservative, ie, not all addresses in | 114 // and verification). The estimate is conservative, ie, not all addresses in |
| 118 // 'allocated' space are actually allocated to our heap. The range is | 115 // '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. | 116 // [lowest, highest), inclusive on the low and and exclusive on the high end. |
| 120 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); | 117 static void* lowest_ever_allocated = reinterpret_cast<void*>(-1); |
| 121 static void* highest_ever_allocated = reinterpret_cast<void*>(0); | 118 static void* highest_ever_allocated = reinterpret_cast<void*>(0); |
| 122 | 119 |
| 123 | 120 |
| 124 static void UpdateAllocatedSpaceLimits(void* address, int size) { | 121 static 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); | 122 lowest_ever_allocated = Min(lowest_ever_allocated, address); |
| 129 highest_ever_allocated = | 123 highest_ever_allocated = |
| 130 Max(highest_ever_allocated, | 124 Max(highest_ever_allocated, |
| 131 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); | 125 reinterpret_cast<void*>(reinterpret_cast<char*>(address) + size)); |
| 132 } | 126 } |
| 133 | 127 |
| 134 | 128 |
| 135 bool OS::IsOutsideAllocatedSpace(void* address) { | 129 bool OS::IsOutsideAllocatedSpace(void* address) { |
| 136 return address < lowest_ever_allocated || address >= highest_ever_allocated; | 130 return address < lowest_ever_allocated || address >= highest_ever_allocated; |
| 137 } | 131 } |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 // This function assumes that the layout of the file is as follows: | 247 // 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] | 248 // hex_start_addr-hex_end_addr rwxp <unused data> [binary_file_name] |
| 255 // If we encounter an unexpected situation we abort scanning further entries. | 249 // If we encounter an unexpected situation we abort scanning further entries. |
| 256 FILE* fp = fopen("/proc/self/maps", "r"); | 250 FILE* fp = fopen("/proc/self/maps", "r"); |
| 257 if (fp == NULL) return; | 251 if (fp == NULL) return; |
| 258 | 252 |
| 259 // Allocate enough room to be able to store a full file name. | 253 // Allocate enough room to be able to store a full file name. |
| 260 const int kLibNameLen = FILENAME_MAX + 1; | 254 const int kLibNameLen = FILENAME_MAX + 1; |
| 261 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); | 255 char* lib_name = reinterpret_cast<char*>(malloc(kLibNameLen)); |
| 262 | 256 |
| 263 i::Isolate* isolate = ISOLATE; | |
| 264 // This loop will terminate once the scanning hits an EOF. | 257 // This loop will terminate once the scanning hits an EOF. |
| 265 while (true) { | 258 while (true) { |
| 266 uintptr_t start, end; | 259 uintptr_t start, end; |
| 267 char attr_r, attr_w, attr_x, attr_p; | 260 char attr_r, attr_w, attr_x, attr_p; |
| 268 // Parse the addresses and permission bits at the beginning of the line. | 261 // Parse the addresses and permission bits at the beginning of the line. |
| 269 if (fscanf(fp, "%" V8PRIxPTR "-%" V8PRIxPTR, &start, &end) != 2) break; | 262 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; | 263 if (fscanf(fp, " %c%c%c%c", &attr_r, &attr_w, &attr_x, &attr_p) != 4) break; |
| 271 | 264 |
| 272 int c; | 265 int c; |
| 273 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { | 266 if (attr_r == 'r' && attr_w != 'w' && attr_x == 'x') { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 287 | 280 |
| 288 // Drop the newline character read by fgets. We do not need to check | 281 // 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 | 282 // for a zero-length string because we know that we at least read the |
| 290 // '/' character. | 283 // '/' character. |
| 291 lib_name[strlen(lib_name) - 1] = '\0'; | 284 lib_name[strlen(lib_name) - 1] = '\0'; |
| 292 } else { | 285 } else { |
| 293 // No library name found, just record the raw address range. | 286 // No library name found, just record the raw address range. |
| 294 snprintf(lib_name, kLibNameLen, | 287 snprintf(lib_name, kLibNameLen, |
| 295 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); | 288 "%08" V8PRIxPTR "-%08" V8PRIxPTR, start, end); |
| 296 } | 289 } |
| 297 LOG(isolate, SharedLibraryEvent(lib_name, start, end)); | 290 LOG(SharedLibraryEvent(lib_name, start, end)); |
| 298 } else { | 291 } else { |
| 299 // Entry not describing executable data. Skip to end of line to setup | 292 // Entry not describing executable data. Skip to end of line to setup |
| 300 // reading the next entry. | 293 // reading the next entry. |
| 301 do { | 294 do { |
| 302 c = getc(fp); | 295 c = getc(fp); |
| 303 } while ((c != EOF) && (c != '\n')); | 296 } while ((c != EOF) && (c != '\n')); |
| 304 if (c == EOF) break; | 297 if (c == EOF) break; |
| 305 } | 298 } |
| 306 } | 299 } |
| 307 free(lib_name); | 300 free(lib_name); |
| 308 fclose(fp); | 301 fclose(fp); |
| 309 #endif | 302 #endif |
| 310 } | 303 } |
| 311 | 304 |
| 312 | 305 |
| 313 void OS::SignalCodeMovingGC() { | 306 void OS::SignalCodeMovingGC() { |
| 314 // Nothing to do on Cygwin. | 307 // Nothing to do on Cygwin. |
| 315 } | 308 } |
| 316 | 309 |
| 317 | 310 |
| 318 int OS::StackWalk(Vector<OS::StackFrame> frames) { | 311 int OS::StackWalk(Vector<OS::StackFrame> frames) { |
| 319 // Not supported on Cygwin. | 312 // Not supported on Cygwin. |
| 320 return 0; | 313 return 0; |
| 321 } | 314 } |
| 322 | 315 |
| 323 | 316 |
| 324 // The VirtualMemory implementation is taken from platform-win32.cc. | 317 // Constants used for mmap. |
| 325 // The mmap-based virtual memory implementation as it is used on most posix | 318 static const int kMmapFd = -1; |
| 326 // platforms does not work well because Cygwin does not support MAP_FIXED. | 319 static const int kMmapFdOffset = 0; |
| 327 // This causes VirtualMemory::Commit to not always commit the memory region | |
| 328 // specified. | |
| 329 | |
| 330 bool VirtualMemory::IsReserved() { | |
| 331 return address_ != NULL; | |
| 332 } | |
| 333 | 320 |
| 334 | 321 |
| 335 VirtualMemory::VirtualMemory(size_t size) { | 322 VirtualMemory::VirtualMemory(size_t size) { |
| 336 address_ = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); | 323 address_ = mmap(NULL, size, PROT_NONE, |
| 324 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, |
| 325 kMmapFd, kMmapFdOffset); |
| 337 size_ = size; | 326 size_ = size; |
| 338 } | 327 } |
| 339 | 328 |
| 340 | 329 |
| 341 VirtualMemory::~VirtualMemory() { | 330 VirtualMemory::~VirtualMemory() { |
| 342 if (IsReserved()) { | 331 if (IsReserved()) { |
| 343 if (0 == VirtualFree(address(), 0, MEM_RELEASE)) address_ = NULL; | 332 if (0 == munmap(address(), size())) address_ = MAP_FAILED; |
| 344 } | 333 } |
| 345 } | 334 } |
| 346 | 335 |
| 347 | 336 |
| 337 bool VirtualMemory::IsReserved() { |
| 338 return address_ != MAP_FAILED; |
| 339 } |
| 340 |
| 341 |
| 348 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { | 342 bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) { |
| 349 int prot = is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; | 343 int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0); |
| 350 if (NULL == VirtualAlloc(address, size, MEM_COMMIT, prot)) { | 344 |
| 345 if (mprotect(address, size, prot) != 0) { |
| 351 return false; | 346 return false; |
| 352 } | 347 } |
| 353 | 348 |
| 354 UpdateAllocatedSpaceLimits(address, static_cast<int>(size)); | 349 UpdateAllocatedSpaceLimits(address, size); |
| 355 return true; | 350 return true; |
| 356 } | 351 } |
| 357 | 352 |
| 358 | 353 |
| 359 bool VirtualMemory::Uncommit(void* address, size_t size) { | 354 bool VirtualMemory::Uncommit(void* address, size_t size) { |
| 360 ASSERT(IsReserved()); | 355 return mmap(address, size, PROT_NONE, |
| 361 return VirtualFree(address, size, MEM_DECOMMIT) != false; | 356 MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, |
| 357 kMmapFd, kMmapFdOffset) != MAP_FAILED; |
| 362 } | 358 } |
| 363 | 359 |
| 364 | 360 |
| 365 class ThreadHandle::PlatformData : public Malloced { | 361 class ThreadHandle::PlatformData : public Malloced { |
| 366 public: | 362 public: |
| 367 explicit PlatformData(ThreadHandle::Kind kind) { | 363 explicit PlatformData(ThreadHandle::Kind kind) { |
| 368 Initialize(kind); | 364 Initialize(kind); |
| 369 } | 365 } |
| 370 | 366 |
| 371 void Initialize(ThreadHandle::Kind kind) { | 367 void Initialize(ThreadHandle::Kind kind) { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 424 } | 420 } |
| 425 | 421 |
| 426 | 422 |
| 427 static void* ThreadEntry(void* arg) { | 423 static void* ThreadEntry(void* arg) { |
| 428 Thread* thread = reinterpret_cast<Thread*>(arg); | 424 Thread* thread = reinterpret_cast<Thread*>(arg); |
| 429 // This is also initialized by the first argument to pthread_create() but we | 425 // This is also initialized by the first argument to pthread_create() but we |
| 430 // don't know which thread will run first (the original thread or the new | 426 // don't know which thread will run first (the original thread or the new |
| 431 // one) so we initialize it here too. | 427 // one) so we initialize it here too. |
| 432 thread->thread_handle_data()->thread_ = pthread_self(); | 428 thread->thread_handle_data()->thread_ = pthread_self(); |
| 433 ASSERT(thread->IsValid()); | 429 ASSERT(thread->IsValid()); |
| 434 Thread::SetThreadLocal(Isolate::isolate_key(), thread->isolate()); | |
| 435 thread->Run(); | 430 thread->Run(); |
| 436 return NULL; | 431 return NULL; |
| 437 } | 432 } |
| 438 | 433 |
| 439 | 434 |
| 440 void Thread::set_name(const char* name) { | 435 void Thread::set_name(const char* name) { |
| 441 strncpy(name_, name, sizeof(name_)); | 436 strncpy(name_, name, sizeof(name_)); |
| 442 name_[sizeof(name_) - 1] = '\0'; | 437 name_[sizeof(name_) - 1] = '\0'; |
| 443 } | 438 } |
| 444 | 439 |
| 445 | 440 |
| 446 void Thread::Start() { | 441 void Thread::Start() { |
| 447 pthread_attr_t* attr_ptr = NULL; | 442 pthread_create(&thread_handle_data()->thread_, NULL, ThreadEntry, this); |
| 448 pthread_attr_t attr; | |
| 449 if (stack_size_ > 0) { | |
| 450 pthread_attr_init(&attr); | |
| 451 pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_)); | |
| 452 attr_ptr = &attr; | |
| 453 } | |
| 454 pthread_create(&thread_handle_data()->thread_, attr_ptr, ThreadEntry, this); | |
| 455 ASSERT(IsValid()); | 443 ASSERT(IsValid()); |
| 456 } | 444 } |
| 457 | 445 |
| 458 | 446 |
| 459 void Thread::Join() { | 447 void Thread::Join() { |
| 460 pthread_join(thread_handle_data()->thread_, NULL); | 448 pthread_join(thread_handle_data()->thread_, NULL); |
| 461 } | 449 } |
| 462 | 450 |
| 463 | 451 |
| 464 static inline Thread::LocalStorageKey PthreadKeyToLocalKey( | 452 static inline Thread::LocalStorageKey PthreadKeyToLocalKey( |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 | 616 |
| 629 #ifdef ENABLE_LOGGING_AND_PROFILING | 617 #ifdef ENABLE_LOGGING_AND_PROFILING |
| 630 | 618 |
| 631 // ---------------------------------------------------------------------------- | 619 // ---------------------------------------------------------------------------- |
| 632 // Cygwin profiler support. | 620 // Cygwin profiler support. |
| 633 // | 621 // |
| 634 // On Cygwin we use the same sampler implementation as on win32. | 622 // On Cygwin we use the same sampler implementation as on win32. |
| 635 | 623 |
| 636 class Sampler::PlatformData : public Malloced { | 624 class Sampler::PlatformData : public Malloced { |
| 637 public: | 625 public: |
| 626 explicit PlatformData(Sampler* sampler) { |
| 627 sampler_ = sampler; |
| 628 sampler_thread_ = INVALID_HANDLE_VALUE; |
| 629 profiled_thread_ = INVALID_HANDLE_VALUE; |
| 630 } |
| 631 |
| 632 Sampler* sampler_; |
| 633 HANDLE sampler_thread_; |
| 634 HANDLE profiled_thread_; |
| 635 RuntimeProfilerRateLimiter rate_limiter_; |
| 636 |
| 637 // Sampler thread handler. |
| 638 void Runner() { |
| 639 while (sampler_->IsActive()) { |
| 640 if (rate_limiter_.SuspendIfNecessary()) continue; |
| 641 Sample(); |
| 642 Sleep(sampler_->interval_); |
| 643 } |
| 644 } |
| 645 |
| 646 void Sample() { |
| 647 if (sampler_->IsProfiling()) { |
| 648 // Context used for sampling the register state of the profiled thread. |
| 649 CONTEXT context; |
| 650 memset(&context, 0, sizeof(context)); |
| 651 |
| 652 TickSample sample_obj; |
| 653 TickSample* sample = CpuProfiler::TickSampleEvent(); |
| 654 if (sample == NULL) sample = &sample_obj; |
| 655 |
| 656 static const DWORD kSuspendFailed = static_cast<DWORD>(-1); |
| 657 if (SuspendThread(profiled_thread_) == kSuspendFailed) return; |
| 658 sample->state = Top::current_vm_state(); |
| 659 |
| 660 context.ContextFlags = CONTEXT_FULL; |
| 661 if (GetThreadContext(profiled_thread_, &context) != 0) { |
| 662 #if V8_HOST_ARCH_X64 |
| 663 sample->pc = reinterpret_cast<Address>(context.Rip); |
| 664 sample->sp = reinterpret_cast<Address>(context.Rsp); |
| 665 sample->fp = reinterpret_cast<Address>(context.Rbp); |
| 666 #else |
| 667 sample->pc = reinterpret_cast<Address>(context.Eip); |
| 668 sample->sp = reinterpret_cast<Address>(context.Esp); |
| 669 sample->fp = reinterpret_cast<Address>(context.Ebp); |
| 670 #endif |
| 671 sampler_->SampleStack(sample); |
| 672 sampler_->Tick(sample); |
| 673 } |
| 674 ResumeThread(profiled_thread_); |
| 675 } |
| 676 if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick(); |
| 677 } |
| 678 }; |
| 679 |
| 680 |
| 681 // Entry point for sampler thread. |
| 682 static DWORD __stdcall SamplerEntry(void* arg) { |
| 683 Sampler::PlatformData* data = |
| 684 reinterpret_cast<Sampler::PlatformData*>(arg); |
| 685 data->Runner(); |
| 686 return 0; |
| 687 } |
| 688 |
| 689 |
| 690 // Initialize a profile sampler. |
| 691 Sampler::Sampler(int interval) |
| 692 : interval_(interval), |
| 693 profiling_(false), |
| 694 active_(false), |
| 695 samples_taken_(0) { |
| 696 data_ = new PlatformData(this); |
| 697 } |
| 698 |
| 699 |
| 700 Sampler::~Sampler() { |
| 701 delete data_; |
| 702 } |
| 703 |
| 704 |
| 705 // Start profiling. |
| 706 void Sampler::Start() { |
| 707 // Do not start multiple threads for the same sampler. |
| 708 ASSERT(!IsActive()); |
| 709 |
| 638 // Get a handle to the calling thread. This is the thread that we are | 710 // Get a handle to the calling thread. This is the thread that we are |
| 639 // going to profile. We need to make a copy of the handle because we are | 711 // going to profile. We need to make a copy of the handle because we are |
| 640 // going to use it in the sampler thread. Using GetThreadHandle() will | 712 // going to use it in the sampler thread. Using GetThreadHandle() will |
| 641 // not work in this case. We're using OpenThread because DuplicateHandle | 713 // not work in this case. We're using OpenThread because DuplicateHandle |
| 642 // for some reason doesn't work in Chrome's sandbox. | 714 // for some reason doesn't work in Chrome's sandbox. |
| 643 PlatformData() : profiled_thread_(OpenThread(THREAD_GET_CONTEXT | | 715 data_->profiled_thread_ = OpenThread(THREAD_GET_CONTEXT | |
| 644 THREAD_SUSPEND_RESUME | | 716 THREAD_SUSPEND_RESUME | |
| 645 THREAD_QUERY_INFORMATION, | 717 THREAD_QUERY_INFORMATION, |
| 646 false, | 718 false, |
| 647 GetCurrentThreadId())) {} | 719 GetCurrentThreadId()); |
| 720 BOOL ok = data_->profiled_thread_ != NULL; |
| 721 if (!ok) return; |
| 648 | 722 |
| 649 ~PlatformData() { | 723 // Start sampler thread. |
| 650 if (profiled_thread_ != NULL) { | 724 DWORD tid; |
| 651 CloseHandle(profiled_thread_); | 725 SetActive(true); |
| 652 profiled_thread_ = NULL; | 726 data_->sampler_thread_ = CreateThread(NULL, 0, SamplerEntry, data_, 0, &tid); |
| 653 } | 727 // Set thread to high priority to increase sampling accuracy. |
| 654 } | 728 SetThreadPriority(data_->sampler_thread_, THREAD_PRIORITY_TIME_CRITICAL); |
| 655 | |
| 656 HANDLE profiled_thread() { return profiled_thread_; } | |
| 657 | |
| 658 private: | |
| 659 HANDLE profiled_thread_; | |
| 660 }; | |
| 661 | |
| 662 | |
| 663 class SamplerThread : public Thread { | |
| 664 public: | |
| 665 explicit SamplerThread(int interval) | |
| 666 : Thread(NULL, "SamplerThread"), | |
| 667 interval_(interval) {} | |
| 668 | |
| 669 static void AddActiveSampler(Sampler* sampler) { | |
| 670 ScopedLock lock(mutex_); | |
| 671 SamplerRegistry::AddActiveSampler(sampler); | |
| 672 if (instance_ == NULL) { | |
| 673 instance_ = new SamplerThread(sampler->interval()); | |
| 674 instance_->Start(); | |
| 675 } else { | |
| 676 ASSERT(instance_->interval_ == sampler->interval()); | |
| 677 } | |
| 678 } | |
| 679 | |
| 680 static void RemoveActiveSampler(Sampler* sampler) { | |
| 681 ScopedLock lock(mutex_); | |
| 682 SamplerRegistry::RemoveActiveSampler(sampler); | |
| 683 if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) { | |
| 684 RuntimeProfiler::WakeUpRuntimeProfilerThreadBeforeShutdown(); | |
| 685 instance_->Join(); | |
| 686 delete instance_; | |
| 687 instance_ = NULL; | |
| 688 } | |
| 689 } | |
| 690 | |
| 691 // Implement Thread::Run(). | |
| 692 virtual void Run() { | |
| 693 SamplerRegistry::State state; | |
| 694 while ((state = SamplerRegistry::GetState()) != | |
| 695 SamplerRegistry::HAS_NO_SAMPLERS) { | |
| 696 bool cpu_profiling_enabled = | |
| 697 (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS); | |
| 698 bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled(); | |
| 699 // When CPU profiling is enabled both JavaScript and C++ code is | |
| 700 // profiled. We must not suspend. | |
| 701 if (!cpu_profiling_enabled) { | |
| 702 if (rate_limiter_.SuspendIfNecessary()) continue; | |
| 703 } | |
| 704 if (cpu_profiling_enabled) { | |
| 705 if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) { | |
| 706 return; | |
| 707 } | |
| 708 } | |
| 709 if (runtime_profiler_enabled) { | |
| 710 if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) { | |
| 711 return; | |
| 712 } | |
| 713 } | |
| 714 OS::Sleep(interval_); | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 static void DoCpuProfile(Sampler* sampler, void* raw_sampler_thread) { | |
| 719 if (!sampler->isolate()->IsInitialized()) return; | |
| 720 if (!sampler->IsProfiling()) return; | |
| 721 SamplerThread* sampler_thread = | |
| 722 reinterpret_cast<SamplerThread*>(raw_sampler_thread); | |
| 723 sampler_thread->SampleContext(sampler); | |
| 724 } | |
| 725 | |
| 726 static void DoRuntimeProfile(Sampler* sampler, void* ignored) { | |
| 727 if (!sampler->isolate()->IsInitialized()) return; | |
| 728 sampler->isolate()->runtime_profiler()->NotifyTick(); | |
| 729 } | |
| 730 | |
| 731 void SampleContext(Sampler* sampler) { | |
| 732 HANDLE profiled_thread = sampler->platform_data()->profiled_thread(); | |
| 733 if (profiled_thread == NULL) return; | |
| 734 | |
| 735 // Context used for sampling the register state of the profiled thread. | |
| 736 CONTEXT context; | |
| 737 memset(&context, 0, sizeof(context)); | |
| 738 | |
| 739 TickSample sample_obj; | |
| 740 TickSample* sample = CpuProfiler::TickSampleEvent(sampler->isolate()); | |
| 741 if (sample == NULL) sample = &sample_obj; | |
| 742 | |
| 743 static const DWORD kSuspendFailed = static_cast<DWORD>(-1); | |
| 744 if (SuspendThread(profiled_thread) == kSuspendFailed) return; | |
| 745 sample->state = sampler->isolate()->current_vm_state(); | |
| 746 | |
| 747 context.ContextFlags = CONTEXT_FULL; | |
| 748 if (GetThreadContext(profiled_thread, &context) != 0) { | |
| 749 #if V8_HOST_ARCH_X64 | |
| 750 sample->pc = reinterpret_cast<Address>(context.Rip); | |
| 751 sample->sp = reinterpret_cast<Address>(context.Rsp); | |
| 752 sample->fp = reinterpret_cast<Address>(context.Rbp); | |
| 753 #else | |
| 754 sample->pc = reinterpret_cast<Address>(context.Eip); | |
| 755 sample->sp = reinterpret_cast<Address>(context.Esp); | |
| 756 sample->fp = reinterpret_cast<Address>(context.Ebp); | |
| 757 #endif | |
| 758 sampler->SampleStack(sample); | |
| 759 sampler->Tick(sample); | |
| 760 } | |
| 761 ResumeThread(profiled_thread); | |
| 762 } | |
| 763 | |
| 764 const int interval_; | |
| 765 RuntimeProfilerRateLimiter rate_limiter_; | |
| 766 | |
| 767 // Protects the process wide state below. | |
| 768 static Mutex* mutex_; | |
| 769 static SamplerThread* instance_; | |
| 770 | |
| 771 DISALLOW_COPY_AND_ASSIGN(SamplerThread); | |
| 772 }; | |
| 773 | |
| 774 | |
| 775 Mutex* SamplerThread::mutex_ = OS::CreateMutex(); | |
| 776 SamplerThread* SamplerThread::instance_ = NULL; | |
| 777 | |
| 778 | |
| 779 Sampler::Sampler(Isolate* isolate, int interval) | |
| 780 : isolate_(isolate), | |
| 781 interval_(interval), | |
| 782 profiling_(false), | |
| 783 active_(false), | |
| 784 samples_taken_(0) { | |
| 785 data_ = new PlatformData; | |
| 786 } | 729 } |
| 787 | 730 |
| 788 | 731 |
| 789 Sampler::~Sampler() { | 732 // Stop profiling. |
| 790 ASSERT(!IsActive()); | 733 void Sampler::Stop() { |
| 791 delete data_; | 734 // Seting active to false triggers termination of the sampler |
| 735 // thread. |
| 736 SetActive(false); |
| 737 |
| 738 // Wait for sampler thread to terminate. |
| 739 Top::WakeUpRuntimeProfilerThreadBeforeShutdown(); |
| 740 WaitForSingleObject(data_->sampler_thread_, INFINITE); |
| 741 |
| 742 // Release the thread handles |
| 743 CloseHandle(data_->sampler_thread_); |
| 744 CloseHandle(data_->profiled_thread_); |
| 792 } | 745 } |
| 793 | 746 |
| 794 | 747 |
| 795 void Sampler::Start() { | |
| 796 ASSERT(!IsActive()); | |
| 797 SetActive(true); | |
| 798 SamplerThread::AddActiveSampler(this); | |
| 799 } | |
| 800 | |
| 801 | |
| 802 void Sampler::Stop() { | |
| 803 ASSERT(IsActive()); | |
| 804 SamplerThread::RemoveActiveSampler(this); | |
| 805 SetActive(false); | |
| 806 } | |
| 807 | |
| 808 #endif // ENABLE_LOGGING_AND_PROFILING | 748 #endif // ENABLE_LOGGING_AND_PROFILING |
| 809 | 749 |
| 810 } } // namespace v8::internal | 750 } } // namespace v8::internal |
| 811 | 751 |
| OLD | NEW |