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