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 
CreateEntriesForRuntimeCallStats()280 void CpuProfiler::CreateEntriesForRuntimeCallStats() {
281   static_entries_.clear();
282   RuntimeCallStats* rcs = isolate_->counters()->runtime_call_stats();
283   CodeMap* code_map = generator_->code_map();
284   for (int i = 0; i < RuntimeCallStats::counters_count; ++i) {
285     RuntimeCallCounter* counter = &(rcs->*(RuntimeCallStats::counters[i]));
286     DCHECK(counter->name());
287     std::unique_ptr<CodeEntry> entry(
288         new CodeEntry(CodeEventListener::FUNCTION_TAG, counter->name(),
289                       CodeEntry::kEmptyNamePrefix, "native V8Runtime"));
290     code_map->AddCode(reinterpret_cast<Address>(counter), entry.get(), 1);
291     static_entries_.push_back(std::move(entry));
292   }
293 }
294 
CollectSample()295 void CpuProfiler::CollectSample() {
296   if (processor_) {
297     processor_->AddCurrentStack(isolate_);
298   }
299 }
300 
StartProfiling(const char * title,bool record_samples)301 void CpuProfiler::StartProfiling(const char* title, bool record_samples) {
302   if (profiles_->StartProfiling(title, record_samples)) {
303     StartProcessorIfNotStarted();
304   }
305 }
306 
307 
StartProfiling(String * title,bool record_samples)308 void CpuProfiler::StartProfiling(String* title, bool record_samples) {
309   StartProfiling(profiles_->GetName(title), record_samples);
310   isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler);
311 }
312 
313 
StartProcessorIfNotStarted()314 void CpuProfiler::StartProcessorIfNotStarted() {
315   if (processor_) {
316     processor_->AddCurrentStack(isolate_);
317     return;
318   }
319   Logger* logger = isolate_->logger();
320   // Disable logging when using the new implementation.
321   saved_is_logging_ = logger->is_logging_;
322   logger->is_logging_ = false;
323   generator_.reset(new ProfileGenerator(profiles_.get()));
324   processor_.reset(new ProfilerEventsProcessor(isolate_, generator_.get(),
325                                                sampling_interval_));
326   CreateEntriesForRuntimeCallStats();
327   logger->SetUpProfilerListener();
328   ProfilerListener* profiler_listener = logger->profiler_listener();
329   profiler_listener->AddObserver(this);
330   is_profiling_ = true;
331   isolate_->set_is_profiling(true);
332   // Enumerate stuff we already have in the heap.
333   DCHECK(isolate_->heap()->HasBeenSetUp());
334   if (!FLAG_prof_browser_mode) {
335     logger->LogCodeObjects();
336   }
337   logger->LogCompiledFunctions();
338   logger->LogAccessorCallbacks();
339   LogBuiltins();
340   // Enable stack sampling.
341   processor_->AddCurrentStack(isolate_);
342   processor_->StartSynchronously();
343 }
344 
StopProfiling(const char * title)345 CpuProfile* CpuProfiler::StopProfiling(const char* title) {
346   if (!is_profiling_) return nullptr;
347   StopProcessorIfLastProfile(title);
348   return profiles_->StopProfiling(title);
349 }
350 
StopProfiling(String * title)351 CpuProfile* CpuProfiler::StopProfiling(String* title) {
352   return StopProfiling(profiles_->GetName(title));
353 }
354 
StopProcessorIfLastProfile(const char * title)355 void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
356   if (!profiles_->IsLastProfile(title)) return;
357   StopProcessor();
358 }
359 
StopProcessor()360 void CpuProfiler::StopProcessor() {
361   Logger* logger = isolate_->logger();
362   is_profiling_ = false;
363   isolate_->set_is_profiling(false);
364   ProfilerListener* profiler_listener = logger->profiler_listener();
365   profiler_listener->RemoveObserver(this);
366   processor_->StopSynchronously();
367   logger->TearDownProfilerListener();
368   processor_.reset();
369   generator_.reset();
370   logger->is_logging_ = saved_is_logging_;
371 }
372 
373 
LogBuiltins()374 void CpuProfiler::LogBuiltins() {
375   Builtins* builtins = isolate_->builtins();
376   DCHECK(builtins->is_initialized());
377   for (int i = 0; i < Builtins::builtin_count; i++) {
378     CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
379     ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
380     Builtins::Name id = static_cast<Builtins::Name>(i);
381     rec->start = builtins->builtin(id)->address();
382     rec->builtin_id = id;
383     processor_->Enqueue(evt_rec);
384   }
385 }
386 
387 }  // namespace internal
388 }  // namespace v8
389