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