Line data Source code
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 <unordered_map>
8 : #include <utility>
9 :
10 : #include "src/base/lazy-instance.h"
11 : #include "src/base/platform/mutex.h"
12 : #include "src/base/template-utils.h"
13 : #include "src/debug/debug.h"
14 : #include "src/deoptimizer.h"
15 : #include "src/frames-inl.h"
16 : #include "src/locked-queue-inl.h"
17 : #include "src/log-inl.h"
18 : #include "src/profiler/cpu-profiler-inl.h"
19 : #include "src/vm-state-inl.h"
20 :
21 : namespace v8 {
22 : namespace internal {
23 :
24 : static const int kProfilerStackSize = 64 * KB;
25 :
26 773 : class CpuSampler : public sampler::Sampler {
27 : public:
28 : CpuSampler(Isolate* isolate, SamplingEventsProcessor* processor)
29 : : sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)),
30 778 : processor_(processor) {}
31 :
32 27002 : void SampleStack(const v8::RegisterState& regs) override {
33 27002 : TickSample* sample = processor_->StartTickSample();
34 54004 : if (sample == nullptr) return;
35 27002 : Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate());
36 27002 : sample->Init(isolate, regs, TickSample::kIncludeCEntryFrame, true);
37 45829 : if (is_counting_samples_ && !sample->timestamp.IsNull()) {
38 18827 : if (sample->state == JS) ++js_sample_count_;
39 18827 : if (sample->state == EXTERNAL) ++external_sample_count_;
40 : }
41 27002 : processor_->FinishTickSample();
42 : }
43 :
44 : private:
45 : SamplingEventsProcessor* processor_;
46 : };
47 :
48 778 : ProfilerEventsProcessor::ProfilerEventsProcessor(Isolate* isolate,
49 : ProfileGenerator* generator)
50 : : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
51 : generator_(generator),
52 : running_(1),
53 : last_code_event_id_(0),
54 : last_processed_code_event_id_(0),
55 1556 : isolate_(isolate) {}
56 :
57 778 : SamplingEventsProcessor::SamplingEventsProcessor(Isolate* isolate,
58 : ProfileGenerator* generator,
59 : base::TimeDelta period)
60 : : ProfilerEventsProcessor(isolate, generator),
61 : sampler_(new CpuSampler(isolate, this)),
62 2334 : period_(period) {
63 778 : sampler_->IncreaseProfilingDepth();
64 778 : }
65 :
66 2319 : SamplingEventsProcessor::~SamplingEventsProcessor() {
67 773 : sampler_->DecreaseProfilingDepth();
68 773 : sampler_->UnregisterIfRegistered();
69 2319 : }
70 :
71 : ProfilerEventsProcessor::~ProfilerEventsProcessor() = default;
72 :
73 1787895 : void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
74 1787895 : event.generic.order = ++last_code_event_id_;
75 1787895 : events_buffer_.Enqueue(event);
76 1787896 : }
77 :
78 0 : void ProfilerEventsProcessor::AddDeoptStack(Address from, int fp_to_sp_delta) {
79 : TickSampleEventRecord record(last_code_event_id_);
80 : RegisterState regs;
81 0 : Address fp = isolate_->c_entry_fp(isolate_->thread_local_top());
82 0 : regs.sp = reinterpret_cast<void*>(fp - fp_to_sp_delta);
83 0 : regs.fp = reinterpret_cast<void*>(fp);
84 0 : regs.pc = reinterpret_cast<void*>(from);
85 : record.sample.Init(isolate_, regs, TickSample::kSkipCEntryFrame, false,
86 0 : false);
87 0 : ticks_from_vm_buffer_.Enqueue(record);
88 0 : }
89 :
90 823 : void ProfilerEventsProcessor::AddCurrentStack(bool update_stats) {
91 : TickSampleEventRecord record(last_code_event_id_);
92 : RegisterState regs;
93 823 : StackFrameIterator it(isolate_);
94 823 : if (!it.done()) {
95 146 : StackFrame* frame = it.frame();
96 73 : regs.sp = reinterpret_cast<void*>(frame->sp());
97 73 : regs.fp = reinterpret_cast<void*>(frame->fp());
98 73 : regs.pc = reinterpret_cast<void*>(frame->pc());
99 : }
100 : record.sample.Init(isolate_, regs, TickSample::kSkipCEntryFrame, update_stats,
101 823 : false);
102 823 : ticks_from_vm_buffer_.Enqueue(record);
103 823 : }
104 :
105 35 : void ProfilerEventsProcessor::AddSample(TickSample sample) {
106 : TickSampleEventRecord record(last_code_event_id_);
107 35 : record.sample = sample;
108 35 : ticks_from_vm_buffer_.Enqueue(record);
109 35 : }
110 :
111 783 : void ProfilerEventsProcessor::StopSynchronously() {
112 2349 : if (!base::Relaxed_AtomicExchange(&running_, 0)) return;
113 778 : Join();
114 : }
115 :
116 :
117 1788629 : bool ProfilerEventsProcessor::ProcessCodeEvent() {
118 : CodeEventsContainer record;
119 1788629 : if (events_buffer_.Dequeue(&record)) {
120 1787875 : switch (record.generic.type) {
121 : #define PROFILER_TYPE_CASE(type, clss) \
122 : case CodeEventRecord::type: \
123 : record.clss##_.UpdateCodeMap(generator_->code_map()); \
124 : break;
125 :
126 1787875 : CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
127 :
128 : #undef PROFILER_TYPE_CASE
129 : default: return true; // Skip record.
130 : }
131 1787849 : last_processed_code_event_id_ = record.generic.order;
132 1787849 : return true;
133 : }
134 : return false;
135 : }
136 :
137 665223 : void ProfilerEventsProcessor::CodeEventHandler(
138 : const CodeEventsContainer& evt_rec) {
139 665223 : switch (evt_rec.generic.type) {
140 : case CodeEventRecord::CODE_CREATION:
141 : case CodeEventRecord::CODE_MOVE:
142 : case CodeEventRecord::CODE_DISABLE_OPT:
143 665223 : Enqueue(evt_rec);
144 665223 : break;
145 : case CodeEventRecord::CODE_DEOPT: {
146 : const CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
147 0 : Address pc = rec->pc;
148 0 : int fp_to_sp_delta = rec->fp_to_sp_delta;
149 0 : Enqueue(evt_rec);
150 0 : AddDeoptStack(pc, fp_to_sp_delta);
151 0 : break;
152 : }
153 : case CodeEventRecord::NONE:
154 : case CodeEventRecord::REPORT_BUILTIN:
155 0 : UNREACHABLE();
156 : }
157 665223 : }
158 :
159 : ProfilerEventsProcessor::SampleProcessingResult
160 1844143 : SamplingEventsProcessor::ProcessOneSample() {
161 : TickSampleEventRecord record1;
162 3632383 : if (ticks_from_vm_buffer_.Peek(&record1) &&
163 1788240 : (record1.order == last_processed_code_event_id_)) {
164 : TickSampleEventRecord record;
165 858 : ticks_from_vm_buffer_.Dequeue(&record);
166 858 : generator_->RecordTickSample(record.sample);
167 : return OneSampleProcessed;
168 : }
169 :
170 1843345 : const TickSampleEventRecord* record = ticks_buffer_.Peek();
171 1843368 : if (record == nullptr) {
172 1276898 : if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
173 1248035 : return FoundSampleForNextCodeEvent;
174 : }
175 566470 : if (record->order != last_processed_code_event_id_) {
176 : return FoundSampleForNextCodeEvent;
177 : }
178 26796 : generator_->RecordTickSample(record->sample);
179 26796 : ticks_buffer_.Remove();
180 26796 : return OneSampleProcessed;
181 : }
182 :
183 778 : void SamplingEventsProcessor::Run() {
184 60612 : while (!!base::Relaxed_Load(&running_)) {
185 : base::TimeTicks nextSampleTime =
186 58278 : base::TimeTicks::HighResolutionNow() + period_;
187 : base::TimeTicks now;
188 : SampleProcessingResult result;
189 : // Keep processing existing events until we need to do next sample
190 : // or the ticks buffer is empty.
191 525645 : do {
192 525645 : result = ProcessOneSample();
193 525660 : if (result == FoundSampleForNextCodeEvent) {
194 : // All ticks of the current last_processed_code_event_id_ are
195 : // processed, proceed to the next code event.
196 471089 : ProcessCodeEvent();
197 : }
198 525665 : now = base::TimeTicks::HighResolutionNow();
199 525645 : } while (result != NoSamplesInQueue && now < nextSampleTime);
200 :
201 29139 : if (nextSampleTime > now) {
202 : #if V8_OS_WIN
203 : if (nextSampleTime - now < base::TimeDelta::FromMilliseconds(100)) {
204 : // Do not use Sleep on Windows as it is very imprecise, with up to 16ms
205 : // jitter, which is unacceptable for short profile intervals.
206 : while (base::TimeTicks::HighResolutionNow() < nextSampleTime) {
207 : }
208 : } else // NOLINT
209 : #endif
210 : {
211 27929 : base::OS::Sleep(nextSampleTime - now);
212 : }
213 : }
214 :
215 : // Schedule next sample.
216 29139 : sampler_->DoSample();
217 : }
218 :
219 : // Process remaining tick events.
220 1317567 : do {
221 : SampleProcessingResult result;
222 1318580 : do {
223 1318580 : result = ProcessOneSample();
224 : } while (result == OneSampleProcessed);
225 1317567 : } while (ProcessCodeEvent());
226 778 : }
227 :
228 30 : void* SamplingEventsProcessor::operator new(size_t size) {
229 778 : return AlignedAlloc(size, alignof(SamplingEventsProcessor));
230 : }
231 :
232 773 : void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); }
233 :
234 155 : int CpuProfiler::GetProfilesCount() {
235 : // The count of profiles doesn't depend on a security token.
236 310 : return static_cast<int>(profiles_->profiles()->size());
237 : }
238 :
239 :
240 35 : CpuProfile* CpuProfiler::GetProfile(int index) {
241 70 : return profiles_->profiles()->at(index).get();
242 : }
243 :
244 :
245 95 : void CpuProfiler::DeleteAllProfiles() {
246 95 : if (is_profiling_) StopProcessor();
247 95 : ResetProfiles();
248 95 : }
249 :
250 :
251 630 : void CpuProfiler::DeleteProfile(CpuProfile* profile) {
252 630 : profiles_->RemoveProfile(profile);
253 630 : if (profiles_->profiles()->empty() && !is_profiling_) {
254 : // If this was the last profile, clean up all accessory data as well.
255 600 : ResetProfiles();
256 : }
257 630 : }
258 :
259 : namespace {
260 :
261 506 : class CpuProfilersManager {
262 : public:
263 273 : void AddProfiler(Isolate* isolate, CpuProfiler* profiler) {
264 273 : base::MutexGuard lock(&mutex_);
265 : profilers_.emplace(isolate, profiler);
266 273 : }
267 :
268 273 : void RemoveProfiler(Isolate* isolate, CpuProfiler* profiler) {
269 273 : base::MutexGuard lock(&mutex_);
270 : auto range = profilers_.equal_range(isolate);
271 546 : for (auto it = range.first; it != range.second; ++it) {
272 273 : if (it->second != profiler) continue;
273 : profilers_.erase(it);
274 273 : return;
275 : }
276 0 : UNREACHABLE();
277 : }
278 :
279 5 : void CallCollectSample(Isolate* isolate) {
280 5 : base::MutexGuard lock(&mutex_);
281 : auto range = profilers_.equal_range(isolate);
282 15 : for (auto it = range.first; it != range.second; ++it) {
283 5 : it->second->CollectSample();
284 : }
285 5 : }
286 :
287 : private:
288 : std::unordered_multimap<Isolate*, CpuProfiler*> profilers_;
289 : base::Mutex mutex_;
290 : };
291 :
292 551 : DEFINE_LAZY_LEAKY_OBJECT_GETTER(CpuProfilersManager, GetProfilersManager);
293 :
294 : } // namespace
295 :
296 253 : CpuProfiler::CpuProfiler(Isolate* isolate)
297 253 : : CpuProfiler(isolate, new CpuProfilesCollection(isolate), nullptr,
298 506 : nullptr) {}
299 :
300 273 : CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilesCollection* test_profiles,
301 : ProfileGenerator* test_generator,
302 : ProfilerEventsProcessor* test_processor)
303 : : isolate_(isolate),
304 : sampling_interval_(base::TimeDelta::FromMicroseconds(
305 273 : FLAG_cpu_profiler_sampling_interval)),
306 : profiles_(test_profiles),
307 : generator_(test_generator),
308 : processor_(test_processor),
309 819 : is_profiling_(false) {
310 : profiles_->set_cpu_profiler(this);
311 273 : GetProfilersManager()->AddProfiler(isolate, this);
312 273 : }
313 :
314 273 : CpuProfiler::~CpuProfiler() {
315 : DCHECK(!is_profiling_);
316 273 : GetProfilersManager()->RemoveProfiler(isolate_, this);
317 273 : }
318 :
319 530 : void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
320 : DCHECK(!is_profiling_);
321 530 : sampling_interval_ = value;
322 530 : }
323 :
324 695 : void CpuProfiler::ResetProfiles() {
325 695 : profiles_.reset(new CpuProfilesCollection(isolate_));
326 : profiles_->set_cpu_profiler(this);
327 : profiler_listener_.reset();
328 : generator_.reset();
329 695 : }
330 :
331 743 : void CpuProfiler::CreateEntriesForRuntimeCallStats() {
332 743 : RuntimeCallStats* rcs = isolate_->counters()->runtime_call_stats();
333 743 : CodeMap* code_map = generator_->code_map();
334 846277 : for (int i = 0; i < RuntimeCallStats::kNumberOfCounters; ++i) {
335 845534 : RuntimeCallCounter* counter = rcs->GetCounter(i);
336 : DCHECK(counter->name());
337 : auto entry = new CodeEntry(CodeEventListener::FUNCTION_TAG, counter->name(),
338 845534 : "native V8Runtime");
339 845534 : code_map->AddCode(reinterpret_cast<Address>(counter), entry, 1);
340 : }
341 743 : }
342 :
343 : // static
344 5 : void CpuProfiler::CollectSample(Isolate* isolate) {
345 5 : GetProfilersManager()->CallCollectSample(isolate);
346 5 : }
347 :
348 0 : void CpuProfiler::CollectSample() {
349 5 : if (processor_) {
350 5 : processor_->AddCurrentStack();
351 : }
352 0 : }
353 :
354 793 : void CpuProfiler::StartProfiling(const char* title, bool record_samples,
355 : ProfilingMode mode) {
356 1586 : if (profiles_->StartProfiling(title, record_samples, mode)) {
357 1586 : TRACE_EVENT0("v8", "CpuProfiler::StartProfiling");
358 793 : StartProcessorIfNotStarted();
359 : }
360 793 : }
361 :
362 743 : void CpuProfiler::StartProfiling(String title, bool record_samples,
363 : ProfilingMode mode) {
364 1486 : StartProfiling(profiles_->GetName(title), record_samples, mode);
365 743 : isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler);
366 743 : }
367 :
368 793 : void CpuProfiler::StartProcessorIfNotStarted() {
369 793 : if (processor_) {
370 45 : processor_->AddCurrentStack();
371 838 : return;
372 : }
373 748 : Logger* logger = isolate_->logger();
374 : // Disable logging when using the new implementation.
375 748 : saved_is_logging_ = logger->is_logging_;
376 748 : logger->is_logging_ = false;
377 :
378 : bool codemap_needs_initialization = false;
379 748 : if (!generator_) {
380 743 : generator_.reset(new ProfileGenerator(profiles_.get()));
381 : codemap_needs_initialization = true;
382 743 : CreateEntriesForRuntimeCallStats();
383 : }
384 : processor_.reset(new SamplingEventsProcessor(isolate_, generator_.get(),
385 2244 : sampling_interval_));
386 748 : if (!profiler_listener_) {
387 743 : profiler_listener_.reset(new ProfilerListener(isolate_, processor_.get()));
388 : }
389 748 : logger->AddCodeEventListener(profiler_listener_.get());
390 748 : is_profiling_ = true;
391 748 : isolate_->set_is_profiling(true);
392 : // Enumerate stuff we already have in the heap.
393 : DCHECK(isolate_->heap()->HasBeenSetUp());
394 748 : if (codemap_needs_initialization) {
395 743 : if (!FLAG_prof_browser_mode) {
396 25 : logger->LogCodeObjects();
397 : }
398 743 : logger->LogCompiledFunctions();
399 743 : logger->LogAccessorCallbacks();
400 743 : LogBuiltins();
401 : }
402 : // Enable stack sampling.
403 748 : processor_->AddCurrentStack();
404 748 : processor_->StartSynchronously();
405 : }
406 :
407 703 : CpuProfile* CpuProfiler::StopProfiling(const char* title) {
408 703 : if (!is_profiling_) return nullptr;
409 703 : StopProcessorIfLastProfile(title);
410 703 : return profiles_->StopProfiling(title);
411 : }
412 :
413 663 : CpuProfile* CpuProfiler::StopProfiling(String title) {
414 663 : return StopProfiling(profiles_->GetName(title));
415 : }
416 :
417 703 : void CpuProfiler::StopProcessorIfLastProfile(const char* title) {
418 1406 : if (!profiles_->IsLastProfile(title)) return;
419 668 : StopProcessor();
420 : }
421 :
422 748 : void CpuProfiler::StopProcessor() {
423 748 : Logger* logger = isolate_->logger();
424 748 : is_profiling_ = false;
425 : isolate_->set_is_profiling(false);
426 748 : logger->RemoveCodeEventListener(profiler_listener_.get());
427 748 : processor_->StopSynchronously();
428 : processor_.reset();
429 748 : logger->is_logging_ = saved_is_logging_;
430 748 : }
431 :
432 :
433 743 : void CpuProfiler::LogBuiltins() {
434 743 : Builtins* builtins = isolate_->builtins();
435 : DCHECK(builtins->is_initialized());
436 1123416 : for (int i = 0; i < Builtins::builtin_count; i++) {
437 : CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN);
438 : ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
439 : Builtins::Name id = static_cast<Builtins::Name>(i);
440 1122673 : rec->instruction_start = builtins->builtin(id)->InstructionStart();
441 1122673 : rec->builtin_id = id;
442 1122673 : processor_->Enqueue(evt_rec);
443 : }
444 743 : }
445 :
446 : } // namespace internal
447 183867 : } // namespace v8
|