| // 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_SAMPLER_H_ |
| #define V8_LIBSAMPLER_SAMPLER_H_ |
| |
| #include <atomic> |
| #include <memory> |
| #include <unordered_map> |
| |
| #include "include/v8.h" |
| #include "src/base/lazy-instance.h" |
| #include "src/base/macros.h" |
| |
| #if V8_OS_POSIX && !V8_OS_CYGWIN && !V8_OS_FUCHSIA |
| #define USE_SIGNALS |
| #endif |
| |
| namespace v8 { |
| namespace sampler { |
| |
| // ---------------------------------------------------------------------------- |
| // Sampler |
| // |
| // A sampler periodically samples the state of the VM and optionally |
| // (if used for profiling) the program counter and stack pointer for |
| // the thread that created it. |
| |
| class V8_EXPORT_PRIVATE Sampler { |
| public: |
| static const int kMaxFramesCountLog2 = 8; |
| static const unsigned kMaxFramesCount = (1u << kMaxFramesCountLog2) - 1; |
| |
| // Initialize sampler. |
| explicit Sampler(Isolate* isolate); |
| virtual ~Sampler(); |
| |
| Isolate* isolate() const { return isolate_; } |
| |
| // 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; |
| |
| // Start and stop sampler. |
| void Start(); |
| void Stop(); |
| |
| // Whether the sampler is running (start has been called). |
| bool IsActive() const { return active_.load(std::memory_order_relaxed); } |
| |
| // Returns true and consumes the pending sample bit if a sample should be |
| // dispatched to this sampler. |
| bool ShouldRecordSample() { |
| return record_sample_.exchange(false, std::memory_order_relaxed); |
| } |
| |
| void DoSample(); |
| |
| // Used in tests to make sure that stack sampling is performed. |
| unsigned js_sample_count() const { return js_sample_count_; } |
| unsigned external_sample_count() const { return external_sample_count_; } |
| void StartCountingSamples() { |
| js_sample_count_ = 0; |
| external_sample_count_ = 0; |
| is_counting_samples_ = true; |
| } |
| |
| class PlatformData; |
| PlatformData* platform_data() const { return data_.get(); } |
| |
| protected: |
| // Counts stack samples taken in various VM states. |
| bool is_counting_samples_ = false; |
| unsigned js_sample_count_ = 0; |
| unsigned external_sample_count_ = 0; |
| |
| void SetActive(bool value) { |
| active_.store(value, std::memory_order_relaxed); |
| } |
| |
| void SetShouldRecordSample() { |
| record_sample_.store(true, std::memory_order_relaxed); |
| } |
| |
| Isolate* isolate_; |
| std::atomic_bool active_{false}; |
| std::atomic_bool record_sample_{false}; |
| std::unique_ptr<PlatformData> data_; // Platform specific data. |
| DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler); |
| }; |
| |
| #ifdef USE_SIGNALS |
| |
| using AtomicMutex = std::atomic_bool; |
| |
| // A helper that uses an std::atomic_bool to create a lock that is obtained on |
| // construction and released on destruction. |
| class V8_EXPORT_PRIVATE AtomicGuard { |
| public: |
| // Attempt to obtain the lock represented by |atomic|. |is_blocking| |
| // determines whether we will block to obtain the lock, or only make one |
| // attempt to gain the lock and then stop. If we fail to gain the lock, |
| // is_success will be false. |
| explicit AtomicGuard(AtomicMutex* atomic, bool is_blocking = true); |
| |
| // Releases the lock represented by atomic, if it is held by this guard. |
| ~AtomicGuard(); |
| |
| // Whether the lock was successfully obtained in the constructor. This will |
| // always be true if is_blocking was true. |
| bool is_success() const; |
| |
| private: |
| AtomicMutex* const atomic_; |
| bool is_success_; |
| }; |
| |
| // SamplerManager keeps a list of Samplers per thread, and allows the caller to |
| // take a sample for every Sampler on the current thread. |
| class V8_EXPORT_PRIVATE SamplerManager { |
| public: |
| using SamplerList = std::vector<Sampler*>; |
| |
| // Add |sampler| to the map if it is not already present. |
| void AddSampler(Sampler* sampler); |
| |
| // If |sampler| exists in the map, remove it and delete the SamplerList if |
| // |sampler| was the last sampler in the list. |
| void RemoveSampler(Sampler* sampler); |
| |
| // Take a sample for every sampler on the current thread. This function can |
| // return without taking samples if AddSampler or RemoveSampler are being |
| // concurrently called on any thread. |
| void DoSample(const v8::RegisterState& state); |
| |
| // Get the lazily instantiated, global SamplerManager instance. |
| static SamplerManager* instance(); |
| |
| private: |
| SamplerManager() = default; |
| // Must be a friend so that it can access the private constructor for the |
| // global lazy instance. |
| friend class base::LeakyObject<SamplerManager>; |
| |
| std::unordered_map<pthread_t, SamplerList> sampler_map_; |
| AtomicMutex samplers_access_counter_{false}; |
| |
| DISALLOW_COPY_AND_ASSIGN(SamplerManager); |
| }; |
| |
| #endif // USE_SIGNALS |
| |
| } // namespace sampler |
| } // namespace v8 |
| |
| #endif // V8_LIBSAMPLER_SAMPLER_H_ |