[cpu-profiler] Refactor SamplingEventsProcessor into base and subclass

This is preparation to allow for a non-sampling events processor which
receives ticks from a source not driven by a timer. This will allow us
to have more deterministic testing of the CPU profiler.

It also allows different implementations for a wall time and CPU time
triggered sampler.

Change-Id: I2e9db9580ec70f05094e59c2c1e5efc28c8f7da8
Reviewed-on: https://chromium-review.googlesource.com/c/1280436
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56717}
This commit is contained in:
Peter Marshall 2018-10-16 15:40:25 +02:00 committed by Commit Bot
parent fb29a554e8
commit 2278383261
4 changed files with 95 additions and 74 deletions

View File

@ -53,8 +53,7 @@ void ReportBuiltinEventRecord::UpdateCodeMap(CodeMap* code_map) {
entry->SetBuiltinId(builtin_id);
}
TickSample* ProfilerEventsProcessor::StartTickSample() {
TickSample* SamplingEventsProcessor::StartTickSample() {
void* address = ticks_buffer_.StartEnqueue();
if (address == nullptr) return nullptr;
TickSampleEventRecord* evt =
@ -62,8 +61,7 @@ TickSample* ProfilerEventsProcessor::StartTickSample() {
return &evt->sample;
}
void ProfilerEventsProcessor::FinishTickSample() {
void SamplingEventsProcessor::FinishTickSample() {
ticks_buffer_.FinishEnqueue();
}

View File

@ -25,7 +25,7 @@ static const int kProfilerStackSize = 64 * KB;
class CpuSampler : public sampler::Sampler {
public:
CpuSampler(Isolate* isolate, ProfilerEventsProcessor* processor)
CpuSampler(Isolate* isolate, SamplingEventsProcessor* processor)
: sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)),
processor_(processor) {}
@ -42,26 +42,31 @@ class CpuSampler : public sampler::Sampler {
}
private:
ProfilerEventsProcessor* processor_;
SamplingEventsProcessor* processor_;
};
ProfilerEventsProcessor::ProfilerEventsProcessor(Isolate* isolate,
ProfileGenerator* generator,
base::TimeDelta period)
ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator)
: Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
generator_(generator),
sampler_(new CpuSampler(isolate, this)),
running_(1),
period_(period),
last_code_event_id_(0),
last_processed_code_event_id_(0) {
last_processed_code_event_id_(0) {}
SamplingEventsProcessor::SamplingEventsProcessor(Isolate* isolate,
ProfileGenerator* generator,
base::TimeDelta period)
: ProfilerEventsProcessor(generator),
sampler_(new CpuSampler(isolate, this)),
period_(period) {
sampler_->IncreaseProfilingDepth();
}
ProfilerEventsProcessor::~ProfilerEventsProcessor() {
SamplingEventsProcessor::~SamplingEventsProcessor() {
sampler_->DecreaseProfilingDepth();
}
ProfilerEventsProcessor::~ProfilerEventsProcessor() = default;
void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
event.generic.order = ++last_code_event_id_;
events_buffer_.Enqueue(event);
@ -96,6 +101,11 @@ void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate,
ticks_from_vm_buffer_.Enqueue(record);
}
void ProfilerEventsProcessor::AddSample(TickSample sample) {
TickSampleEventRecord record(last_code_event_id_);
record.sample = sample;
ticks_from_vm_buffer_.Enqueue(record);
}
void ProfilerEventsProcessor::StopSynchronously() {
if (!base::Relaxed_AtomicExchange(&running_, 0)) return;
@ -124,7 +134,7 @@ bool ProfilerEventsProcessor::ProcessCodeEvent() {
}
ProfilerEventsProcessor::SampleProcessingResult
ProfilerEventsProcessor::ProcessOneSample() {
SamplingEventsProcessor::ProcessOneSample() {
TickSampleEventRecord record1;
if (ticks_from_vm_buffer_.Peek(&record1) &&
(record1.order == last_processed_code_event_id_)) {
@ -147,8 +157,7 @@ ProfilerEventsProcessor::SampleProcessingResult
return OneSampleProcessed;
}
void ProfilerEventsProcessor::Run() {
void SamplingEventsProcessor::Run() {
while (!!base::Relaxed_Load(&running_)) {
base::TimeTicks nextSampleTime =
base::TimeTicks::HighResolutionNow() + period_;
@ -180,8 +189,8 @@ void ProfilerEventsProcessor::Run() {
}
}
// Schedule next sample. sampler_ is nullptr in tests.
if (sampler_) sampler_->DoSample();
// Schedule next sample.
sampler_->DoSample();
}
// Process remaining tick events.
@ -193,16 +202,11 @@ void ProfilerEventsProcessor::Run() {
} while (ProcessCodeEvent());
}
void* ProfilerEventsProcessor::operator new(size_t size) {
return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor));
}
void ProfilerEventsProcessor::operator delete(void* ptr) {
AlignedFree(ptr);
void* SamplingEventsProcessor::operator new(size_t size) {
return AlignedAlloc(size, V8_ALIGNOF(SamplingEventsProcessor));
}
void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); }
int CpuProfiler::GetProfilesCount() {
// The count of profiles doesn't depend on a security token.
@ -375,7 +379,7 @@ void CpuProfiler::StartProcessorIfNotStarted() {
codemap_needs_initialization = true;
CreateEntriesForRuntimeCallStats();
}
processor_.reset(new ProfilerEventsProcessor(isolate_, generator_.get(),
processor_.reset(new SamplingEventsProcessor(isolate_, generator_.get(),
sampling_interval_));
if (!profiler_listener_) {
profiler_listener_.reset(new ProfilerListener(isolate_, this));

View File

@ -133,35 +133,23 @@ class CodeEventsContainer {
// methods called by event producers: VM and stack sampler threads.
class ProfilerEventsProcessor : public base::Thread {
public:
ProfilerEventsProcessor(Isolate* isolate, ProfileGenerator* generator,
base::TimeDelta period);
~ProfilerEventsProcessor() override;
virtual ~ProfilerEventsProcessor();
// Thread control.
void Run() override;
virtual void Run() = 0;
void StopSynchronously();
V8_INLINE bool running() { return !!base::Relaxed_Load(&running_); }
void Enqueue(const CodeEventsContainer& event);
// Puts current stack into tick sample events buffer.
// Puts current stack into the tick sample events buffer.
void AddCurrentStack(Isolate* isolate, bool update_stats = false);
void AddDeoptStack(Isolate* isolate, Address from, int fp_to_sp_delta);
// Puts the given sample into the tick sample events buffer.
void AddSample(TickSample sample);
// Tick sample events are filled directly in the buffer of the circular
// queue (because the structure is of fixed width, but usually not all
// stack frame entries are filled.) This method returns a pointer to the
// next record of the buffer.
inline TickSample* StartTickSample();
inline void FinishTickSample();
protected:
explicit ProfilerEventsProcessor(ProfileGenerator* generator);
// SamplingCircularQueue has stricter alignment requirements than a normal new
// can fulfil, so we need to provide our own new/delete here.
void* operator new(size_t size);
void operator delete(void* ptr);
sampler::Sampler* sampler() { return sampler_.get(); }
private:
// Called from events processing thread (Run() method.)
bool ProcessCodeEvent();
@ -170,21 +158,48 @@ class ProfilerEventsProcessor : public base::Thread {
FoundSampleForNextCodeEvent,
NoSamplesInQueue
};
SampleProcessingResult ProcessOneSample();
virtual SampleProcessingResult ProcessOneSample() = 0;
ProfileGenerator* generator_;
std::unique_ptr<sampler::Sampler> sampler_;
base::Atomic32 running_;
const base::TimeDelta period_; // Samples & code events processing period.
LockedQueue<CodeEventsContainer> events_buffer_;
LockedQueue<TickSampleEventRecord> ticks_from_vm_buffer_;
std::atomic<unsigned> last_code_event_id_;
unsigned last_processed_code_event_id_;
};
class SamplingEventsProcessor : public ProfilerEventsProcessor {
public:
SamplingEventsProcessor(Isolate* isolate, ProfileGenerator* generator,
base::TimeDelta period);
~SamplingEventsProcessor() override;
// SamplingCircularQueue has stricter alignment requirements than a normal new
// can fulfil, so we need to provide our own new/delete here.
void* operator new(size_t size);
void operator delete(void* ptr);
void Run() override;
// Tick sample events are filled directly in the buffer of the circular
// queue (because the structure is of fixed width, but usually not all
// stack frame entries are filled.) This method returns a pointer to the
// next record of the buffer.
inline TickSample* StartTickSample();
inline void FinishTickSample();
sampler::Sampler* sampler() { return sampler_.get(); }
private:
SampleProcessingResult ProcessOneSample() override;
static const size_t kTickSampleBufferSize = 1 * MB;
static const size_t kTickSampleQueueLength =
kTickSampleBufferSize / sizeof(TickSampleEventRecord);
SamplingCircularQueue<TickSampleEventRecord,
kTickSampleQueueLength> ticks_buffer_;
LockedQueue<TickSampleEventRecord> ticks_from_vm_buffer_;
std::atomic<unsigned> last_code_event_id_;
unsigned last_processed_code_event_id_;
std::unique_ptr<sampler::Sampler> sampler_;
const base::TimeDelta period_; // Samples & code events processing period.
};
class CpuProfiler : public CodeEventObserver {

View File

@ -78,7 +78,7 @@ TEST(StartStop) {
CpuProfilesCollection profiles(isolate);
ProfileGenerator generator(&profiles);
std::unique_ptr<ProfilerEventsProcessor> processor(
new ProfilerEventsProcessor(isolate, &generator,
new SamplingEventsProcessor(isolate, &generator,
v8::base::TimeDelta::FromMicroseconds(100)));
processor->Start();
processor->StopSynchronously();
@ -88,19 +88,20 @@ static void EnqueueTickSampleEvent(ProfilerEventsProcessor* proc,
i::Address frame1,
i::Address frame2 = kNullAddress,
i::Address frame3 = kNullAddress) {
v8::TickSample* sample = proc->StartTickSample();
sample->pc = reinterpret_cast<void*>(frame1);
sample->tos = reinterpret_cast<void*>(frame1);
sample->frames_count = 0;
v8::internal::TickSample sample;
sample.pc = reinterpret_cast<void*>(frame1);
sample.tos = reinterpret_cast<void*>(frame1);
sample.frames_count = 0;
if (frame2 != kNullAddress) {
sample->stack[0] = reinterpret_cast<void*>(frame2);
sample->frames_count = 1;
sample.stack[0] = reinterpret_cast<void*>(frame2);
sample.frames_count = 1;
}
if (frame3 != kNullAddress) {
sample->stack[1] = reinterpret_cast<void*>(frame3);
sample->frames_count = 2;
sample.stack[1] = reinterpret_cast<void*>(frame3);
sample.frames_count = 2;
}
proc->FinishTickSample();
sample.timestamp = base::TimeTicks::HighResolutionNow();
proc->AddSample(sample);
}
namespace {
@ -159,7 +160,7 @@ TEST(CodeEvents) {
CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
ProfileGenerator* generator = new ProfileGenerator(profiles);
ProfilerEventsProcessor* processor = new ProfilerEventsProcessor(
ProfilerEventsProcessor* processor = new SamplingEventsProcessor(
isolate, generator, v8::base::TimeDelta::FromMicroseconds(100));
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);
@ -221,7 +222,7 @@ TEST(TickEvents) {
CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
ProfileGenerator* generator = new ProfileGenerator(profiles);
ProfilerEventsProcessor* processor =
new ProfilerEventsProcessor(CcTest::i_isolate(), generator,
new SamplingEventsProcessor(CcTest::i_isolate(), generator,
v8::base::TimeDelta::FromMicroseconds(100));
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);
@ -290,7 +291,7 @@ TEST(Issue1398) {
CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
ProfileGenerator* generator = new ProfileGenerator(profiles);
ProfilerEventsProcessor* processor =
new ProfilerEventsProcessor(CcTest::i_isolate(), generator,
new SamplingEventsProcessor(CcTest::i_isolate(), generator,
v8::base::TimeDelta::FromMicroseconds(100));
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);
@ -299,14 +300,15 @@ TEST(Issue1398) {
profiler_listener.CodeCreateEvent(i::Logger::BUILTIN_TAG, code, "bbb");
v8::TickSample* sample = processor->StartTickSample();
sample->pc = reinterpret_cast<void*>(code->InstructionStart());
sample->tos = nullptr;
sample->frames_count = v8::TickSample::kMaxFramesCount;
for (unsigned i = 0; i < sample->frames_count; ++i) {
sample->stack[i] = reinterpret_cast<void*>(code->InstructionStart());
v8::internal::TickSample sample;
sample.pc = reinterpret_cast<void*>(code->InstructionStart());
sample.tos = nullptr;
sample.frames_count = v8::TickSample::kMaxFramesCount;
for (unsigned i = 0; i < sample.frames_count; ++i) {
sample.stack[i] = reinterpret_cast<void*>(code->InstructionStart());
}
processor->FinishTickSample();
sample.timestamp = base::TimeTicks::HighResolutionNow();
processor->AddSample(sample);
processor->StopSynchronously();
CpuProfile* profile = profiles->StopProfiling("");
@ -458,7 +460,9 @@ v8::CpuProfile* ProfilerHelper::Run(v8::Local<v8::Function> function,
v8::internal::CpuProfiler* iprofiler =
reinterpret_cast<v8::internal::CpuProfiler*>(profiler_);
v8::sampler::Sampler* sampler = iprofiler->processor()->sampler();
v8::sampler::Sampler* sampler =
reinterpret_cast<i::SamplingEventsProcessor*>(iprofiler->processor())
->sampler();
sampler->StartCountingSamples();
do {
function->Call(context_, context_->Global(), argc, argv).ToLocalChecked();
@ -1143,7 +1147,7 @@ static void TickLines(bool optimize) {
CpuProfilesCollection* profiles = new CpuProfilesCollection(isolate);
ProfileGenerator* generator = new ProfileGenerator(profiles);
ProfilerEventsProcessor* processor =
new ProfilerEventsProcessor(CcTest::i_isolate(), generator,
new SamplingEventsProcessor(CcTest::i_isolate(), generator,
v8::base::TimeDelta::FromMicroseconds(100));
CpuProfiler profiler(isolate, profiles, generator, processor);
profiles->StartProfiling("", false);