sampler.h 4.75 KB
Newer Older
1
// Copyright 2016 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5 6
#ifndef V8_LIBSAMPLER_SAMPLER_H_
#define V8_LIBSAMPLER_SAMPLER_H_
7

8
#include <atomic>
9
#include <memory>
10
#include <unordered_map>
11

12 13
#include "include/v8.h"
#include "src/base/lazy-instance.h"
lpy's avatar
lpy committed
14
#include "src/base/macros.h"
15

16 17 18 19
#if V8_OS_POSIX && !V8_OS_CYGWIN && !V8_OS_FUCHSIA
#define USE_SIGNALS
#endif

20
namespace v8 {
21
namespace sampler {
22 23 24 25 26 27 28 29

// ----------------------------------------------------------------------------
// 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.

30
class V8_EXPORT_PRIVATE Sampler {
31
 public:
32 33 34
  static const int kMaxFramesCountLog2 = 8;
  static const unsigned kMaxFramesCount = (1u << kMaxFramesCountLog2) - 1;

35
  // Initialize sampler.
36
  explicit Sampler(Isolate* isolate);
37 38 39 40 41
  virtual ~Sampler();

  Isolate* isolate() const { return isolate_; }

  // Performs stack sampling.
42 43 44
  // 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;
45 46 47 48 49

  // Start and stop sampler.
  void Start();
  void Stop();

50
  // Whether the sampler is running (start has been called).
51
  bool IsActive() const { return active_.load(std::memory_order_relaxed); }
52

53 54 55 56 57 58
  // 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);
  }

59
  void DoSample();
60

61
  // Used in tests to make sure that stack sampling is performed.
62 63
  unsigned js_sample_count() const { return js_sample_count_; }
  unsigned external_sample_count() const { return external_sample_count_; }
64
  void StartCountingSamples() {
65 66 67
    js_sample_count_ = 0;
    external_sample_count_ = 0;
    is_counting_samples_ = true;
68
  }
69 70

  class PlatformData;
71
  PlatformData* platform_data() const { return data_.get(); }
72

73
 protected:
74
  // Counts stack samples taken in various VM states.
75
  bool is_counting_samples_ = false;
76 77
  unsigned js_sample_count_ = 0;
  unsigned external_sample_count_ = 0;
78

79 80 81
  void SetActive(bool value) {
    active_.store(value, std::memory_order_relaxed);
  }
82

83 84 85 86
  void SetShouldRecordSample() {
    record_sample_.store(true, std::memory_order_relaxed);
  }

87
  Isolate* isolate_;
88
  std::atomic_bool active_{false};
89
  std::atomic_bool record_sample_{false};
90
  std::unique_ptr<PlatformData> data_;  // Platform specific data.
91 92 93
  DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler);
};

94 95
#ifdef USE_SIGNALS

96
using AtomicMutex = std::atomic_bool;
97 98 99

// A helper that uses an std::atomic_bool to create a lock that is obtained on
// construction and released on destruction.
100
class V8_EXPORT_PRIVATE AtomicGuard {
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
 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.
122
class V8_EXPORT_PRIVATE SamplerManager {
123
 public:
124
  using SamplerList = std::vector<Sampler*>;
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

  // 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

155
}  // namespace sampler
156
}  // namespace v8
157

158
#endif  // V8_LIBSAMPLER_SAMPLER_H_