From 636f1e8e591dde3d71c45740ceda95ceb592cb0e Mon Sep 17 00:00:00 2001 From: lpy Date: Wed, 25 May 2016 13:22:34 -0700 Subject: [PATCH] Revert of Create libsampler as V8 sampler library. (patchset #24 id:460001 of https://codereview.chromium.org/1922303002/ ) Reason for revert: V8 Linux64 TSAN failure because ThreadSanitizer indicated data race. Original issue's description: > Create libsampler as V8 sampler library. > > This patch does five things: > > 1. Extracts sampler as libsampler to provide sampling functionality support. > 2. Makes SampleStack virtual so embedders can override the behaviour of sample collecting. > 3. Removes sampler.[h|cc]. > 4. Moves sampling thread into log.cc as workaround to keep the --prof functionality. > 5. Creates SamplerManager to manage the relationship between samplers and threads. > > The reason we port hashmap.h is that in debug mode, STL containers are using > mutexes from a mutex pool, which may lead to deadlock when using asynchronously > signal handler. > > Currently libsampler is used in V8 temporarily. > > BUG=v8:4789 > LOG=n > > Committed: https://crrev.com/06cc9b7c176a6223971deaa9fbcafe1a05058c7b > Cr-Commit-Position: refs/heads/master@{#36527} TBR=jochen@chromium.org,alph@chromium.org,fmeawad@chromium.org,yangguo@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true BUG=v8:4789 Review-Url: https://codereview.chromium.org/2000323007 Cr-Commit-Position: refs/heads/master@{#36529} --- BUILD.gn | 25 +- include/v8.h | 6 - src/api.cc | 6 - src/isolate.cc | 4 +- src/libsampler/DEPS | 6 - src/libsampler/hashmap.h | 278 ------ src/libsampler/utils.h | 27 - src/libsampler/v8-sampler.cc | 678 -------------- src/log.cc | 68 +- src/log.h | 7 +- src/profiler/cpu-profiler.cc | 7 +- src/profiler/cpu-profiler.h | 6 +- src/profiler/sampler.cc | 828 ++++++++++++++++++ .../v8-sampler.h => profiler/sampler.h} | 41 +- src/v8.cc | 6 +- src/v8.gyp | 35 +- test/cctest/cctest.gyp | 1 - test/cctest/libsampler/test-sampler.cc | 141 --- test/cctest/test-cpu-profiler.cc | 2 +- 19 files changed, 878 insertions(+), 1294 deletions(-) delete mode 100644 src/libsampler/DEPS delete mode 100644 src/libsampler/hashmap.h delete mode 100644 src/libsampler/utils.h delete mode 100644 src/libsampler/v8-sampler.cc create mode 100644 src/profiler/sampler.cc rename src/{libsampler/v8-sampler.h => profiler/sampler.h} (79%) delete mode 100644 test/cctest/libsampler/test-sampler.cc diff --git a/BUILD.gn b/BUILD.gn index 82023a4add3..13943c9078e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -105,11 +105,6 @@ config("libplatform_config") { include_dirs = [ "include" ] } -# This config should be applied to code using the libsampler. -config("libsampler_config") { - include_dirs = [ "include" ] -} - # This config should only be applied to code using V8 and not any V8 code # itself. config("external_config") { @@ -1334,6 +1329,8 @@ v8_source_set("v8_base") { "src/profiler/profile-generator-inl.h", "src/profiler/profile-generator.cc", "src/profiler/profile-generator.h", + "src/profiler/sampler.cc", + "src/profiler/sampler.h", "src/profiler/sampling-heap-profiler.cc", "src/profiler/sampling-heap-profiler.h", "src/profiler/strings-storage.cc", @@ -1819,7 +1816,6 @@ v8_source_set("v8_base") { defines = [] deps = [ ":v8_libbase", - ":v8_libsampler", ] if (is_win) { @@ -1974,23 +1970,6 @@ v8_source_set("v8_libplatform") { ] } -v8_source_set("v8_libsampler") { - sources = [ - "src/libsampler/hashmap.h", - "src/libsampler/utils.h", - "src/libsampler/v8-sampler.cc", - "src/libsampler/v8-sampler.h", - ] - - configs = [ ":internal_config_base" ] - - public_configs = [ ":libsampler_config" ] - - deps = [ - ":v8_libbase", - ] -} - v8_source_set("fuzzer_support") { visibility = [ ":*" ] # Only targets in this file can depend on this. diff --git a/include/v8.h b/include/v8.h index 43154927ea0..b5db5fe4295 100644 --- a/include/v8.h +++ b/include/v8.h @@ -6331,12 +6331,6 @@ class V8_EXPORT Isolate { */ void VisitWeakHandles(PersistentHandleVisitor* visitor); - /** - * Check if this isolate is in use. - * True if at least one thread Enter'ed this isolate. - */ - bool IsInUse(); - private: template friend class PersistentValueMapBase; diff --git a/src/api.cc b/src/api.cc index 4a9c2114801..e5bcd24bd7b 100644 --- a/src/api.cc +++ b/src/api.cc @@ -7793,12 +7793,6 @@ void Isolate::VisitExternalResources(ExternalResourceVisitor* visitor) { } -bool Isolate::IsInUse() { - i::Isolate* isolate = reinterpret_cast(this); - return isolate->IsInUse(); -} - - class VisitorAdapter : public i::ObjectVisitor { public: explicit VisitorAdapter(PersistentHandleVisitor* visitor) diff --git a/src/isolate.cc b/src/isolate.cc index e0d5285ed69..a721e3bdb12 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -27,10 +27,10 @@ #include "src/ic/stub-cache.h" #include "src/interpreter/interpreter.h" #include "src/isolate-inl.h" -#include "src/libsampler/v8-sampler.h" #include "src/log.h" #include "src/messages.h" #include "src/profiler/cpu-profiler.h" +#include "src/profiler/sampler.h" #include "src/prototype.h" #include "src/regexp/regexp-stack.h" #include "src/runtime-profiler.h" @@ -2006,7 +2006,7 @@ void Isolate::Deinit() { } // We must stop the logger before we tear down other components. - sampler::Sampler* sampler = logger_->sampler(); + Sampler* sampler = logger_->sampler(); if (sampler && sampler->IsActive()) sampler->Stop(); delete deoptimizer_data_; diff --git a/src/libsampler/DEPS b/src/libsampler/DEPS deleted file mode 100644 index bdf1a82670b..00000000000 --- a/src/libsampler/DEPS +++ /dev/null @@ -1,6 +0,0 @@ -include_rules = [ - "+include", - "-src", - "+src/base", - "+src/libsampler", -] \ No newline at end of file diff --git a/src/libsampler/hashmap.h b/src/libsampler/hashmap.h deleted file mode 100644 index e4b3cc6009b..00000000000 --- a/src/libsampler/hashmap.h +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2012 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is ported from src/hashmap.h - -#ifndef V8_LIBSAMPLER_HASHMAP_H_ -#define V8_LIBSAMPLER_HASHMAP_H_ - -#include "src/base/bits.h" -#include "src/base/logging.h" -#include "src/libsampler/utils.h" - -namespace v8 { -namespace sampler { - -class HashMapImpl { - public: - typedef bool (*MatchFun) (void* key1, void* key2); - - // The default capacity. - static const uint32_t kDefaultHashMapCapacity = 8; - - // initial_capacity is the size of the initial hash map; - // it must be a power of 2 (and thus must not be 0). - HashMapImpl(MatchFun match, - uint32_t capacity = kDefaultHashMapCapacity); - - ~HashMapImpl(); - - // HashMap entries are (key, value, hash) triplets. - // Some clients may not need to use the value slot - // (e.g. implementers of sets, where the key is the value). - struct Entry { - void* key; - void* value; - uint32_t hash; // The full hash value for key - int order; // If you never remove entries this is the insertion order. - }; - - // If an entry with matching key is found, returns that entry. - // Otherwise, NULL is returned. - Entry* Lookup(void* key, uint32_t hash) const; - - // If an entry with matching key is found, returns that entry. - // If no matching entry is found, a new entry is inserted with - // corresponding key, key hash, and NULL value. - Entry* LookupOrInsert(void* key, uint32_t hash); - - // Removes the entry with matching key. - // It returns the value of the deleted entry - // or null if there is no value for such key. - void* Remove(void* key, uint32_t hash); - - // Empties the hash map (occupancy() == 0). - void Clear(); - - // The number of (non-empty) entries in the table. - uint32_t occupancy() const { return occupancy_; } - - // The capacity of the table. The implementation - // makes sure that occupancy is at most 80% of - // the table capacity. - uint32_t capacity() const { return capacity_; } - - // Iteration - // - // for (Entry* p = map.Start(); p != NULL; p = map.Next(p)) { - // ... - // } - // - // If entries are inserted during iteration, the effect of - // calling Next() is undefined. - Entry* Start() const; - Entry* Next(Entry* p) const; - - // Some match functions defined for convenience. - static bool PointersMatch(void* key1, void* key2) { - return key1 == key2; - } - - private: - MatchFun match_; - Entry* map_; - uint32_t capacity_; - uint32_t occupancy_; - - Entry* map_end() const { return map_ + capacity_; } - Entry* Probe(void* key, uint32_t hash) const; - void Initialize(uint32_t capacity); - void Resize(); -}; - -typedef HashMapImpl HashMap; - -HashMapImpl::HashMapImpl(MatchFun match, uint32_t initial_capacity) { - match_ = match; - Initialize(initial_capacity); -} - - -HashMapImpl::~HashMapImpl() { - Malloced::Delete(map_); -} - - -HashMapImpl::Entry* HashMapImpl::Lookup(void* key, uint32_t hash) const { - Entry* p = Probe(key, hash); - return p->key != NULL ? p : NULL; -} - - -HashMapImpl::Entry* HashMapImpl::LookupOrInsert(void* key, uint32_t hash) { - // Find a matching entry. - Entry* p = Probe(key, hash); - if (p->key != NULL) { - return p; - } - - // No entry found; insert one. - p->key = key; - p->value = NULL; - p->hash = hash; - p->order = occupancy_; - occupancy_++; - - // Grow the map if we reached >= 80% occupancy. - if (occupancy_ + occupancy_ / 4 >= capacity_) { - Resize(); - p = Probe(key, hash); - } - - return p; -} - - -void* HashMapImpl::Remove(void* key, uint32_t hash) { - // Lookup the entry for the key to remove. - Entry* p = Probe(key, hash); - if (p->key == NULL) { - // Key not found nothing to remove. - return NULL; - } - - void* value = p->value; - // To remove an entry we need to ensure that it does not create an empty - // entry that will cause the search for another entry to stop too soon. If all - // the entries between the entry to remove and the next empty slot have their - // initial position inside this interval, clearing the entry to remove will - // not break the search. If, while searching for the next empty entry, an - // entry is encountered which does not have its initial position between the - // entry to remove and the position looked at, then this entry can be moved to - // the place of the entry to remove without breaking the search for it. The - // entry made vacant by this move is now the entry to remove and the process - // starts over. - // Algorithm from http://en.wikipedia.org/wiki/Open_addressing. - - // This guarantees loop termination as there is at least one empty entry so - // eventually the removed entry will have an empty entry after it. - DCHECK(occupancy_ < capacity_); - - // p is the candidate entry to clear. q is used to scan forwards. - Entry* q = p; // Start at the entry to remove. - while (true) { - // Move q to the next entry. - q = q + 1; - if (q == map_end()) { - q = map_; - } - - // All entries between p and q have their initial position between p and q - // and the entry p can be cleared without breaking the search for these - // entries. - if (q->key == NULL) { - break; - } - - // Find the initial position for the entry at position q. - Entry* r = map_ + (q->hash & (capacity_ - 1)); - - // If the entry at position q has its initial position outside the range - // between p and q it can be moved forward to position p and will still be - // found. There is now a new candidate entry for clearing. - if ((q > p && (r <= p || r > q)) || - (q < p && (r <= p && r > q))) { - *p = *q; - p = q; - } - } - - // Clear the entry which is allowed to en emptied. - p->key = NULL; - occupancy_--; - return value; -} - - -void HashMapImpl::Clear() { - // Mark all entries as empty. - const Entry* end = map_end(); - for (Entry* p = map_; p < end; p++) { - p->key = NULL; - } - occupancy_ = 0; -} - - -HashMapImpl::Entry* HashMapImpl::Start() const { - return Next(map_ - 1); -} - - -HashMapImpl::Entry* HashMapImpl::Next(Entry* p) const { - const Entry* end = map_end(); - DCHECK(map_ - 1 <= p && p < end); - for (p++; p < end; p++) { - if (p->key != NULL) { - return p; - } - } - return NULL; -} - - -HashMapImpl::Entry* HashMapImpl::Probe(void* key, uint32_t hash) const { - DCHECK(key != NULL); - - DCHECK(base::bits::IsPowerOfTwo32(capacity_)); - Entry* p = map_ + (hash & (capacity_ - 1)); - const Entry* end = map_end(); - DCHECK(map_ <= p && p < end); - - DCHECK(occupancy_ < capacity_); // Guarantees loop termination. - while (p->key != NULL && (hash != p->hash || !match_(key, p->key))) { - p++; - if (p >= end) { - p = map_; - } - } - - return p; -} - - -void HashMapImpl::Initialize(uint32_t capacity) { - DCHECK(base::bits::IsPowerOfTwo32(capacity)); - map_ = reinterpret_cast(Malloced::New(capacity * sizeof(Entry))); - CHECK(map_ != NULL); - capacity_ = capacity; - Clear(); -} - - -void HashMapImpl::Resize() { - Entry* map = map_; - uint32_t n = occupancy_; - - // Allocate larger map. - Initialize(capacity_ * 2); - - // Rehash all current entries. - for (Entry* p = map; n > 0; p++) { - if (p->key != NULL) { - Entry* entry = LookupOrInsert(p->key, p->hash); - entry->value = p->value; - entry->order = p->order; - n--; - } - } - - // Delete old map. - Malloced::Delete(map); -} - -} // namespace sampler -} // namespace v8 - -#endif // V8_LIBSAMPLER_HASHMAP_H_ diff --git a/src/libsampler/utils.h b/src/libsampler/utils.h deleted file mode 100644 index a0e28cefc85..00000000000 --- a/src/libsampler/utils.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2016 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef V8_LIBSAMPLER_UTILS_H_ -#define V8_LIBSAMPLER_UTILS_H_ - -#include "include/v8.h" - -namespace v8 { -namespace sampler { - -class Malloced { - public: - static void* New(size_t size) { - return malloc(size); - } - - static void Delete(void* p) { - free(p); - } -}; - -} // namespace sampler -} // namespace v8 - -#endif // V8_LIBSAMPLER_UTILS_H_ diff --git a/src/libsampler/v8-sampler.cc b/src/libsampler/v8-sampler.cc deleted file mode 100644 index a7798b5b8ee..00000000000 --- a/src/libsampler/v8-sampler.cc +++ /dev/null @@ -1,678 +0,0 @@ -// Copyright 2016 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/libsampler/v8-sampler.h" - -#if V8_OS_POSIX && !V8_OS_CYGWIN - -#define USE_SIGNALS - -#include -#include -#include -#include - -#if !V8_OS_QNX && !V8_OS_NACL && !V8_OS_AIX -#include // NOLINT -#endif - -#if V8_OS_MACOSX -#include -// OpenBSD doesn't have . ucontext_t lives in -// and is a typedef for struct sigcontext. There is no uc_mcontext. -#elif(!V8_OS_ANDROID || defined(__BIONIC_HAVE_UCONTEXT_T)) && \ - !V8_OS_OPENBSD && !V8_OS_NACL -#include -#endif - -#include - -// GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'. -// Old versions of the C library didn't define the type. -#if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T) && \ - (defined(__arm__) || defined(__aarch64__)) && \ - !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT) -#include // NOLINT -#endif - -#elif V8_OS_WIN || V8_OS_CYGWIN - -#include "src/base/win32-headers.h" - -#endif - -#include -#include -#include - -#include "src/base/atomic-utils.h" -#include "src/base/platform/platform.h" -#include "src/libsampler/hashmap.h" - - -#if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T) - -// Not all versions of Android's C library provide ucontext_t. -// Detect this and provide custom but compatible definitions. Note that these -// follow the GLibc naming convention to access register values from -// mcontext_t. -// -// See http://code.google.com/p/android/issues/detail?id=34784 - -#if defined(__arm__) - -typedef struct sigcontext mcontext_t; - -typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - // Other fields are not used by V8, don't define them here. -} ucontext_t; - -#elif defined(__aarch64__) - -typedef struct sigcontext mcontext_t; - -typedef struct ucontext { - uint64_t uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - // Other fields are not used by V8, don't define them here. -} ucontext_t; - -#elif defined(__mips__) -// MIPS version of sigcontext, for Android bionic. -typedef struct { - uint32_t regmask; - uint32_t status; - uint64_t pc; - uint64_t gregs[32]; - uint64_t fpregs[32]; - uint32_t acx; - uint32_t fpc_csr; - uint32_t fpc_eir; - uint32_t used_math; - uint32_t dsp; - uint64_t mdhi; - uint64_t mdlo; - uint32_t hi1; - uint32_t lo1; - uint32_t hi2; - uint32_t lo2; - uint32_t hi3; - uint32_t lo3; -} mcontext_t; - -typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - // Other fields are not used by V8, don't define them here. -} ucontext_t; - -#elif defined(__i386__) -// x86 version for Android. -typedef struct { - uint32_t gregs[19]; - void* fpregs; - uint32_t oldmask; - uint32_t cr2; -} mcontext_t; - -typedef uint32_t kernel_sigset_t[2]; // x86 kernel uses 64-bit signal masks -typedef struct ucontext { - uint32_t uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - // Other fields are not used by V8, don't define them here. -} ucontext_t; -enum { REG_EBP = 6, REG_ESP = 7, REG_EIP = 14 }; - -#elif defined(__x86_64__) -// x64 version for Android. -typedef struct { - uint64_t gregs[23]; - void* fpregs; - uint64_t __reserved1[8]; -} mcontext_t; - -typedef struct ucontext { - uint64_t uc_flags; - struct ucontext *uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - // Other fields are not used by V8, don't define them here. -} ucontext_t; -enum { REG_RBP = 10, REG_RSP = 15, REG_RIP = 16 }; -#endif - -#endif // V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T) - - -namespace v8 { -namespace sampler { - -namespace { - -#if defined(USE_SIGNALS) -typedef std::vector SamplerList; -typedef SamplerList::iterator SamplerListIterator; - -class AtomicGuard { - public: - explicit AtomicGuard(base::AtomicValue* atomic, bool is_block = true) - : atomic_(atomic), - is_success_(false) { - do { - // Use Acquire_Load to gain mutual exclusion. - USE(atomic_->Value()); - is_success_ = atomic_->TrySetValue(0, 1); - } while (is_block && !is_success_); - } - - bool is_success() { return is_success_; } - - ~AtomicGuard() { - if (is_success_) { - atomic_->SetValue(0); - } - atomic_ = NULL; - } - - private: - base::AtomicValue* atomic_; - bool is_success_; -}; - - -// Returns key for hash map. -void* ThreadKey(pthread_t thread_id) { - return reinterpret_cast(thread_id); -} - - -// Returns hash value for hash map. -uint32_t ThreadHash(pthread_t thread_id) { -#if V8_OS_MACOSX - return static_cast(reinterpret_cast(thread_id)); -#else - return static_cast(thread_id); -#endif -} - -#endif // USE_SIGNALS - -} // namespace - -#if defined(USE_SIGNALS) - -class Sampler::PlatformData { - public: - PlatformData() : vm_tid_(pthread_self()) {} - pthread_t vm_tid() const { return vm_tid_; } - - private: - pthread_t vm_tid_; -}; - - -class SamplerManager { - public: - static void AddSampler(Sampler* sampler) { - AtomicGuard atomic_guard(&samplers_access_counter_); - DCHECK(sampler->IsActive() || !sampler->IsRegistered()); - // Add sampler into map if needed. - pthread_t thread_id = sampler->platform_data()->vm_tid(); - HashMap::Entry* entry = - sampler_map_.Pointer()->LookupOrInsert(ThreadKey(thread_id), - ThreadHash(thread_id)); - DCHECK(entry != NULL); - if (entry->value == NULL) { - SamplerList* samplers = new SamplerList(); - samplers->push_back(sampler); - entry->value = samplers; - } else { - SamplerList* samplers = reinterpret_cast(entry->value); - bool exists = false; - for (SamplerListIterator iter = samplers->begin(); - iter != samplers->end(); ++iter) { - if (*iter == sampler) { - exists = true; - break; - } - } - if (!exists) { - samplers->push_back(sampler); - } - } - } - - static void RemoveSampler(Sampler* sampler) { - AtomicGuard atomic_guard(&samplers_access_counter_); - DCHECK(sampler->IsActive() || sampler->IsRegistered()); - // Remove sampler from map. - pthread_t thread_id = sampler->platform_data()->vm_tid(); - void* thread_key = ThreadKey(thread_id); - uint32_t thread_hash = ThreadHash(thread_id); - HashMap::Entry* entry = sampler_map_.Get().Lookup(thread_key, thread_hash); - DCHECK(entry != NULL); - SamplerList* samplers = reinterpret_cast(entry->value); - for (SamplerListIterator iter = samplers->begin(); iter != samplers->end(); - ++iter) { - if (*iter == sampler) { - samplers->erase(iter); - break; - } - } - if (samplers->empty()) { - sampler_map_.Pointer()->Remove(thread_key, thread_hash); - delete samplers; - } - } - - private: - struct HashMapCreateTrait { - static void Construct(HashMap* allocated_ptr) { - new (allocated_ptr) HashMap(HashMap::PointersMatch); - } - }; - friend class SignalHandler; - static base::LazyInstance::type - sampler_map_; - static base::AtomicValue samplers_access_counter_; -}; - -base::LazyInstance::type - SamplerManager::sampler_map_ = LAZY_INSTANCE_INITIALIZER; -base::AtomicValue SamplerManager::samplers_access_counter_(0); - - -#elif V8_OS_WIN || V8_OS_CYGWIN - -// ---------------------------------------------------------------------------- -// Win32 profiler support. On Cygwin we use the same sampler implementation as -// on Win32. - -class Sampler::PlatformData { - public: - // Get a handle to the calling thread. This is the thread that we are - // going to profile. We need to make a copy of the handle because we are - // going to use it in the sampler thread. Using GetThreadHandle() will - // not work in this case. We're using OpenThread because DuplicateHandle - // for some reason doesn't work in Chrome's sandbox. - PlatformData() - : profiled_thread_(OpenThread(THREAD_GET_CONTEXT | - THREAD_SUSPEND_RESUME | - THREAD_QUERY_INFORMATION, - false, - GetCurrentThreadId())) {} - - ~PlatformData() { - if (profiled_thread_ != NULL) { - CloseHandle(profiled_thread_); - profiled_thread_ = NULL; - } - } - - HANDLE profiled_thread() { return profiled_thread_; } - - private: - HANDLE profiled_thread_; -}; -#endif // USE_SIGNALS - - -#if defined(USE_SIGNALS) -class SignalHandler { - public: - static void SetUp() { if (!mutex_) mutex_ = new base::Mutex(); } - static void TearDown() { delete mutex_; mutex_ = NULL; } - - static void IncreaseSamplerCount() { - base::LockGuard lock_guard(mutex_); - if (++client_count_ == 1) Install(); - } - - static void DecreaseSamplerCount() { - base::LockGuard lock_guard(mutex_); - if (--client_count_ == 0) Restore(); - } - - static bool Installed() { - return signal_handler_installed_; - } - - private: - static void Install() { -#if !V8_OS_NACL - struct sigaction sa; - sa.sa_sigaction = &HandleProfilerSignal; - sigemptyset(&sa.sa_mask); -#if V8_OS_QNX - sa.sa_flags = SA_SIGINFO; -#else - sa.sa_flags = SA_RESTART | SA_SIGINFO; -#endif - signal_handler_installed_ = - (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); -#endif // !V8_OS_NACL - } - - static void Restore() { -#if !V8_OS_NACL - if (signal_handler_installed_) { - sigaction(SIGPROF, &old_signal_handler_, 0); - signal_handler_installed_ = false; - } -#endif - } - -#if !V8_OS_NACL - static void FillRegisterState(void* context, RegisterState* regs); - static void HandleProfilerSignal(int signal, siginfo_t* info, void* context); -#endif - // Protects the process wide state below. - static base::Mutex* mutex_; - static int client_count_; - static bool signal_handler_installed_; - static struct sigaction old_signal_handler_; -}; - - -base::Mutex* SignalHandler::mutex_ = NULL; -int SignalHandler::client_count_ = 0; -struct sigaction SignalHandler::old_signal_handler_; -bool SignalHandler::signal_handler_installed_ = false; - - -// As Native Client does not support signal handling, profiling is disabled. -#if !V8_OS_NACL -void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info, - void* context) { - USE(info); - if (signal != SIGPROF) return; - AtomicGuard atomic_guard(&SamplerManager::samplers_access_counter_, false); - if (!atomic_guard.is_success()) return; - pthread_t thread_id = pthread_self(); - HashMap::Entry* entry = - SamplerManager::sampler_map_.Pointer()->Lookup(ThreadKey(thread_id), - ThreadHash(thread_id)); - if (entry == NULL) return; - SamplerList* samplers = reinterpret_cast(entry->value); - - v8::RegisterState state; - FillRegisterState(context, &state); - - for (int i = 0; i < samplers->size(); ++i) { - Sampler* sampler = (*samplers)[i]; - Isolate* isolate = sampler->isolate(); - - // We require a fully initialized and entered isolate. - if (isolate == NULL || !isolate->IsInUse()) return; - - if (v8::Locker::IsActive() && !Locker::IsLocked(isolate)) return; - - sampler->SampleStack(state); - } -} - -void SignalHandler::FillRegisterState(void* context, RegisterState* state) { - // Extracting the sample from the context is extremely machine dependent. - ucontext_t* ucontext = reinterpret_cast(context); -#if !(V8_OS_OPENBSD || (V8_OS_LINUX && (V8_HOST_ARCH_PPC || V8_HOST_ARCH_S390))) - mcontext_t& mcontext = ucontext->uc_mcontext; -#endif -#if V8_OS_LINUX -#if V8_HOST_ARCH_IA32 - state->pc = reinterpret_cast(mcontext.gregs[REG_EIP]); - state->sp = reinterpret_cast(mcontext.gregs[REG_ESP]); - state->fp = reinterpret_cast(mcontext.gregs[REG_EBP]); -#elif V8_HOST_ARCH_X64 - state->pc = reinterpret_cast(mcontext.gregs[REG_RIP]); - state->sp = reinterpret_cast(mcontext.gregs[REG_RSP]); - state->fp = reinterpret_cast(mcontext.gregs[REG_RBP]); -#elif V8_HOST_ARCH_ARM -#if V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4) - // Old GLibc ARM versions used a gregs[] array to access the register - // values from mcontext_t. - state->pc = reinterpret_cast(mcontext.gregs[R15]); - state->sp = reinterpret_cast(mcontext.gregs[R13]); - state->fp = reinterpret_cast(mcontext.gregs[R11]); -#else - state->pc = reinterpret_cast(mcontext.arm_pc); - state->sp = reinterpret_cast(mcontext.arm_sp); - state->fp = reinterpret_cast(mcontext.arm_fp); -#endif // V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4) -#elif V8_HOST_ARCH_ARM64 - state->pc = reinterpret_cast(mcontext.pc); - state->sp = reinterpret_cast(mcontext.sp); - // FP is an alias for x29. - state->fp = reinterpret_cast(mcontext.regs[29]); -#elif V8_HOST_ARCH_MIPS - state->pc = reinterpret_cast(mcontext.pc); - state->sp = reinterpret_cast(mcontext.gregs[29]); - state->fp = reinterpret_cast(mcontext.gregs[30]); -#elif V8_HOST_ARCH_MIPS64 - state->pc = reinterpret_cast(mcontext.pc); - state->sp = reinterpret_cast(mcontext.gregs[29]); - state->fp = reinterpret_cast(mcontext.gregs[30]); -#elif V8_HOST_ARCH_PPC - state->pc = reinterpret_cast(ucontext->uc_mcontext.regs->nip); - state->sp = - reinterpret_cast(ucontext->uc_mcontext.regs->gpr[PT_R1]); - state->fp = - reinterpret_cast(ucontext->uc_mcontext.regs->gpr[PT_R31]); -#elif V8_HOST_ARCH_S390 -#if V8_TARGET_ARCH_32_BIT - // 31-bit target will have bit 0 (MSB) of the PSW set to denote addressing - // mode. This bit needs to be masked out to resolve actual address. - state->pc = - reinterpret_cast(ucontext->uc_mcontext.psw.addr & 0x7FFFFFFF); -#else - state->pc = reinterpret_cast(ucontext->uc_mcontext.psw.addr); -#endif // V8_TARGET_ARCH_32_BIT - state->sp = reinterpret_cast(ucontext->uc_mcontext.gregs[15]); - state->fp = reinterpret_cast(ucontext->uc_mcontext.gregs[11]); -#endif // V8_HOST_ARCH_* -#elif V8_OS_MACOSX -#if V8_HOST_ARCH_X64 -#if __DARWIN_UNIX03 - state->pc = reinterpret_cast(mcontext->__ss.__rip); - state->sp = reinterpret_cast(mcontext->__ss.__rsp); - state->fp = reinterpret_cast(mcontext->__ss.__rbp); -#else // !__DARWIN_UNIX03 - state->pc = reinterpret_cast(mcontext->ss.rip); - state->sp = reinterpret_cast(mcontext->ss.rsp); - state->fp = reinterpret_cast(mcontext->ss.rbp); -#endif // __DARWIN_UNIX03 -#elif V8_HOST_ARCH_IA32 -#if __DARWIN_UNIX03 - state->pc = reinterpret_cast(mcontext->__ss.__eip); - state->sp = reinterpret_cast(mcontext->__ss.__esp); - state->fp = reinterpret_cast(mcontext->__ss.__ebp); -#else // !__DARWIN_UNIX03 - state->pc = reinterpret_cast(mcontext->ss.eip); - state->sp = reinterpret_cast(mcontext->ss.esp); - state->fp = reinterpret_cast(mcontext->ss.ebp); -#endif // __DARWIN_UNIX03 -#endif // V8_HOST_ARCH_IA32 -#elif V8_OS_FREEBSD -#if V8_HOST_ARCH_IA32 - state->pc = reinterpret_cast(mcontext.mc_eip); - state->sp = reinterpret_cast(mcontext.mc_esp); - state->fp = reinterpret_cast(mcontext.mc_ebp); -#elif V8_HOST_ARCH_X64 - state->pc = reinterpret_cast(mcontext.mc_rip); - state->sp = reinterpret_cast(mcontext.mc_rsp); - state->fp = reinterpret_cast(mcontext.mc_rbp); -#elif V8_HOST_ARCH_ARM - state->pc = reinterpret_cast(mcontext.mc_r15); - state->sp = reinterpret_cast(mcontext.mc_r13); - state->fp = reinterpret_cast(mcontext.mc_r11); -#endif // V8_HOST_ARCH_* -#elif V8_OS_NETBSD -#if V8_HOST_ARCH_IA32 - state->pc = reinterpret_cast(mcontext.__gregs[_REG_EIP]); - state->sp = reinterpret_cast(mcontext.__gregs[_REG_ESP]); - state->fp = reinterpret_cast(mcontext.__gregs[_REG_EBP]); -#elif V8_HOST_ARCH_X64 - state->pc = reinterpret_cast(mcontext.__gregs[_REG_RIP]); - state->sp = reinterpret_cast(mcontext.__gregs[_REG_RSP]); - state->fp = reinterpret_cast(mcontext.__gregs[_REG_RBP]); -#endif // V8_HOST_ARCH_* -#elif V8_OS_OPENBSD -#if V8_HOST_ARCH_IA32 - state->pc = reinterpret_cast(ucontext->sc_eip); - state->sp = reinterpret_cast(ucontext->sc_esp); - state->fp = reinterpret_cast(ucontext->sc_ebp); -#elif V8_HOST_ARCH_X64 - state->pc = reinterpret_cast(ucontext->sc_rip); - state->sp = reinterpret_cast(ucontext->sc_rsp); - state->fp = reinterpret_cast(ucontext->sc_rbp); -#endif // V8_HOST_ARCH_* -#elif V8_OS_SOLARIS - state->pc = reinterpret_cast(mcontext.gregs[REG_PC]); - state->sp = reinterpret_cast(mcontext.gregs[REG_SP]); - state->fp = reinterpret_cast(mcontext.gregs[REG_FP]); -#elif V8_OS_QNX -#if V8_HOST_ARCH_IA32 - state->pc = reinterpret_cast(mcontext.cpu.eip); - state->sp = reinterpret_cast(mcontext.cpu.esp); - state->fp = reinterpret_cast(mcontext.cpu.ebp); -#elif V8_HOST_ARCH_ARM - state->pc = reinterpret_cast(mcontext.cpu.gpr[ARM_REG_PC]); - state->sp = reinterpret_cast(mcontext.cpu.gpr[ARM_REG_SP]); - state->fp = reinterpret_cast(mcontext.cpu.gpr[ARM_REG_FP]); -#endif // V8_HOST_ARCH_* -#elif V8_OS_AIX - state->pc = reinterpret_cast(mcontext.jmp_context.iar); - state->sp = reinterpret_cast(mcontext.jmp_context.gpr[1]); - state->fp = reinterpret_cast(mcontext.jmp_context.gpr[31]); -#endif // V8_OS_AIX -} - -#endif // !V8_OS_NACL - -#endif // USE_SIGNALS - - -void Sampler::SetUp() { -#if defined(USE_SIGNALS) - SignalHandler::SetUp(); -#endif -} - - -void Sampler::TearDown() { -#if defined(USE_SIGNALS) - SignalHandler::TearDown(); -#endif -} - -Sampler::Sampler(Isolate* isolate) - : is_counting_samples_(false), - js_sample_count_(0), - external_sample_count_(0), - isolate_(isolate), - profiling_(false), - has_processing_thread_(false), - active_(false), - registered_(false) { - data_ = new PlatformData; -} - -Sampler::~Sampler() { - DCHECK(!IsActive()); -#if defined(USE_SIGNALS) - if (IsRegistered()) { - SamplerManager::RemoveSampler(this); - } -#endif - delete data_; -} - -void Sampler::Start() { - DCHECK(!IsActive()); - SetActive(true); -#if defined(USE_SIGNALS) - SamplerManager::AddSampler(this); -#endif -} - - -void Sampler::Stop() { -#if defined(USE_SIGNALS) - SamplerManager::RemoveSampler(this); -#endif - DCHECK(IsActive()); - SetActive(false); - SetRegistered(false); -} - - -void Sampler::IncreaseProfilingDepth() { - base::NoBarrier_AtomicIncrement(&profiling_, 1); -#if defined(USE_SIGNALS) - SignalHandler::IncreaseSamplerCount(); -#endif -} - - -void Sampler::DecreaseProfilingDepth() { -#if defined(USE_SIGNALS) - SignalHandler::DecreaseSamplerCount(); -#endif - base::NoBarrier_AtomicIncrement(&profiling_, -1); -} - - -#if defined(USE_SIGNALS) - -void Sampler::DoSample() { - if (!SignalHandler::Installed()) return; - if (!IsActive() && !IsRegistered()) { - SamplerManager::AddSampler(this); - SetRegistered(true); - } - pthread_kill(platform_data()->vm_tid(), SIGPROF); -} - -#elif V8_OS_WIN || V8_OS_CYGWIN - -void Sampler::DoSample() { - HANDLE profiled_thread = platform_data()->profiled_thread(); - if (profiled_thread == NULL) return; - - const DWORD kSuspendFailed = static_cast(-1); - if (SuspendThread(profiled_thread) == kSuspendFailed) return; - - // Context used for sampling the register state of the profiled thread. - CONTEXT context; - memset(&context, 0, sizeof(context)); - context.ContextFlags = CONTEXT_FULL; - if (GetThreadContext(profiled_thread, &context) != 0) { - v8::RegisterState state; -#if V8_HOST_ARCH_X64 - state.pc = reinterpret_cast(context.Rip); - state.sp = reinterpret_cast(context.Rsp); - state.fp = reinterpret_cast(context.Rbp); -#else - state.pc = reinterpret_cast(context.Eip); - state.sp = reinterpret_cast(context.Esp); - state.fp = reinterpret_cast(context.Ebp); -#endif - SampleStack(state); - } - ResumeThread(profiled_thread); -} - -#endif // USE_SIGNALS - -} // namespace sampler -} // namespace v8 diff --git a/src/log.cc b/src/log.cc index 859c51121ac..8ec267a6100 100644 --- a/src/log.cc +++ b/src/log.cc @@ -15,12 +15,11 @@ #include "src/global-handles.h" #include "src/interpreter/bytecodes.h" #include "src/interpreter/interpreter.h" -#include "src/libsampler/v8-sampler.h" #include "src/log-inl.h" #include "src/log-utils.h" #include "src/macro-assembler.h" #include "src/perf-jit.h" -#include "src/profiler/cpu-profiler-inl.h" +#include "src/profiler/cpu-profiler.h" #include "src/runtime-profiler.h" #include "src/string-stream.h" #include "src/vm-state-inl.h" @@ -540,31 +539,6 @@ void JitLogger::EndCodePosInfoEvent(AbstractCode* code, } -// TODO(lpy): Keeping sampling thread inside V8 is a workaround currently, -// the reason is to reduce code duplication during migration to sampler library, -// sampling thread, as well as the sampler, will be moved to D8 eventually. -class SamplingThread : public base::Thread { - public: - static const int kSamplingThreadStackSize = 64 * KB; - - SamplingThread(sampler::Sampler* sampler, int interval) - : base::Thread(base::Thread::Options("SamplingThread", - kSamplingThreadStackSize)), - sampler_(sampler), - interval_(interval) {} - void Run() override { - while (sampler_->IsProfiling()) { - sampler_->DoSample(); - base::OS::Sleep(base::TimeDelta::FromMilliseconds(interval_)); - } - } - - private: - sampler::Sampler* sampler_; - const int interval_; -}; - - // The Profiler samples pc and sp values for the main thread. // Each sample is appended to a circular buffer. // An independent thread removes data and writes it to the log. @@ -637,16 +611,16 @@ class Profiler: public base::Thread { // Ticker used to provide ticks to the profiler and the sliding state // window. // -class Ticker: public sampler::Sampler { +class Ticker: public Sampler { public: Ticker(Isolate* isolate, int interval): - sampler::Sampler(reinterpret_cast(isolate)), - profiler_(NULL), - sampling_thread_(new SamplingThread(this, interval)) {} + Sampler(isolate, interval), + profiler_(NULL) {} - ~Ticker() { - if (IsActive()) Stop(); - delete sampling_thread_; + ~Ticker() { if (IsActive()) Stop(); } + + virtual void Tick(TickSample* sample) { + if (profiler_) profiler_->Insert(sample); } void SetProfiler(Profiler* profiler) { @@ -654,40 +628,16 @@ class Ticker: public sampler::Sampler { profiler_ = profiler; IncreaseProfilingDepth(); if (!IsActive()) Start(); - sampling_thread_->StartSynchronously(); } void ClearProfiler() { profiler_ = NULL; if (IsActive()) Stop(); DecreaseProfilingDepth(); - sampling_thread_->Join(); - } - - void SampleStack(const v8::RegisterState& state) override { - v8::Isolate* v8_isolate = isolate(); - Isolate* isolate = reinterpret_cast(v8_isolate); -#if defined(USE_SIMULATOR) - SimulatorHelper::FillRegisters(isolate, - const_cast(&state)); -#endif - TickSample* sample = isolate->cpu_profiler()->StartTickSample(); - TickSample sample_obj; - if (sample == NULL) sample = &sample_obj; - sample->Init(isolate, state, TickSample::kIncludeCEntryFrame, true); - if (is_counting_samples_ && !sample->timestamp.IsNull()) { - if (sample->state == JS) ++js_sample_count_; - if (sample->state == EXTERNAL) ++external_sample_count_; - } - if (profiler_) profiler_->Insert(sample); - if (sample != &sample_obj) { - isolate->cpu_profiler()->FinishTickSample(); - } } private: Profiler* profiler_; - SamplingThread* sampling_thread_; }; @@ -1860,7 +1810,7 @@ void Logger::SetCodeEventHandler(uint32_t options, } -sampler::Sampler* Logger::sampler() { +Sampler* Logger::sampler() { return ticker_; } diff --git a/src/log.h b/src/log.h index dec389a3a9a..9953b4cd873 100644 --- a/src/log.h +++ b/src/log.h @@ -19,10 +19,6 @@ namespace base { class Semaphore; } -namespace sampler { -class Sampler; -} - namespace internal { // Logger is used for collecting logging information from V8 during @@ -145,6 +141,7 @@ class JitLogger; class PerfBasicLogger; class LowLevelLogger; class PerfJitLogger; +class Sampler; class Logger { public: @@ -164,7 +161,7 @@ class Logger { void SetCodeEventHandler(uint32_t options, JitCodeEventHandler event_handler); - sampler::Sampler* sampler(); + Sampler* sampler(); // Frees resources acquired in SetUp. // When a temporary file is used for the log, returns its stream descriptor, diff --git a/src/profiler/cpu-profiler.cc b/src/profiler/cpu-profiler.cc index dbd0434b0aa..5e4a444e8c9 100644 --- a/src/profiler/cpu-profiler.cc +++ b/src/profiler/cpu-profiler.cc @@ -21,7 +21,7 @@ static const int kProfilerStackSize = 64 * KB; ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator, - sampler::Sampler* sampler, + Sampler* sampler, base::TimeDelta period) : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)), generator_(generator), @@ -566,7 +566,7 @@ void CpuProfiler::StartProcessorIfNotStarted() { saved_is_logging_ = logger->is_logging_; logger->is_logging_ = false; generator_ = new ProfileGenerator(profiles_); - sampler::Sampler* sampler = logger->sampler(); + Sampler* sampler = logger->sampler(); processor_ = new ProfilerEventsProcessor( generator_, sampler, sampling_interval_); is_profiling_ = true; @@ -612,8 +612,7 @@ void CpuProfiler::StopProcessorIfLastProfile(const char* title) { void CpuProfiler::StopProcessor() { Logger* logger = isolate_->logger(); - sampler::Sampler* sampler = - reinterpret_cast(logger->ticker_); + Sampler* sampler = reinterpret_cast(logger->ticker_); is_profiling_ = false; processor_->StopSynchronously(); delete processor_; diff --git a/src/profiler/cpu-profiler.h b/src/profiler/cpu-profiler.h index 4261c367e0e..ed1e15fb32d 100644 --- a/src/profiler/cpu-profiler.h +++ b/src/profiler/cpu-profiler.h @@ -10,9 +10,9 @@ #include "src/base/atomicops.h" #include "src/base/platform/time.h" #include "src/compiler.h" -#include "src/libsampler/v8-sampler.h" #include "src/locked-queue.h" #include "src/profiler/circular-queue.h" +#include "src/profiler/sampler.h" #include "src/profiler/tick-sample.h" namespace v8 { @@ -128,7 +128,7 @@ class CodeEventsContainer { class ProfilerEventsProcessor : public base::Thread { public: ProfilerEventsProcessor(ProfileGenerator* generator, - sampler::Sampler* sampler, + Sampler* sampler, base::TimeDelta period); virtual ~ProfilerEventsProcessor(); @@ -166,7 +166,7 @@ class ProfilerEventsProcessor : public base::Thread { SampleProcessingResult ProcessOneSample(); ProfileGenerator* generator_; - sampler::Sampler* sampler_; + Sampler* sampler_; base::Atomic32 running_; const base::TimeDelta period_; // Samples & code events processing period. LockedQueue events_buffer_; diff --git a/src/profiler/sampler.cc b/src/profiler/sampler.cc new file mode 100644 index 00000000000..ae47dca1504 --- /dev/null +++ b/src/profiler/sampler.cc @@ -0,0 +1,828 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/profiler/sampler.h" + +#if V8_OS_POSIX && !V8_OS_CYGWIN + +#define USE_SIGNALS + +#include +#include +#include +#include + +#if !V8_OS_QNX && !V8_OS_NACL && !V8_OS_AIX +#include // NOLINT +#endif + +#if V8_OS_MACOSX +#include +// OpenBSD doesn't have . ucontext_t lives in +// and is a typedef for struct sigcontext. There is no uc_mcontext. +#elif(!V8_OS_ANDROID || defined(__BIONIC_HAVE_UCONTEXT_T)) && \ + !V8_OS_OPENBSD && !V8_OS_NACL +#include +#endif + +#include + +// GLibc on ARM defines mcontext_t has a typedef for 'struct sigcontext'. +// Old versions of the C library didn't define the type. +#if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T) && \ + (defined(__arm__) || defined(__aarch64__)) && \ + !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT) +#include // NOLINT +#endif + +#elif V8_OS_WIN || V8_OS_CYGWIN + +#include "src/base/win32-headers.h" + +#endif + +#include "src/base/atomic-utils.h" +#include "src/base/platform/platform.h" +#include "src/profiler/cpu-profiler-inl.h" +#include "src/profiler/tick-sample.h" +#include "src/simulator.h" +#include "src/v8threads.h" + + +#if V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T) + +// Not all versions of Android's C library provide ucontext_t. +// Detect this and provide custom but compatible definitions. Note that these +// follow the GLibc naming convention to access register values from +// mcontext_t. +// +// See http://code.google.com/p/android/issues/detail?id=34784 + +#if defined(__arm__) + +typedef struct sigcontext mcontext_t; + +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + // Other fields are not used by V8, don't define them here. +} ucontext_t; + +#elif defined(__aarch64__) + +typedef struct sigcontext mcontext_t; + +typedef struct ucontext { + uint64_t uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + // Other fields are not used by V8, don't define them here. +} ucontext_t; + +#elif defined(__mips__) +// MIPS version of sigcontext, for Android bionic. +typedef struct { + uint32_t regmask; + uint32_t status; + uint64_t pc; + uint64_t gregs[32]; + uint64_t fpregs[32]; + uint32_t acx; + uint32_t fpc_csr; + uint32_t fpc_eir; + uint32_t used_math; + uint32_t dsp; + uint64_t mdhi; + uint64_t mdlo; + uint32_t hi1; + uint32_t lo1; + uint32_t hi2; + uint32_t lo2; + uint32_t hi3; + uint32_t lo3; +} mcontext_t; + +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + // Other fields are not used by V8, don't define them here. +} ucontext_t; + +#elif defined(__i386__) +// x86 version for Android. +typedef struct { + uint32_t gregs[19]; + void* fpregs; + uint32_t oldmask; + uint32_t cr2; +} mcontext_t; + +typedef uint32_t kernel_sigset_t[2]; // x86 kernel uses 64-bit signal masks +typedef struct ucontext { + uint32_t uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + // Other fields are not used by V8, don't define them here. +} ucontext_t; +enum { REG_EBP = 6, REG_ESP = 7, REG_EIP = 14 }; + +#elif defined(__x86_64__) +// x64 version for Android. +typedef struct { + uint64_t gregs[23]; + void* fpregs; + uint64_t __reserved1[8]; +} mcontext_t; + +typedef struct ucontext { + uint64_t uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + // Other fields are not used by V8, don't define them here. +} ucontext_t; +enum { REG_RBP = 10, REG_RSP = 15, REG_RIP = 16 }; +#endif + +#endif // V8_OS_ANDROID && !defined(__BIONIC_HAVE_UCONTEXT_T) + + +namespace v8 { +namespace internal { + +namespace { + +class PlatformDataCommon : public Malloced { + public: + PlatformDataCommon() : profiled_thread_id_(ThreadId::Current()) {} + ThreadId profiled_thread_id() { return profiled_thread_id_; } + + protected: + ~PlatformDataCommon() {} + + private: + ThreadId profiled_thread_id_; +}; + + +typedef List SamplerList; + +#if defined(USE_SIGNALS) +class AtomicGuard { + public: + explicit AtomicGuard(base::AtomicValue* atomic, bool is_block = true) + : atomic_(atomic), + is_success_(false) { + do { + // Use Acquire_Load to gain mutual exclusion. + USE(atomic_->Value()); + is_success_ = atomic_->TrySetValue(0, 1); + } while (is_block && !is_success_); + } + + bool is_success() { return is_success_; } + + ~AtomicGuard() { + if (is_success_) { + atomic_->SetValue(0); + } + atomic_ = NULL; + } + + private: + base::AtomicValue* atomic_; + bool is_success_; +}; + + +// Returns key for hash map. +void* ThreadKey(pthread_t thread_id) { + return reinterpret_cast(thread_id); +} + + +// Returns hash value for hash map. +uint32_t ThreadHash(pthread_t thread_id) { +#if V8_OS_MACOSX + return static_cast(reinterpret_cast(thread_id)); +#else + return static_cast(thread_id); +#endif +} +#endif // USE_SIGNALS + +} // namespace + +#if defined(USE_SIGNALS) + +class Sampler::PlatformData : public PlatformDataCommon { + public: + PlatformData() : vm_tid_(pthread_self()) {} + pthread_t vm_tid() const { return vm_tid_; } + + private: + pthread_t vm_tid_; +}; + +#elif V8_OS_WIN || V8_OS_CYGWIN + +// ---------------------------------------------------------------------------- +// Win32 profiler support. On Cygwin we use the same sampler implementation as +// on Win32. + +class Sampler::PlatformData : public PlatformDataCommon { + public: + // Get a handle to the calling thread. This is the thread that we are + // going to profile. We need to make a copy of the handle because we are + // going to use it in the sampler thread. Using GetThreadHandle() will + // not work in this case. We're using OpenThread because DuplicateHandle + // for some reason doesn't work in Chrome's sandbox. + PlatformData() + : profiled_thread_(OpenThread(THREAD_GET_CONTEXT | + THREAD_SUSPEND_RESUME | + THREAD_QUERY_INFORMATION, + false, + GetCurrentThreadId())) {} + + ~PlatformData() { + if (profiled_thread_ != NULL) { + CloseHandle(profiled_thread_); + profiled_thread_ = NULL; + } + } + + HANDLE profiled_thread() { return profiled_thread_; } + + private: + HANDLE profiled_thread_; +}; +#endif + + +#if defined(USE_SIGNALS) + +class SignalHandler : public AllStatic { + public: + static void SetUp() { if (!mutex_) mutex_ = new base::Mutex(); } + static void TearDown() { delete mutex_; mutex_ = NULL; } + + static void IncreaseSamplerCount() { + base::LockGuard lock_guard(mutex_); + if (++client_count_ == 1) Install(); + } + + static void DecreaseSamplerCount() { + base::LockGuard lock_guard(mutex_); + if (--client_count_ == 0) Restore(); + } + + static bool Installed() { + return signal_handler_installed_; + } + +#if !V8_OS_NACL + static void CollectSample(void* context, Sampler* sampler); +#endif + + private: + static void Install() { +#if !V8_OS_NACL + struct sigaction sa; + sa.sa_sigaction = &HandleProfilerSignal; + sigemptyset(&sa.sa_mask); +#if V8_OS_QNX + sa.sa_flags = SA_SIGINFO; +#else + sa.sa_flags = SA_RESTART | SA_SIGINFO; +#endif + signal_handler_installed_ = + (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0); +#endif + } + + static void Restore() { +#if !V8_OS_NACL + if (signal_handler_installed_) { + sigaction(SIGPROF, &old_signal_handler_, 0); + signal_handler_installed_ = false; + } +#endif + } + +#if !V8_OS_NACL + static void HandleProfilerSignal(int signal, siginfo_t* info, void* context); +#endif + // Protects the process wide state below. + static base::Mutex* mutex_; + static int client_count_; + static bool signal_handler_installed_; + static struct sigaction old_signal_handler_; +}; + + +base::Mutex* SignalHandler::mutex_ = NULL; +int SignalHandler::client_count_ = 0; +struct sigaction SignalHandler::old_signal_handler_; +bool SignalHandler::signal_handler_installed_ = false; + + +// As Native Client does not support signal handling, profiling is disabled. +#if !V8_OS_NACL +void SignalHandler::CollectSample(void* context, Sampler* sampler) { + if (sampler == NULL || (!sampler->IsProfiling() && + !sampler->IsRegistered())) { + return; + } + Isolate* isolate = sampler->isolate(); + + // We require a fully initialized and entered isolate. + if (isolate == NULL || !isolate->IsInUse()) return; + + if (v8::Locker::IsActive() && + !isolate->thread_manager()->IsLockedByCurrentThread()) { + return; + } + + v8::RegisterState state; + +#if defined(USE_SIMULATOR) + if (!SimulatorHelper::FillRegisters(isolate, &state)) return; +#else + // Extracting the sample from the context is extremely machine dependent. + ucontext_t* ucontext = reinterpret_cast(context); +#if !(V8_OS_OPENBSD || (V8_OS_LINUX && (V8_HOST_ARCH_PPC || V8_HOST_ARCH_S390))) + mcontext_t& mcontext = ucontext->uc_mcontext; +#endif +#if V8_OS_LINUX +#if V8_HOST_ARCH_IA32 + state.pc = reinterpret_cast
(mcontext.gregs[REG_EIP]); + state.sp = reinterpret_cast
(mcontext.gregs[REG_ESP]); + state.fp = reinterpret_cast
(mcontext.gregs[REG_EBP]); +#elif V8_HOST_ARCH_X64 + state.pc = reinterpret_cast
(mcontext.gregs[REG_RIP]); + state.sp = reinterpret_cast
(mcontext.gregs[REG_RSP]); + state.fp = reinterpret_cast
(mcontext.gregs[REG_RBP]); +#elif V8_HOST_ARCH_ARM +#if V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4) + // Old GLibc ARM versions used a gregs[] array to access the register + // values from mcontext_t. + state.pc = reinterpret_cast
(mcontext.gregs[R15]); + state.sp = reinterpret_cast
(mcontext.gregs[R13]); + state.fp = reinterpret_cast
(mcontext.gregs[R11]); +#else + state.pc = reinterpret_cast
(mcontext.arm_pc); + state.sp = reinterpret_cast
(mcontext.arm_sp); + state.fp = reinterpret_cast
(mcontext.arm_fp); +#endif // V8_LIBC_GLIBC && !V8_GLIBC_PREREQ(2, 4) +#elif V8_HOST_ARCH_ARM64 + state.pc = reinterpret_cast
(mcontext.pc); + state.sp = reinterpret_cast
(mcontext.sp); + // FP is an alias for x29. + state.fp = reinterpret_cast
(mcontext.regs[29]); +#elif V8_HOST_ARCH_MIPS + state.pc = reinterpret_cast
(mcontext.pc); + state.sp = reinterpret_cast
(mcontext.gregs[29]); + state.fp = reinterpret_cast
(mcontext.gregs[30]); +#elif V8_HOST_ARCH_MIPS64 + state.pc = reinterpret_cast
(mcontext.pc); + state.sp = reinterpret_cast
(mcontext.gregs[29]); + state.fp = reinterpret_cast
(mcontext.gregs[30]); +#elif V8_HOST_ARCH_PPC + state.pc = reinterpret_cast
(ucontext->uc_mcontext.regs->nip); + state.sp = reinterpret_cast
(ucontext->uc_mcontext.regs->gpr[PT_R1]); + state.fp = reinterpret_cast
(ucontext->uc_mcontext.regs->gpr[PT_R31]); +#elif V8_HOST_ARCH_S390 +#if V8_TARGET_ARCH_32_BIT + // 31-bit target will have bit 0 (MSB) of the PSW set to denote addressing + // mode. This bit needs to be masked out to resolve actual address. + state.pc = + reinterpret_cast
(ucontext->uc_mcontext.psw.addr & 0x7FFFFFFF); +#else + state.pc = reinterpret_cast
(ucontext->uc_mcontext.psw.addr); +#endif // V8_TARGET_ARCH_32_BIT + state.sp = reinterpret_cast
(ucontext->uc_mcontext.gregs[15]); + state.fp = reinterpret_cast
(ucontext->uc_mcontext.gregs[11]); +#endif // V8_HOST_ARCH_* +#elif V8_OS_MACOSX +#if V8_HOST_ARCH_X64 +#if __DARWIN_UNIX03 + state.pc = reinterpret_cast
(mcontext->__ss.__rip); + state.sp = reinterpret_cast
(mcontext->__ss.__rsp); + state.fp = reinterpret_cast
(mcontext->__ss.__rbp); +#else // !__DARWIN_UNIX03 + state.pc = reinterpret_cast
(mcontext->ss.rip); + state.sp = reinterpret_cast
(mcontext->ss.rsp); + state.fp = reinterpret_cast
(mcontext->ss.rbp); +#endif // __DARWIN_UNIX03 +#elif V8_HOST_ARCH_IA32 +#if __DARWIN_UNIX03 + state.pc = reinterpret_cast
(mcontext->__ss.__eip); + state.sp = reinterpret_cast
(mcontext->__ss.__esp); + state.fp = reinterpret_cast
(mcontext->__ss.__ebp); +#else // !__DARWIN_UNIX03 + state.pc = reinterpret_cast
(mcontext->ss.eip); + state.sp = reinterpret_cast
(mcontext->ss.esp); + state.fp = reinterpret_cast
(mcontext->ss.ebp); +#endif // __DARWIN_UNIX03 +#endif // V8_HOST_ARCH_IA32 +#elif V8_OS_FREEBSD +#if V8_HOST_ARCH_IA32 + state.pc = reinterpret_cast
(mcontext.mc_eip); + state.sp = reinterpret_cast
(mcontext.mc_esp); + state.fp = reinterpret_cast
(mcontext.mc_ebp); +#elif V8_HOST_ARCH_X64 + state.pc = reinterpret_cast
(mcontext.mc_rip); + state.sp = reinterpret_cast
(mcontext.mc_rsp); + state.fp = reinterpret_cast
(mcontext.mc_rbp); +#elif V8_HOST_ARCH_ARM + state.pc = reinterpret_cast
(mcontext.mc_r15); + state.sp = reinterpret_cast
(mcontext.mc_r13); + state.fp = reinterpret_cast
(mcontext.mc_r11); +#endif // V8_HOST_ARCH_* +#elif V8_OS_NETBSD +#if V8_HOST_ARCH_IA32 + state.pc = reinterpret_cast
(mcontext.__gregs[_REG_EIP]); + state.sp = reinterpret_cast
(mcontext.__gregs[_REG_ESP]); + state.fp = reinterpret_cast
(mcontext.__gregs[_REG_EBP]); +#elif V8_HOST_ARCH_X64 + state.pc = reinterpret_cast
(mcontext.__gregs[_REG_RIP]); + state.sp = reinterpret_cast
(mcontext.__gregs[_REG_RSP]); + state.fp = reinterpret_cast
(mcontext.__gregs[_REG_RBP]); +#endif // V8_HOST_ARCH_* +#elif V8_OS_OPENBSD +#if V8_HOST_ARCH_IA32 + state.pc = reinterpret_cast
(ucontext->sc_eip); + state.sp = reinterpret_cast
(ucontext->sc_esp); + state.fp = reinterpret_cast
(ucontext->sc_ebp); +#elif V8_HOST_ARCH_X64 + state.pc = reinterpret_cast
(ucontext->sc_rip); + state.sp = reinterpret_cast
(ucontext->sc_rsp); + state.fp = reinterpret_cast
(ucontext->sc_rbp); +#endif // V8_HOST_ARCH_* +#elif V8_OS_SOLARIS + state.pc = reinterpret_cast
(mcontext.gregs[REG_PC]); + state.sp = reinterpret_cast
(mcontext.gregs[REG_SP]); + state.fp = reinterpret_cast
(mcontext.gregs[REG_FP]); +#elif V8_OS_QNX +#if V8_HOST_ARCH_IA32 + state.pc = reinterpret_cast
(mcontext.cpu.eip); + state.sp = reinterpret_cast
(mcontext.cpu.esp); + state.fp = reinterpret_cast
(mcontext.cpu.ebp); +#elif V8_HOST_ARCH_ARM + state.pc = reinterpret_cast
(mcontext.cpu.gpr[ARM_REG_PC]); + state.sp = reinterpret_cast
(mcontext.cpu.gpr[ARM_REG_SP]); + state.fp = reinterpret_cast
(mcontext.cpu.gpr[ARM_REG_FP]); +#endif // V8_HOST_ARCH_* +#elif V8_OS_AIX + state.pc = reinterpret_cast
(mcontext.jmp_context.iar); + state.sp = reinterpret_cast
(mcontext.jmp_context.gpr[1]); + state.fp = reinterpret_cast
(mcontext.jmp_context.gpr[31]); +#endif // V8_OS_AIX +#endif // USE_SIMULATOR + sampler->SampleStack(state); +} +#endif // V8_OS_NACL + +#endif // USE_SIGNALS + + +class SamplerThread : public base::Thread { + public: + static const int kSamplerThreadStackSize = 64 * KB; + + explicit SamplerThread(int interval) + : Thread(base::Thread::Options("SamplerThread", kSamplerThreadStackSize)), + interval_(interval) {} + + static void SetUp() { if (!mutex_) mutex_ = new base::Mutex(); } + static void TearDown() { delete mutex_; mutex_ = NULL; } + + static void AddActiveSampler(Sampler* sampler) { + bool need_to_start = false; + base::LockGuard lock_guard(mutex_); + if (instance_ == NULL) { + // Start a thread that will send SIGPROF signal to VM threads, + // when CPU profiling will be enabled. + instance_ = new SamplerThread(sampler->interval()); + need_to_start = true; + } + + DCHECK(sampler->IsActive()); + DCHECK(instance_->interval_ == sampler->interval()); + +#if defined(USE_SIGNALS) + AddSampler(sampler); +#else + DCHECK(!instance_->active_samplers_.Contains(sampler)); + instance_->active_samplers_.Add(sampler); +#endif // USE_SIGNALS + + if (need_to_start) instance_->StartSynchronously(); + } + + static void RemoveSampler(Sampler* sampler) { + SamplerThread* instance_to_remove = NULL; + { + base::LockGuard lock_guard(mutex_); + + DCHECK(sampler->IsActive() || sampler->IsRegistered()); +#if defined(USE_SIGNALS) + { + AtomicGuard atomic_guard(&sampler_list_access_counter_); + // Remove sampler from map. + pthread_t thread_id = sampler->platform_data()->vm_tid(); + void* thread_key = ThreadKey(thread_id); + uint32_t thread_hash = ThreadHash(thread_id); + HashMap::Entry* entry = + thread_id_to_samplers_.Get().Lookup(thread_key, thread_hash); + DCHECK(entry != NULL); + SamplerList* samplers = reinterpret_cast(entry->value); + samplers->RemoveElement(sampler); + if (samplers->is_empty()) { + thread_id_to_samplers_.Pointer()->Remove(thread_key, thread_hash); + delete samplers; + } + if (thread_id_to_samplers_.Get().occupancy() == 0) { + instance_to_remove = instance_; + instance_ = NULL; + } + } +#else + bool removed = instance_->active_samplers_.RemoveElement(sampler); + DCHECK(removed); + USE(removed); + + // We cannot delete the instance immediately as we need to Join() the + // thread but we are holding mutex_ and the thread may try to acquire it. + if (instance_->active_samplers_.is_empty()) { + instance_to_remove = instance_; + instance_ = NULL; + } +#endif // USE_SIGNALS + } + + if (!instance_to_remove) return; + instance_to_remove->Join(); + delete instance_to_remove; + } + + // Unlike AddActiveSampler, this method only adds a sampler, + // but won't start the sampler thread. + static void RegisterSampler(Sampler* sampler) { + base::LockGuard lock_guard(mutex_); +#if defined(USE_SIGNALS) + AddSampler(sampler); +#endif // USE_SIGNALS + } + + // Implement Thread::Run(). + virtual void Run() { + while (true) { + { + base::LockGuard lock_guard(mutex_); +#if defined(USE_SIGNALS) + if (thread_id_to_samplers_.Get().occupancy() == 0) break; + if (SignalHandler::Installed()) { + for (HashMap::Entry *p = thread_id_to_samplers_.Get().Start(); + p != NULL; p = thread_id_to_samplers_.Get().Next(p)) { +#if V8_OS_AIX && V8_TARGET_ARCH_PPC64 + // on AIX64, cannot cast (void *) to pthread_t which is + // of type unsigned int (4bytes) + pthread_t thread_id = reinterpret_cast(p->key); +#else + pthread_t thread_id = reinterpret_cast(p->key); +#endif + pthread_kill(thread_id, SIGPROF); + } + } +#else + if (active_samplers_.is_empty()) break; + // When CPU profiling is enabled both JavaScript and C++ code is + // profiled. We must not suspend. + for (int i = 0; i < active_samplers_.length(); ++i) { + Sampler* sampler = active_samplers_.at(i); + if (!sampler->IsProfiling()) continue; + sampler->DoSample(); + } +#endif // USE_SIGNALS + } + base::OS::Sleep(base::TimeDelta::FromMilliseconds(interval_)); + } + } + + private: + // Protects the process wide state below. + static base::Mutex* mutex_; + static SamplerThread* instance_; + + const int interval_; + +#if defined(USE_SIGNALS) + struct HashMapCreateTrait { + static void Construct(HashMap* allocated_ptr) { + new (allocated_ptr) HashMap(HashMap::PointersMatch); + } + }; + friend class SignalHandler; + static base::LazyInstance::type + thread_id_to_samplers_; + static base::AtomicValue sampler_list_access_counter_; + static void AddSampler(Sampler* sampler) { + AtomicGuard atomic_guard(&sampler_list_access_counter_); + // Add sampler into map if needed. + pthread_t thread_id = sampler->platform_data()->vm_tid(); + HashMap::Entry *entry = + thread_id_to_samplers_.Pointer()->LookupOrInsert(ThreadKey(thread_id), + ThreadHash(thread_id)); + if (entry->value == NULL) { + SamplerList* samplers = new SamplerList(); + samplers->Add(sampler); + entry->value = samplers; + } else { + SamplerList* samplers = reinterpret_cast(entry->value); + if (!samplers->Contains(sampler)) { + samplers->Add(sampler); + } + } + } +#else + SamplerList active_samplers_; +#endif // USE_SIGNALS + + DISALLOW_COPY_AND_ASSIGN(SamplerThread); +}; + + +base::Mutex* SamplerThread::mutex_ = NULL; +SamplerThread* SamplerThread::instance_ = NULL; +#if defined(USE_SIGNALS) +base::LazyInstance::type + SamplerThread::thread_id_to_samplers_ = LAZY_INSTANCE_INITIALIZER; +base::AtomicValue SamplerThread::sampler_list_access_counter_(0); + +// As Native Client does not support signal handling, profiling is disabled. +#if !V8_OS_NACL +void SignalHandler::HandleProfilerSignal(int signal, siginfo_t* info, + void* context) { + USE(info); + if (signal != SIGPROF) return; + AtomicGuard atomic_guard(&SamplerThread::sampler_list_access_counter_, false); + if (!atomic_guard.is_success()) return; + pthread_t thread_id = pthread_self(); + HashMap::Entry* entry = + SamplerThread::thread_id_to_samplers_.Pointer()->Lookup( + ThreadKey(thread_id), ThreadHash(thread_id)); + if (entry == NULL) + return; + SamplerList* samplers = reinterpret_cast(entry->value); + for (int i = 0; i < samplers->length(); ++i) { + Sampler* sampler = samplers->at(i); + CollectSample(context, sampler); + } +} +#endif // !V8_OS_NACL +#endif // USE_SIGNALs + + +void Sampler::SetUp() { +#if defined(USE_SIGNALS) + SignalHandler::SetUp(); +#endif + SamplerThread::SetUp(); +} + + +void Sampler::TearDown() { + SamplerThread::TearDown(); +#if defined(USE_SIGNALS) + SignalHandler::TearDown(); +#endif +} + +Sampler::Sampler(Isolate* isolate, int interval) + : isolate_(isolate), + interval_(interval), + profiling_(false), + has_processing_thread_(false), + active_(false), + registered_(false), + is_counting_samples_(false), + js_sample_count_(0), + external_sample_count_(0) { + data_ = new PlatformData; +} + +Sampler::~Sampler() { + DCHECK(!IsActive()); + if (IsRegistered()) { + SamplerThread::RemoveSampler(this); + } + delete data_; +} + +void Sampler::Start() { + DCHECK(!IsActive()); + SetActive(true); + SamplerThread::AddActiveSampler(this); +} + + +void Sampler::Stop() { + DCHECK(IsActive()); + SamplerThread::RemoveSampler(this); + SetActive(false); + SetRegistered(false); +} + + +void Sampler::IncreaseProfilingDepth() { + base::NoBarrier_AtomicIncrement(&profiling_, 1); +#if defined(USE_SIGNALS) + SignalHandler::IncreaseSamplerCount(); +#endif +} + + +void Sampler::DecreaseProfilingDepth() { +#if defined(USE_SIGNALS) + SignalHandler::DecreaseSamplerCount(); +#endif + base::NoBarrier_AtomicIncrement(&profiling_, -1); +} + + +void Sampler::SampleStack(const v8::RegisterState& state) { + TickSample* sample = isolate_->cpu_profiler()->StartTickSample(); + TickSample sample_obj; + if (sample == NULL) sample = &sample_obj; + sample->Init(isolate_, state, TickSample::kIncludeCEntryFrame, true); + if (is_counting_samples_ && !sample->timestamp.IsNull()) { + if (sample->state == JS) ++js_sample_count_; + if (sample->state == EXTERNAL) ++external_sample_count_; + } + Tick(sample); + if (sample != &sample_obj) { + isolate_->cpu_profiler()->FinishTickSample(); + } +} + + +#if defined(USE_SIGNALS) + +void Sampler::DoSample() { + if (!SignalHandler::Installed()) return; + if (!IsActive() && !IsRegistered()) { + SamplerThread::RegisterSampler(this); + SetRegistered(true); + } + pthread_kill(platform_data()->vm_tid(), SIGPROF); +} + +#elif V8_OS_WIN || V8_OS_CYGWIN + +void Sampler::DoSample() { + HANDLE profiled_thread = platform_data()->profiled_thread(); + if (profiled_thread == NULL) return; + + const DWORD kSuspendFailed = static_cast(-1); + if (SuspendThread(profiled_thread) == kSuspendFailed) return; + + // Context used for sampling the register state of the profiled thread. + CONTEXT context; + memset(&context, 0, sizeof(context)); + context.ContextFlags = CONTEXT_FULL; + if (GetThreadContext(profiled_thread, &context) != 0) { + v8::RegisterState state; +#if defined(USE_SIMULATOR) + if (!SimulatorHelper::FillRegisters(isolate(), &state)) { + ResumeThread(profiled_thread); + return; + } +#else +#if V8_HOST_ARCH_X64 + state.pc = reinterpret_cast
(context.Rip); + state.sp = reinterpret_cast
(context.Rsp); + state.fp = reinterpret_cast
(context.Rbp); +#else + state.pc = reinterpret_cast
(context.Eip); + state.sp = reinterpret_cast
(context.Esp); + state.fp = reinterpret_cast
(context.Ebp); +#endif +#endif // USE_SIMULATOR + SampleStack(state); + } + ResumeThread(profiled_thread); +} + +#endif // USE_SIGNALS + + +} // namespace internal +} // namespace v8 diff --git a/src/libsampler/v8-sampler.h b/src/profiler/sampler.h similarity index 79% rename from src/libsampler/v8-sampler.h rename to src/profiler/sampler.h index 7ae3c8c4766..3d3a6e997ff 100644 --- a/src/libsampler/v8-sampler.h +++ b/src/profiler/sampler.h @@ -1,9 +1,9 @@ -// Copyright 2016 the V8 project authors. All rights reserved. +// Copyright 2013 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef V8_LIBSAMPLER_SAMPLER_H_ -#define V8_LIBSAMPLER_SAMPLER_H_ +#ifndef V8_PROFILER_SAMPLER_H_ +#define V8_PROFILER_SAMPLER_H_ #include "include/v8.h" @@ -11,7 +11,10 @@ #include "src/base/macros.h" namespace v8 { -namespace sampler { +namespace internal { + +class Isolate; +struct TickSample; // ---------------------------------------------------------------------------- // Sampler @@ -22,23 +25,19 @@ namespace sampler { class Sampler { public: - static const int kMaxFramesCountLog2 = 8; - static const unsigned kMaxFramesCount = (1u << kMaxFramesCountLog2) - 1; - // Initializes the Sampler support. Called once at VM startup. static void SetUp(); static void TearDown(); // Initialize sampler. - explicit Sampler(Isolate* isolate); + Sampler(Isolate* isolate, int interval); virtual ~Sampler(); Isolate* isolate() const { return isolate_; } + int interval() const { return interval_; } // Performs stack sampling. - // Clients should override this method in order to do something on samples, - // for example buffer samples in a queue. - virtual void SampleStack(const v8::RegisterState& regs) = 0; + void SampleStack(const v8::RegisterState& regs); // Start and stop sampler. void Start(); @@ -61,7 +60,8 @@ class Sampler { bool IsRegistered() const { return base::NoBarrier_Load(®istered_); } void DoSample(); - + // If true next sample must be initiated on the profiler event processor + // thread right after latest sample is processed. void SetHasProcessingThread(bool value) { base::NoBarrier_Store(&has_processing_thread_, value); } @@ -79,25 +79,30 @@ class Sampler { PlatformData* platform_data() const { return data_; } protected: - // Counts stack samples taken in various VM states. - bool is_counting_samples_; - unsigned js_sample_count_; - unsigned external_sample_count_; + // This method is called for each sampling period with the current + // program counter. + virtual void Tick(TickSample* sample) = 0; private: void SetActive(bool value) { base::NoBarrier_Store(&active_, value); } + void SetRegistered(bool value) { base::NoBarrier_Store(®istered_, value); } Isolate* isolate_; + const int interval_; base::Atomic32 profiling_; base::Atomic32 has_processing_thread_; base::Atomic32 active_; base::Atomic32 registered_; PlatformData* data_; // Platform specific data. + // Counts stack samples taken in various VM states. + bool is_counting_samples_; + unsigned js_sample_count_; + unsigned external_sample_count_; DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler); }; -} // namespace sampler +} // namespace internal } // namespace v8 -#endif // V8_LIBSAMPLER_SAMPLER_H_ +#endif // V8_PROFILER_SAMPLER_H_ diff --git a/src/v8.cc b/src/v8.cc index 6a7aaab0628..154cf6201d6 100644 --- a/src/v8.cc +++ b/src/v8.cc @@ -14,9 +14,9 @@ #include "src/elements.h" #include "src/frames.h" #include "src/isolate.h" -#include "src/libsampler/v8-sampler.h" #include "src/objects.h" #include "src/profiler/heap-profiler.h" +#include "src/profiler/sampler.h" #include "src/runtime-profiler.h" #include "src/snapshot/natives.h" #include "src/snapshot/snapshot.h" @@ -48,7 +48,7 @@ void V8::TearDown() { ExternalReference::TearDownMathExpData(); RegisteredExtension::UnregisterAll(); Isolate::GlobalTearDown(); - sampler::Sampler::TearDown(); + Sampler::TearDown(); FlagList::ResetAllFlags(); // Frees memory held by string arguments. } @@ -76,7 +76,7 @@ void V8::InitializeOncePerProcessImpl() { Isolate::InitializeOncePerProcess(); - sampler::Sampler::SetUp(); + Sampler::SetUp(); CpuFeatures::Probe(false); ElementsAccessor::InitializeOncePerProcess(); LOperand::SetUpCaches(); diff --git a/src/v8.gyp b/src/v8.gyp index f9478c52578..c5e214acdf5 100644 --- a/src/v8.gyp +++ b/src/v8.gyp @@ -381,7 +381,6 @@ 'type': 'static_library', 'dependencies': [ 'v8_libbase', - 'v8_libsampler', ], 'variables': { 'optimize': 'max', @@ -1002,6 +1001,8 @@ 'profiler/profile-generator-inl.h', 'profiler/profile-generator.cc', 'profiler/profile-generator.h', + 'profiler/sampler.cc', + 'profiler/sampler.h', 'profiler/sampling-heap-profiler.cc', 'profiler/sampling-heap-profiler.h', 'profiler/strings-storage.cc', @@ -1938,38 +1939,6 @@ ], }, }, - { - 'target_name': 'v8_libsampler', - 'type': 'static_library', - 'variables': { - 'optimize': 'max', - }, - 'dependencies': [ - 'v8_libbase', - ], - 'include_dirs+': [ - '..', - '../include', - ], - 'sources': [ - 'libsampler/hashmap.h', - 'libsampler/utils.h', - 'libsampler/v8-sampler.cc', - 'libsampler/v8-sampler.h' - ], - 'conditions': [ - ['want_separate_host_toolset==1', { - 'toolsets': ['host', 'target'], - }, { - 'toolsets': ['target'], - }], - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../include', - ], - }, - }, { 'target_name': 'natives_blob', 'type': 'none', diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 2d1480ad91f..c6c44d3637c 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -108,7 +108,6 @@ 'heap/test-lab.cc', 'heap/test-mark-compact.cc', 'heap/test-spaces.cc', - 'libsampler/test-sampler.cc', 'print-extension.cc', 'profiler-extension.cc', 'test-accessors.cc', diff --git a/test/cctest/libsampler/test-sampler.cc b/test/cctest/libsampler/test-sampler.cc deleted file mode 100644 index 745b139b301..00000000000 --- a/test/cctest/libsampler/test-sampler.cc +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2016 the V8 project authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// Tests of sampler functionalities. - -#include "src/libsampler/v8-sampler.h" - -#include "src/base/platform/platform.h" -#include "test/cctest/cctest.h" - - -namespace v8 { -namespace sampler { - -namespace { - -class TestSamplingThread : public base::Thread { - public: - static const int kSamplerThreadStackSize = 64 * 1024; - - explicit TestSamplingThread(Sampler* sampler) - : Thread(base::Thread::Options("TestSamplingThread", - kSamplerThreadStackSize)), - sampler_(sampler) {} - - // Implement Thread::Run(). - void Run() override { - while (sampler_->IsProfiling()) { - sampler_->DoSample(); - base::OS::Sleep(base::TimeDelta::FromMilliseconds(1)); - } - } - - private: - Sampler* sampler_; -}; - - -class TestSampler : public Sampler { - public: - explicit TestSampler(Isolate* isolate) : Sampler(isolate) {} - - void SampleStack(const v8::RegisterState& regs) override { - void* frames[Sampler::kMaxFramesCount]; - SampleInfo sample_info; - isolate()->GetStackSample(regs, reinterpret_cast(frames), - Sampler::kMaxFramesCount, &sample_info); - if (is_counting_samples_) { - if (sample_info.vm_state == JS) ++js_sample_count_; - if (sample_info.vm_state == EXTERNAL) ++external_sample_count_; - } - } -}; - - -class TestApiCallbacks { - public: - TestApiCallbacks() {} - - static void Getter(v8::Local name, - const v8::PropertyCallbackInfo& info) { - } - - static void Setter(v8::Local name, - v8::Local value, - const v8::PropertyCallbackInfo& info) { - } -}; - - -static void RunSampler(v8::Local env, - v8::Local function, - v8::Local argv[], int argc, - unsigned min_js_samples = 0, - unsigned min_external_samples = 0) { - Sampler::SetUp(); - TestSampler* sampler = new TestSampler(env->GetIsolate()); - TestSamplingThread* thread = new TestSamplingThread(sampler); - sampler->IncreaseProfilingDepth(); - sampler->Start(); - sampler->StartCountingSamples(); - thread->StartSynchronously(); - do { - function->Call(env, env->Global(), argc, argv).ToLocalChecked(); - } while (sampler->js_sample_count() < min_js_samples || - sampler->external_sample_count() < min_external_samples); - sampler->Stop(); - sampler->DecreaseProfilingDepth(); - thread->Join(); - delete thread; - delete sampler; - Sampler::TearDown(); -} - -} // namespace - -static const char* sampler_test_source = "function start(count) {\n" -" for (var i = 0; i < count; i++) {\n" -" var o = instance.foo;\n" -" instance.foo = o + 1;\n" -" }\n" -"}\n"; - -static v8::Local GetFunction(v8::Local env, - const char* name) { - return v8::Local::Cast( - env->Global()->Get(env, v8_str(name)).ToLocalChecked()); -} - - -TEST(LibSamplerCollectSample) { - LocalContext env; - v8::Isolate* isolate = env->GetIsolate(); - v8::HandleScope scope(isolate); - - v8::Local func_template = - v8::FunctionTemplate::New(isolate); - v8::Local instance_template = - func_template->InstanceTemplate(); - - TestApiCallbacks accessors; - v8::Local data = - v8::External::New(isolate, &accessors); - instance_template->SetAccessor(v8_str("foo"), &TestApiCallbacks::Getter, - &TestApiCallbacks::Setter, data); - v8::Local func = - func_template->GetFunction(env.local()).ToLocalChecked(); - v8::Local instance = - func->NewInstance(env.local()).ToLocalChecked(); - env->Global()->Set(env.local(), v8_str("instance"), instance).FromJust(); - - CompileRun(sampler_test_source); - v8::Local function = GetFunction(env.local(), "start"); - - int32_t repeat_count = 100; - v8::Local args[] = {v8::Integer::New(isolate, repeat_count)}; - RunSampler(env.local(), function, args, arraysize(args), 100, 100); -} - -} // namespace sampler -} // namespace v8 diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc index 7740a1d9bf2..e4eceeceab7 100644 --- a/test/cctest/test-cpu-profiler.cc +++ b/test/cctest/test-cpu-profiler.cc @@ -422,7 +422,7 @@ static v8::CpuProfile* RunProfiler(v8::Local env, cpu_profiler->SetSamplingInterval(100); cpu_profiler->StartProfiling(profile_name, collect_samples); - v8::sampler::Sampler* sampler = + i::Sampler* sampler = reinterpret_cast(env->GetIsolate())->logger()->sampler(); sampler->StartCountingSamples(); do {