1 // Copyright 2012 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 #include "src/profiler/cpu-profiler.h"
6
7 #include "src/debug/debug.h"
8 #include "src/deoptimizer.h"
9 #include "src/frames-inl.h"
10 #include "src/locked-queue-inl.h"
11 #include "src/log-inl.h"
12 #include "src/profiler/cpu-profiler-inl.h"
13 #include "src/vm-state-inl.h"
14
15 namespace v8 {
16 namespace internal {
17
18 static const int kProfilerStackSize = 64 * KB;
19
20 class CpuSampler : public sampler::Sampler {
21 public:
CpuSampler(Isolate * isolate,ProfilerEventsProcessor * processor)22 CpuSampler(Isolate* isolate, ProfilerEventsProcessor* processor)
23 : sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)),
24 processor_(processor) {}
25
SampleStack(const v8::RegisterState & regs)26 void SampleStack(const v8::RegisterState& regs) override {
27 TickSample* sample = processor_->StartTickSample();
28 if (sample == nullptr) return;
29 Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate());
30 sample->Init(isolate, regs, TickSample::kIncludeCEntryFrame, true);
31 if (is_counting_samples_ && !sample->timestamp.IsNull()) {
32 if (sample->state == JS) ++js_sample_count_;
33 if (sample->state == EXTERNAL) ++external_sample_count_;
34 }
35 processor_->FinishTickSample();
36 }
37
38 private:
39 ProfilerEventsProcessor* processor_;
40 };
41
ProfilerEventsProcessor(Isolate * isolate,ProfileGenerator * generator,base::TimeDelta period)42 ProfilerEventsProcessor::ProfilerEventsProcessor(Isolate* isolate,
43 ProfileGenerator* generator,
44 base::TimeDelta period)
45 : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
46 generator_(generator),
47 sampler_(new CpuSampler(isolate, this)),
48 running_(1),
49 period_(period),
50 last_code_event_id_(0),
51 last_processed_code_event_id_(0) {
52 sampler_->IncreaseProfilingDepth();
53 }
54
~ProfilerEventsProcessor()55 ProfilerEventsProcessor::~ProfilerEventsProcessor() {
56 sampler_->DecreaseProfilingDepth();
57 }
58
Enqueue(const CodeEventsContainer & event)59 void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
60 event.generic.order = last_code_event_id_.Increment(1);
61 events_buffer_.Enqueue(event);
62 }
63
64
AddDeoptStack(Isolate * isolate,Address from,int fp_to_sp_delta)65 void ProfilerEventsProcessor::AddDeoptStack(Isolate* isolate, Address from,
66 int fp_to_sp_delta) {
67 TickSampleEventRecord record(last_code_event_id_.Value());
68 RegisterState regs;
69 Address fp = isolate->c_entry_fp(isolate->thread_local_top());
70 regs.sp = fp - fp_to_sp_delta;
71 regs.fp = fp;
72 regs.pc = from;
73 record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, false, false);
74 ticks_from_vm_buffer_.Enqueue(record);
75 }
76
AddCurrentStack(Isolate * isolate,bool update_stats)77 void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate,
78 bool update_stats) {
79 TickSampleEventRecord record(last_code_event_id_.Value());
80 RegisterState regs;
81 StackFrameIterator it(isolate);
82 if (!it.done()) {
83 StackFrame* frame = it.frame();
84 regs.sp = frame->sp();
85 regs.fp = frame->fp();
86 regs.pc = frame->pc();
87 }
88 record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, update_stats,
89 false);
90 ticks_from_vm_buffer_.Enqueue(record);
91 }
92
93
StopSynchronously()94 void ProfilerEventsProcessor::StopSynchronously() {
95 if (!base::NoBarrier_AtomicExchange(&running_, 0)) return;
96 Join();
97 }
98
99
ProcessCodeEvent()100 bool ProfilerEventsProcessor::ProcessCodeEvent() {
101 CodeEventsContainer record;
102 if (events_buffer_.Dequeue(&record)) {
103 switch (record.generic.type) {
104 #define PROFILER_TYPE_CASE(type, clss) \
105 case CodeEventRecord::type: \
106 record.clss##_.UpdateCodeMap(generator_->code_map()); \
107 break;
108
109 CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
110
111 #undef PROFILER_TYPE_CASE
112 default: return true; // Skip record.
113 }
114 last_processed_code_event_id_ = record.generic.order;
115 return true;
116 }
117 return false;
118 }
119
120 ProfilerEventsProcessor::SampleProcessingResult
ProcessOneSample()121 ProfilerEventsProcessor::ProcessOneSample() {
122 TickSampleEventRecord record1;
123 if (ticks_from_vm_buffer_.Peek(&record1) &&
124 (record1.order == last_processed_code_event_id_)) {
125 TickSampleEventRecord record;
126 ticks_from_vm_buffer_.Dequeue(&record);
127 generator_->RecordTickSample(record.sample);
128 return OneSampleProcessed;
129 }
130
131 const TickSampleEventRecord* record = ticks_buffer_.Peek();
132 if (record == NULL) {
133 if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
134 return FoundSampleForNextCodeEvent;
135 }
136 if (record->order != last_processed_code_event_id_) {
137 return FoundSampleForNextCodeEvent;
138 }
139 generator_->RecordTickSample(record->sample);
140 ticks_buffer_.Remove();
141 return OneSampleProcessed;
142 }
143
144
Run()145 void ProfilerEventsProcessor::Run() {
146 while (!!base::NoBarrier_Load(&running_)) {
147 base::TimeTicks nextSampleTime =
148 base::TimeTicks::HighResolutionNow() + period_;
149 base::TimeTicks now;
150 SampleProcessingResult result;
151 // Keep processing existing events until we need to do next sample
152 // or the ticks buffer is empty.
153 do {
154 result = ProcessOneSample();
155 if (result == FoundSampleForNextCodeEvent) {
156 // All ticks of the current last_processed_code_event_id_ are
157 // processed, proceed to the next code event.
158 ProcessCodeEvent();
159 }
160 now = base::TimeTicks::HighResolutionNow();
161 } while (result != NoSamplesInQueue && now < nextSampleTime);
162
163 if (nextSampleTime > now) {
164 #if V8_OS_WIN
165 // Do not use Sleep on Windows as it is very imprecise.
166 // Could be up to 16ms jitter, which is unacceptable for the purpose.
167 while (base::TimeTicks::HighResolutionNow() < nextSampleTime) {
168 }
169 #else
170 base::OS::Sleep(nextSampleTime - now);
171 #endif
172 }
173
174 // Schedule next sample. sampler_ is NULL in tests.
175 if (sampler_) sampler_->DoSample();
176 }
177
178 // Process remaining tick events.
179 do {
180 SampleProcessingResult result;
181 do {
182 result = ProcessOneSample();
183 } while (result == OneSampleProcessed);
184 } while (ProcessCodeEvent());
185 }
186
187
operator new(size_t size)188 void* ProfilerEventsProcessor::operator new(size_t size) {
189 return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor));
190 }
191
192
operator delete(void * ptr)193 void ProfilerEventsProcessor::operator delete(void* ptr) {
194 AlignedFree(ptr);
195 }
196
197
GetProfilesCount()198 int CpuProfiler::GetProfilesCount() {
199 // The count of profiles doesn't depend on a security token.
200 return profiles_->profiles()->length();
201 }
202
203
GetProfile(int index)204 CpuProfile* CpuProfiler::GetProfile(int index) {
205 return profiles_->profiles()->at(index);
206 }
207
208
DeleteAllProfiles()209 void CpuProfiler::DeleteAllProfiles() {
210 if (is_profiling_) StopProcessor();
211 ResetProfiles();
212 }
213
214
DeleteProfile(CpuProfile * profile)215 void CpuProfiler::DeleteProfile(CpuProfile* profile) {
216 profiles_->RemoveProfile(profile);
217 delete profile;
218 if (profiles_->profiles()->is_empty() && !is_profiling_) {
219 // If this was the last profile, clean up all accessory data as well.
220 ResetProfiles();
221 }
222 }
223
CodeEventHandler(const CodeEventsContainer & evt_rec)224 void CpuProfiler::CodeEventHandler(const CodeEventsContainer& evt_rec) {
225 switch (evt_rec.generic.type) {
226 case CodeEventRecord::CODE_CREATION:
227 case CodeEventRecord::CODE_MOVE:
228 case CodeEventRecord::CODE_DISABLE_OPT:
229 processor_->Enqueue(evt_rec);
230 break;
231 case CodeEventRecord::CODE_DEOPT: {
232 const CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
233 Address pc = reinterpret_cast<Address>(rec->pc);
234 int fp_to_sp_delta = rec->fp_to_sp_delta;
235 processor_->Enqueue(evt_rec);
236 processor_->AddDeoptStack(isolate_, pc, fp_to_sp_delta);
237 break;
238 }
239 default:
240 UNREACHABLE();
241 }
242 }
243
CpuProfiler(Isolate * isolate)244 CpuProfiler::CpuProfiler(Isolate* isolate)
245 : isolate_(isolate),
246 sampling_interval_(base::TimeDelta::FromMicroseconds(
247 FLAG_cpu_profiler_sampling_interval)),
248 profiles_(new CpuProfilesCollection(isolate)),
249 is_profiling_(false) {
250 profiles_->set_cpu_profiler(this);
251 }
252
CpuProfiler(Isolate * isolate,CpuProfilesCollection * test_profiles,ProfileGenerator * test_generator,ProfilerEventsProcessor * test_processor)253 CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilesCollection* test_profiles,
254 ProfileGenerator* test_generator,
255 ProfilerEventsProcessor* test_processor)
256 : isolate_(isolate),
257 sampling_interval_(base::TimeDelta::FromMicroseconds(
258 FLAG_cpu_profiler_sampling_interval)),
259 profiles_(test_profiles),
260 generator_(test_generator),
261 processor_(test_processor),
262 is_profiling_(false) {
263 profiles_->set_cpu_profiler(this);
264 }
265
~CpuProfiler()266 CpuProfiler::~CpuProfiler() {
267 DCHECK(!is_profiling_);
268 }
269
set_sampling_interval(base::TimeDelta value)270 void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
271 DCHECK(!is_profiling_);
272 sampling_interval_ = value;
273 }
274
ResetProfiles()275 void CpuProfiler::ResetProfiles() {
276 profiles_.reset(new CpuProfilesCollection(isolate_));
277 profiles_->set_cpu_profiler(this);
278 }
279
CollectSample()280 void CpuProfiler::CollectSample() {
281 if (processor_) {
282 processor_->AddCurrentStack(isolate_);
283 }
284 }
285
StartProfiling(const char * title,bool record_samples)286 void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
287 if (profiles_->StartProfiling(title, record_samples)) {
288 StartProcessorIfNotStarted();
289 }
290 }
291
292
StartProfiling(String * title,bool record_samples)293 void CpuProfiler::StartProfiling(String* title, bool record_samples) {
294 StartProfiling(profiles_->GetName(title), record_samples);
295 isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler);
296 }
297
298
StartProcessorIfNotStarted()299 void CpuProfiler::StartProcessorIfNotStarted() {
300 if (processor_) {
301 processor_->AddCurrentStack(isolate_);
302 return;
303 }
304 Logger* logger = isolate_->logger();
305 // Disable logging when using the new implementation.
306 saved_is_logging_ = logger->is_logging_;
307 logger->is_logging_ = false;
308 generator_.reset(new ProfileGenerator(isolate_, profiles_.get()));
309 processor_.reset(new ProfilerEventsProcessor(isolate_, generator_.get(),
310 sampling_interval_));
311 logger->SetUpProfilerListener();
312 ProfilerListener* profiler_listener = logger->profiler_listener();
313 profiler_listener->AddObserver(this);
314 is_profiling_ = true;
315 isolate_->set_is_profiling(true);
316 // Enumerate stuff we already have in the heap.
317 DCHECK(isolate_->heap()->HasBeenSetUp());
318 if (!FLAG_prof_browser_mode) {
319 logger->LogCodeObjects();
320 }
321 logger->LogCompiledFunctions();
322 logger->LogAccessorCallbacks();
323 LogBuiltins();
324 // Enable stack sampling.
325 processor_->AddCurrentStack(isolate_);
326 processor_->StartSynchronously();
327 }
328
StopProfiling(const char * title)329 CpuProfile* CpuProfiler::StopProfiling(const char* title) {
330 if (!is_profiling_) return nullptr;
331 StopProcessorIfLastProfile(title);
332 return profiles_->StopProfiling(title);
333 }
334
StopProfiling(String * title)335 CpuProfile* CpuProfiler::StopProfiling(String* title) {
336 return StopProfiling(profiles_->GetName(title));
337 }
338
StopProcessorIfLastProfile(const char * title)339 void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
340 if (!profiles_->IsLastProfile(title)) return;
341 StopProcessor();
342 }
343
StopProcessor()344 void CpuProfiler::StopProcessor() {
345 Logger* logger = isolate_->logger();
346 is_profiling_ = false;
347 isolate_->set_is_profiling(false);
348 ProfilerListener* profiler_listener = logger->profiler_listener();
349 profiler_listener->RemoveObserver(this);
350 processor_->StopSynchronously();
351 logger->TearDownProfilerListener();
352 processor_.reset();
353 generator_.reset();
354 logger->is_logging_ = saved_is_logging_;
355 }
356
357
LogBuiltins()358 void CpuProfiler::LogBuiltins() {
359 Builtins* builtins = isolate_->builtins();
360 DCHECK(builtins->is_initialized());
361 for (int i = 0; i < Builtins::builtin_count; i++) {
362 CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
363 ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
364 Builtins::Name id = static_cast<Builtins::Name>(i);
365 rec->start = builtins->builtin(id)->address();
366 rec->builtin_id = id;
367 processor_->Enqueue(evt_rec);
368 }
369 }
370
371 } // namespace internal
372 } // namespace v8
373