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