1// Copyright 2016 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef V8_LIBSAMPLER_SAMPLER_H_ 6#define V8_LIBSAMPLER_SAMPLER_H_ 7 8#include <atomic> 9#include <memory> 10#include <unordered_map> 11#include <vector> 12 13#include "src/base/lazy-instance.h" 14#include "src/base/macros.h" 15 16#if V8_OS_POSIX && !V8_OS_CYGWIN && !V8_OS_FUCHSIA 17#define USE_SIGNALS 18#endif 19 20namespace v8 { 21 22class Isolate; 23struct RegisterState; 24 25namespace sampler { 26 27// ---------------------------------------------------------------------------- 28// Sampler 29// 30// A sampler periodically samples the state of the VM and optionally 31// (if used for profiling) the program counter and stack pointer for 32// the thread that created it. 33 34class V8_EXPORT_PRIVATE Sampler { 35 public: 36 static const int kMaxFramesCountLog2 = 8; 37 static const unsigned kMaxFramesCount = (1u << kMaxFramesCountLog2) - 1; 38 39 // Initialize sampler. 40 explicit Sampler(Isolate* isolate); 41 virtual ~Sampler(); 42 43 Isolate* isolate() const { return isolate_; } 44 45 // Performs stack sampling. 46 // Clients should override this method in order to do something on samples, 47 // for example buffer samples in a queue. 48 virtual void SampleStack(const v8::RegisterState& regs) = 0; 49 50 // Start and stop sampler. 51 void Start(); 52 void Stop(); 53 54 // Whether the sampler is running (start has been called). 55 bool IsActive() const { return active_.load(std::memory_order_relaxed); } 56 57 // Returns true and consumes the pending sample bit if a sample should be 58 // dispatched to this sampler. 59 bool ShouldRecordSample() { 60 return record_sample_.exchange(false, std::memory_order_relaxed); 61 } 62 63 void DoSample(); 64 65 // Used in tests to make sure that stack sampling is performed. 66 unsigned js_sample_count() const { return js_sample_count_; } 67 unsigned external_sample_count() const { return external_sample_count_; } 68 void StartCountingSamples() { 69 js_sample_count_ = 0; 70 external_sample_count_ = 0; 71 is_counting_samples_ = true; 72 } 73 74 class PlatformData; 75 PlatformData* platform_data() const { return data_.get(); } 76 77 protected: 78 // Counts stack samples taken in various VM states. 79 bool is_counting_samples_ = false; 80 unsigned js_sample_count_ = 0; 81 unsigned external_sample_count_ = 0; 82 83 void SetActive(bool value) { 84 active_.store(value, std::memory_order_relaxed); 85 } 86 87 void SetShouldRecordSample() { 88 record_sample_.store(true, std::memory_order_relaxed); 89 } 90 91 Isolate* isolate_; 92 std::atomic_bool active_{false}; 93 std::atomic_bool record_sample_{false}; 94 std::unique_ptr<PlatformData> data_; // Platform specific data. 95 DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler); 96}; 97 98#ifdef USE_SIGNALS 99 100using AtomicMutex = std::atomic_bool; 101 102// A helper that uses an std::atomic_bool to create a lock that is obtained on 103// construction and released on destruction. 104class V8_EXPORT_PRIVATE V8_NODISCARD AtomicGuard { 105 public: 106 // Attempt to obtain the lock represented by |atomic|. |is_blocking| 107 // determines whether we will block to obtain the lock, or only make one 108 // attempt to gain the lock and then stop. If we fail to gain the lock, 109 // is_success will be false. 110 explicit AtomicGuard(AtomicMutex* atomic, bool is_blocking = true); 111 112 // Releases the lock represented by atomic, if it is held by this guard. 113 ~AtomicGuard(); 114 115 // Whether the lock was successfully obtained in the constructor. This will 116 // always be true if is_blocking was true. 117 bool is_success() const; 118 119 private: 120 AtomicMutex* const atomic_; 121 bool is_success_; 122}; 123 124// SamplerManager keeps a list of Samplers per thread, and allows the caller to 125// take a sample for every Sampler on the current thread. 126class V8_EXPORT_PRIVATE SamplerManager { 127 public: 128 using SamplerList = std::vector<Sampler*>; 129 130 SamplerManager(const SamplerManager&) = delete; 131 SamplerManager& operator=(const SamplerManager&) = delete; 132 133 // Add |sampler| to the map if it is not already present. 134 void AddSampler(Sampler* sampler); 135 136 // If |sampler| exists in the map, remove it and delete the SamplerList if 137 // |sampler| was the last sampler in the list. 138 void RemoveSampler(Sampler* sampler); 139 140 // Take a sample for every sampler on the current thread. This function can 141 // return without taking samples if AddSampler or RemoveSampler are being 142 // concurrently called on any thread. 143 void DoSample(const v8::RegisterState& state); 144 145 // Get the lazily instantiated, global SamplerManager instance. 146 static SamplerManager* instance(); 147 148 private: 149 SamplerManager() = default; 150 // Must be a friend so that it can access the private constructor for the 151 // global lazy instance. 152 friend class base::LeakyObject<SamplerManager>; 153 154 std::unordered_map<pthread_t, SamplerList> sampler_map_; 155 AtomicMutex samplers_access_counter_{false}; 156}; 157 158#endif // USE_SIGNALS 159 160} // namespace sampler 161} // namespace v8 162 163#endif // V8_LIBSAMPLER_SAMPLER_H_ 164