[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:
parent
fb29a554e8
commit
2278383261
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user