blob: e81ba9c90e15f7e4e08be0ef7ab4278e5c883673 [file] [log] [blame]
// 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_