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 "include/v8-locker.h"
11 #include "src/base/lazy-instance.h"
12 #include "src/base/template-utils.h"
13 #include "src/debug/debug.h"
14 #include "src/execution/frames-inl.h"
15 #include "src/execution/v8threads.h"
16 #include "src/execution/vm-state-inl.h"
17 #include "src/libsampler/sampler.h"
18 #include "src/logging/counters.h"
19 #include "src/logging/log.h"
20 #include "src/profiler/cpu-profiler-inl.h"
21 #include "src/profiler/profiler-stats.h"
22 #include "src/profiler/symbolizer.h"
23 #include "src/utils/locked-queue-inl.h"
24
25 #if V8_ENABLE_WEBASSEMBLY
26 #include "src/wasm/wasm-engine.h"
27 #endif // V8_ENABLE_WEBASSEMBLY
28
29 namespace v8 {
30 namespace internal {
31
32 static const int kProfilerStackSize = 64 * KB;
33
34 class CpuSampler : public sampler::Sampler {
35 public:
CpuSampler(Isolate * isolate,SamplingEventsProcessor * processor)36 CpuSampler(Isolate* isolate, SamplingEventsProcessor* processor)
37 : sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)),
38 processor_(processor),
39 perThreadData_(isolate->FindPerThreadDataForThisThread()) {}
40
SampleStack(const v8::RegisterState & regs)41 void SampleStack(const v8::RegisterState& regs) override {
42 Isolate* isolate = reinterpret_cast<Isolate*>(this->isolate());
43 if (isolate->was_locker_ever_used() &&
44 (!isolate->thread_manager()->IsLockedByThread(
45 perThreadData_->thread_id()) ||
46 perThreadData_->thread_state() != nullptr)) {
47 ProfilerStats::Instance()->AddReason(
48 ProfilerStats::Reason::kIsolateNotLocked);
49 return;
50 }
51 TickSample* sample = processor_->StartTickSample();
52 if (sample == nullptr) {
53 ProfilerStats::Instance()->AddReason(
54 ProfilerStats::Reason::kTickBufferFull);
55 return;
56 }
57 // Every bailout up until here resulted in a dropped sample. From now on,
58 // the sample is created in the buffer.
59 sample->Init(isolate, regs, TickSample::kIncludeCEntryFrame,
60 /* update_stats */ true,
61 /* use_simulator_reg_state */ true, processor_->period());
62 if (is_counting_samples_ && !sample->timestamp.IsNull()) {
63 if (sample->state == JS) ++js_sample_count_;
64 if (sample->state == EXTERNAL) ++external_sample_count_;
65 }
66 processor_->FinishTickSample();
67 }
68
69 private:
70 SamplingEventsProcessor* processor_;
71 Isolate::PerIsolateThreadData* perThreadData_;
72 };
73
ProfilingScope(Isolate * isolate,ProfilerListener * listener)74 ProfilingScope::ProfilingScope(Isolate* isolate, ProfilerListener* listener)
75 : isolate_(isolate), listener_(listener) {
76 size_t profiler_count = isolate_->num_cpu_profilers();
77 profiler_count++;
78 isolate_->set_num_cpu_profilers(profiler_count);
79 isolate_->SetIsProfiling(true);
80 #if V8_ENABLE_WEBASSEMBLY
81 wasm::GetWasmEngine()->EnableCodeLogging(isolate_);
82 #endif // V8_ENABLE_WEBASSEMBLY
83
84 Logger* logger = isolate_->logger();
85 logger->AddCodeEventListener(listener_);
86 // Populate the ProfilerCodeObserver with the initial functions and
87 // callbacks on the heap.
88 DCHECK(isolate_->heap()->HasBeenSetUp());
89
90 if (!FLAG_prof_browser_mode) {
91 logger->LogCodeObjects();
92 }
93 logger->LogCompiledFunctions();
94 logger->LogAccessorCallbacks();
95 }
96
~ProfilingScope()97 ProfilingScope::~ProfilingScope() {
98 isolate_->logger()->RemoveCodeEventListener(listener_);
99
100 size_t profiler_count = isolate_->num_cpu_profilers();
101 DCHECK_GT(profiler_count, 0);
102 profiler_count--;
103 isolate_->set_num_cpu_profilers(profiler_count);
104 if (profiler_count == 0) isolate_->SetIsProfiling(false);
105 }
106
ProfilerEventsProcessor(Isolate * isolate,Symbolizer * symbolizer,ProfilerCodeObserver * code_observer,CpuProfilesCollection * profiles)107 ProfilerEventsProcessor::ProfilerEventsProcessor(
108 Isolate* isolate, Symbolizer* symbolizer,
109 ProfilerCodeObserver* code_observer, CpuProfilesCollection* profiles)
110 : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)),
111 symbolizer_(symbolizer),
112 code_observer_(code_observer),
113 profiles_(profiles),
114 last_code_event_id_(0),
115 last_processed_code_event_id_(0),
116 isolate_(isolate) {
117 DCHECK(!code_observer_->processor());
118 code_observer_->set_processor(this);
119 }
120
SamplingEventsProcessor(Isolate * isolate,Symbolizer * symbolizer,ProfilerCodeObserver * code_observer,CpuProfilesCollection * profiles,base::TimeDelta period,bool use_precise_sampling)121 SamplingEventsProcessor::SamplingEventsProcessor(
122 Isolate* isolate, Symbolizer* symbolizer,
123 ProfilerCodeObserver* code_observer, CpuProfilesCollection* profiles,
124 base::TimeDelta period, bool use_precise_sampling)
125 : ProfilerEventsProcessor(isolate, symbolizer, code_observer, profiles),
126 sampler_(new CpuSampler(isolate, this)),
127 period_(period),
128 use_precise_sampling_(use_precise_sampling) {
129 sampler_->Start();
130 }
131
~SamplingEventsProcessor()132 SamplingEventsProcessor::~SamplingEventsProcessor() { sampler_->Stop(); }
133
~ProfilerEventsProcessor()134 ProfilerEventsProcessor::~ProfilerEventsProcessor() {
135 DCHECK_EQ(code_observer_->processor(), this);
136 code_observer_->clear_processor();
137 }
138
Enqueue(const CodeEventsContainer & event)139 void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) {
140 event.generic.order = ++last_code_event_id_;
141 events_buffer_.Enqueue(event);
142 }
143
AddDeoptStack(Address from,int fp_to_sp_delta)144 void ProfilerEventsProcessor::AddDeoptStack(Address from, int fp_to_sp_delta) {
145 TickSampleEventRecord record(last_code_event_id_);
146 RegisterState regs;
147 Address fp = isolate_->c_entry_fp(isolate_->thread_local_top());
148 regs.sp = reinterpret_cast<void*>(fp - fp_to_sp_delta);
149 regs.fp = reinterpret_cast<void*>(fp);
150 regs.pc = reinterpret_cast<void*>(from);
151 record.sample.Init(isolate_, regs, TickSample::kSkipCEntryFrame, false,
152 false);
153 ticks_from_vm_buffer_.Enqueue(record);
154 }
155
AddCurrentStack(bool update_stats)156 void ProfilerEventsProcessor::AddCurrentStack(bool update_stats) {
157 TickSampleEventRecord record(last_code_event_id_);
158 RegisterState regs;
159 StackFrameIterator it(isolate_);
160 if (!it.done()) {
161 StackFrame* frame = it.frame();
162 regs.sp = reinterpret_cast<void*>(frame->sp());
163 regs.fp = reinterpret_cast<void*>(frame->fp());
164 regs.pc = reinterpret_cast<void*>(frame->pc());
165 }
166 record.sample.Init(isolate_, regs, TickSample::kSkipCEntryFrame, update_stats,
167 false);
168 ticks_from_vm_buffer_.Enqueue(record);
169 }
170
AddSample(TickSample sample)171 void ProfilerEventsProcessor::AddSample(TickSample sample) {
172 TickSampleEventRecord record(last_code_event_id_);
173 record.sample = sample;
174 ticks_from_vm_buffer_.Enqueue(record);
175 }
176
StopSynchronously()177 void ProfilerEventsProcessor::StopSynchronously() {
178 bool expected = true;
179 if (!running_.compare_exchange_strong(expected, false,
180 std::memory_order_relaxed))
181 return;
182 {
183 base::MutexGuard guard(&running_mutex_);
184 running_cond_.NotifyOne();
185 }
186 Join();
187 }
188
189
ProcessCodeEvent()190 bool ProfilerEventsProcessor::ProcessCodeEvent() {
191 CodeEventsContainer record;
192 if (events_buffer_.Dequeue(&record)) {
193 if (record.generic.type == CodeEventRecord::Type::kNativeContextMove) {
194 NativeContextMoveEventRecord& nc_record =
195 record.NativeContextMoveEventRecord_;
196 profiles_->UpdateNativeContextAddressForCurrentProfiles(
197 nc_record.from_address, nc_record.to_address);
198 } else {
199 code_observer_->CodeEventHandlerInternal(record);
200 }
201 last_processed_code_event_id_ = record.generic.order;
202 return true;
203 }
204 return false;
205 }
206
CodeEventHandler(const CodeEventsContainer & evt_rec)207 void ProfilerEventsProcessor::CodeEventHandler(
208 const CodeEventsContainer& evt_rec) {
209 switch (evt_rec.generic.type) {
210 case CodeEventRecord::Type::kCodeCreation:
211 case CodeEventRecord::Type::kCodeMove:
212 case CodeEventRecord::Type::kCodeDisableOpt:
213 case CodeEventRecord::Type::kCodeDelete:
214 case CodeEventRecord::Type::kNativeContextMove:
215 Enqueue(evt_rec);
216 break;
217 case CodeEventRecord::Type::kCodeDeopt: {
218 const CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
219 Address pc = rec->pc;
220 int fp_to_sp_delta = rec->fp_to_sp_delta;
221 Enqueue(evt_rec);
222 AddDeoptStack(pc, fp_to_sp_delta);
223 break;
224 }
225 case CodeEventRecord::Type::kNoEvent:
226 case CodeEventRecord::Type::kReportBuiltin:
227 UNREACHABLE();
228 }
229 }
230
SymbolizeAndAddToProfiles(const TickSampleEventRecord * record)231 void SamplingEventsProcessor::SymbolizeAndAddToProfiles(
232 const TickSampleEventRecord* record) {
233 const TickSample& tick_sample = record->sample;
234 Symbolizer::SymbolizedSample symbolized =
235 symbolizer_->SymbolizeTickSample(tick_sample);
236 profiles_->AddPathToCurrentProfiles(
237 tick_sample.timestamp, symbolized.stack_trace, symbolized.src_line,
238 tick_sample.update_stats_, tick_sample.sampling_interval_,
239 tick_sample.state, tick_sample.embedder_state,
240 reinterpret_cast<Address>(tick_sample.context),
241 reinterpret_cast<Address>(tick_sample.embedder_context));
242 }
243
244 ProfilerEventsProcessor::SampleProcessingResult
ProcessOneSample()245 SamplingEventsProcessor::ProcessOneSample() {
246 TickSampleEventRecord record1;
247 if (ticks_from_vm_buffer_.Peek(&record1) &&
248 (record1.order == last_processed_code_event_id_)) {
249 TickSampleEventRecord record;
250 ticks_from_vm_buffer_.Dequeue(&record);
251 SymbolizeAndAddToProfiles(&record);
252 return OneSampleProcessed;
253 }
254
255 const TickSampleEventRecord* record = ticks_buffer_.Peek();
256 if (record == nullptr) {
257 if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue;
258 return FoundSampleForNextCodeEvent;
259 }
260 if (record->order != last_processed_code_event_id_) {
261 return FoundSampleForNextCodeEvent;
262 }
263 SymbolizeAndAddToProfiles(record);
264 ticks_buffer_.Remove();
265 return OneSampleProcessed;
266 }
267
Run()268 void SamplingEventsProcessor::Run() {
269 base::MutexGuard guard(&running_mutex_);
270 while (running_.load(std::memory_order_relaxed)) {
271 base::TimeTicks nextSampleTime = base::TimeTicks::Now() + period_;
272 base::TimeTicks now;
273 SampleProcessingResult result;
274 // Keep processing existing events until we need to do next sample
275 // or the ticks buffer is empty.
276 do {
277 result = ProcessOneSample();
278 if (result == FoundSampleForNextCodeEvent) {
279 // All ticks of the current last_processed_code_event_id_ are
280 // processed, proceed to the next code event.
281 ProcessCodeEvent();
282 }
283 now = base::TimeTicks::Now();
284 } while (result != NoSamplesInQueue && now < nextSampleTime);
285
286 if (nextSampleTime > now) {
287 #if V8_OS_WIN
288 if (use_precise_sampling_ &&
289 nextSampleTime - now < base::TimeDelta::FromMilliseconds(100)) {
290 // Do not use Sleep on Windows as it is very imprecise, with up to 16ms
291 // jitter, which is unacceptable for short profile intervals.
292 while (base::TimeTicks::Now() < nextSampleTime) {
293 }
294 } else // NOLINT
295 #else
296 USE(use_precise_sampling_);
297 #endif // V8_OS_WIN
298 {
299 // Allow another thread to interrupt the delay between samples in the
300 // event of profiler shutdown.
301 while (now < nextSampleTime &&
302 running_cond_.WaitFor(&running_mutex_, nextSampleTime - now)) {
303 // If true was returned, we got interrupted before the timeout
304 // elapsed. If this was not due to a change in running state, a
305 // spurious wakeup occurred (thus we should continue to wait).
306 if (!running_.load(std::memory_order_relaxed)) {
307 break;
308 }
309 now = base::TimeTicks::Now();
310 }
311 }
312 }
313
314 // Schedule next sample.
315 sampler_->DoSample();
316 }
317
318 // Process remaining tick events.
319 do {
320 SampleProcessingResult result;
321 do {
322 result = ProcessOneSample();
323 } while (result == OneSampleProcessed);
324 } while (ProcessCodeEvent());
325 }
326
SetSamplingInterval(base::TimeDelta period)327 void SamplingEventsProcessor::SetSamplingInterval(base::TimeDelta period) {
328 if (period_ == period) return;
329 StopSynchronously();
330
331 period_ = period;
332 running_.store(true, std::memory_order_relaxed);
333
334 StartSynchronously();
335 }
336
operator new(size_t size)337 void* SamplingEventsProcessor::operator new(size_t size) {
338 return AlignedAlloc(size, alignof(SamplingEventsProcessor));
339 }
340
operator delete(void * ptr)341 void SamplingEventsProcessor::operator delete(void* ptr) { AlignedFree(ptr); }
342
ProfilerCodeObserver(Isolate * isolate,CodeEntryStorage & storage)343 ProfilerCodeObserver::ProfilerCodeObserver(Isolate* isolate,
344 CodeEntryStorage& storage)
345 : isolate_(isolate),
346 code_entries_(storage),
347 code_map_(storage),
348 weak_code_registry_(isolate),
349 processor_(nullptr) {
350 CreateEntriesForRuntimeCallStats();
351 LogBuiltins();
352 }
353
ClearCodeMap()354 void ProfilerCodeObserver::ClearCodeMap() {
355 weak_code_registry_.Clear();
356 code_map_.Clear();
357 }
358
CodeEventHandler(const CodeEventsContainer & evt_rec)359 void ProfilerCodeObserver::CodeEventHandler(
360 const CodeEventsContainer& evt_rec) {
361 if (processor_) {
362 processor_->CodeEventHandler(evt_rec);
363 return;
364 }
365 CodeEventHandlerInternal(evt_rec);
366 }
367
GetEstimatedMemoryUsage() const368 size_t ProfilerCodeObserver::GetEstimatedMemoryUsage() const {
369 // To avoid race condition in codemap,
370 // for now limit computation in kEagerLogging mode
371 if (!processor_) {
372 return sizeof(*this) + code_map_.GetEstimatedMemoryUsage() +
373 code_entries_.strings().GetStringSize();
374 }
375 return 0;
376 }
377
CodeEventHandlerInternal(const CodeEventsContainer & evt_rec)378 void ProfilerCodeObserver::CodeEventHandlerInternal(
379 const CodeEventsContainer& evt_rec) {
380 CodeEventsContainer record = evt_rec;
381 switch (evt_rec.generic.type) {
382 #define PROFILER_TYPE_CASE(type, clss) \
383 case CodeEventRecord::Type::type: \
384 record.clss##_.UpdateCodeMap(&code_map_); \
385 break;
386
387 CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE)
388
389 #undef PROFILER_TYPE_CASE
390 default:
391 break;
392 }
393 }
394
CreateEntriesForRuntimeCallStats()395 void ProfilerCodeObserver::CreateEntriesForRuntimeCallStats() {
396 #ifdef V8_RUNTIME_CALL_STATS
397 RuntimeCallStats* rcs = isolate_->counters()->runtime_call_stats();
398 for (int i = 0; i < RuntimeCallStats::kNumberOfCounters; ++i) {
399 RuntimeCallCounter* counter = rcs->GetCounter(i);
400 DCHECK(counter->name());
401 auto entry = code_entries_.Create(CodeEventListener::FUNCTION_TAG,
402 counter->name(), "native V8Runtime");
403 code_map_.AddCode(reinterpret_cast<Address>(counter), entry, 1);
404 }
405 #endif // V8_RUNTIME_CALL_STATS
406 }
407
LogBuiltins()408 void ProfilerCodeObserver::LogBuiltins() {
409 Builtins* builtins = isolate_->builtins();
410 DCHECK(builtins->is_initialized());
411 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast;
412 ++builtin) {
413 CodeEventsContainer evt_rec(CodeEventRecord::Type::kReportBuiltin);
414 ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_;
415 Code code = FromCodeT(builtins->code(builtin));
416 rec->instruction_start = code.InstructionStart();
417 rec->instruction_size = code.InstructionSize();
418 rec->builtin = builtin;
419 CodeEventHandlerInternal(evt_rec);
420 }
421 }
422
GetProfilesCount()423 int CpuProfiler::GetProfilesCount() {
424 // The count of profiles doesn't depend on a security token.
425 return static_cast<int>(profiles_->profiles()->size());
426 }
427
428
GetProfile(int index)429 CpuProfile* CpuProfiler::GetProfile(int index) {
430 return profiles_->profiles()->at(index).get();
431 }
432
433
DeleteAllProfiles()434 void CpuProfiler::DeleteAllProfiles() {
435 if (is_profiling_) StopProcessor();
436 ResetProfiles();
437 }
438
439
DeleteProfile(CpuProfile * profile)440 void CpuProfiler::DeleteProfile(CpuProfile* profile) {
441 profiles_->RemoveProfile(profile);
442 if (profiles_->profiles()->empty() && !is_profiling_) {
443 // If this was the last profile, clean up all accessory data as well.
444 ResetProfiles();
445 }
446 }
447
448 namespace {
449
450 class CpuProfilersManager {
451 public:
AddProfiler(Isolate * isolate,CpuProfiler * profiler)452 void AddProfiler(Isolate* isolate, CpuProfiler* profiler) {
453 base::MutexGuard lock(&mutex_);
454 profilers_.emplace(isolate, profiler);
455 }
456
RemoveProfiler(Isolate * isolate,CpuProfiler * profiler)457 void RemoveProfiler(Isolate* isolate, CpuProfiler* profiler) {
458 base::MutexGuard lock(&mutex_);
459 auto range = profilers_.equal_range(isolate);
460 for (auto it = range.first; it != range.second; ++it) {
461 if (it->second != profiler) continue;
462 profilers_.erase(it);
463 return;
464 }
465 UNREACHABLE();
466 }
467
CallCollectSample(Isolate * isolate)468 void CallCollectSample(Isolate* isolate) {
469 base::MutexGuard lock(&mutex_);
470 auto range = profilers_.equal_range(isolate);
471 for (auto it = range.first; it != range.second; ++it) {
472 it->second->CollectSample();
473 }
474 }
475
GetAllProfilersMemorySize(Isolate * isolate)476 size_t GetAllProfilersMemorySize(Isolate* isolate) {
477 base::MutexGuard lock(&mutex_);
478 size_t estimated_memory = 0;
479 auto range = profilers_.equal_range(isolate);
480 for (auto it = range.first; it != range.second; ++it) {
481 estimated_memory += it->second->GetEstimatedMemoryUsage();
482 }
483 return estimated_memory;
484 }
485
486 private:
487 std::unordered_multimap<Isolate*, CpuProfiler*> profilers_;
488 base::Mutex mutex_;
489 };
490
491 DEFINE_LAZY_LEAKY_OBJECT_GETTER(CpuProfilersManager, GetProfilersManager)
492
493 } // namespace
494
CpuProfiler(Isolate * isolate,CpuProfilingNamingMode naming_mode,CpuProfilingLoggingMode logging_mode)495 CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode,
496 CpuProfilingLoggingMode logging_mode)
497 : CpuProfiler(isolate, naming_mode, logging_mode,
498 new CpuProfilesCollection(isolate), nullptr, nullptr,
499 new ProfilerCodeObserver(isolate, code_entries_)) {}
500
CpuProfiler(Isolate * isolate,CpuProfilingNamingMode naming_mode,CpuProfilingLoggingMode logging_mode,CpuProfilesCollection * test_profiles,Symbolizer * test_symbolizer,ProfilerEventsProcessor * test_processor,ProfilerCodeObserver * test_code_observer)501 CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilingNamingMode naming_mode,
502 CpuProfilingLoggingMode logging_mode,
503 CpuProfilesCollection* test_profiles,
504 Symbolizer* test_symbolizer,
505 ProfilerEventsProcessor* test_processor,
506 ProfilerCodeObserver* test_code_observer)
507 : isolate_(isolate),
508 naming_mode_(naming_mode),
509 logging_mode_(logging_mode),
510 base_sampling_interval_(base::TimeDelta::FromMicroseconds(
511 FLAG_cpu_profiler_sampling_interval)),
512 code_observer_(test_code_observer),
513 profiles_(test_profiles),
514 symbolizer_(test_symbolizer),
515 processor_(test_processor),
516 is_profiling_(false) {
517 profiles_->set_cpu_profiler(this);
518 GetProfilersManager()->AddProfiler(isolate, this);
519
520 if (logging_mode == kEagerLogging) EnableLogging();
521 }
522
~CpuProfiler()523 CpuProfiler::~CpuProfiler() {
524 DCHECK(!is_profiling_);
525 GetProfilersManager()->RemoveProfiler(isolate_, this);
526
527 DisableLogging();
528 profiles_.reset();
529
530 // We don't currently expect any references to refcounted strings to be
531 // maintained with zero profiles after the code map is cleared.
532 DCHECK(code_entries_.strings().empty());
533 }
534
set_sampling_interval(base::TimeDelta value)535 void CpuProfiler::set_sampling_interval(base::TimeDelta value) {
536 DCHECK(!is_profiling_);
537 base_sampling_interval_ = value;
538 }
539
set_use_precise_sampling(bool value)540 void CpuProfiler::set_use_precise_sampling(bool value) {
541 DCHECK(!is_profiling_);
542 use_precise_sampling_ = value;
543 }
544
ResetProfiles()545 void CpuProfiler::ResetProfiles() {
546 profiles_.reset(new CpuProfilesCollection(isolate_));
547 profiles_->set_cpu_profiler(this);
548 }
549
EnableLogging()550 void CpuProfiler::EnableLogging() {
551 if (profiling_scope_) return;
552
553 if (!profiler_listener_) {
554 profiler_listener_.reset(new ProfilerListener(
555 isolate_, code_observer_.get(), *code_observer_->code_entries(),
556 *code_observer_->weak_code_registry(), naming_mode_));
557 }
558 profiling_scope_.reset(
559 new ProfilingScope(isolate_, profiler_listener_.get()));
560 }
561
DisableLogging()562 void CpuProfiler::DisableLogging() {
563 if (!profiling_scope_) return;
564
565 DCHECK(profiler_listener_);
566 profiling_scope_.reset();
567 profiler_listener_.reset();
568 code_observer_->ClearCodeMap();
569 }
570
ComputeSamplingInterval() const571 base::TimeDelta CpuProfiler::ComputeSamplingInterval() const {
572 return profiles_->GetCommonSamplingInterval();
573 }
574
AdjustSamplingInterval()575 void CpuProfiler::AdjustSamplingInterval() {
576 if (!processor_) return;
577
578 base::TimeDelta base_interval = ComputeSamplingInterval();
579 processor_->SetSamplingInterval(base_interval);
580 }
581
582 // static
CollectSample(Isolate * isolate)583 void CpuProfiler::CollectSample(Isolate* isolate) {
584 GetProfilersManager()->CallCollectSample(isolate);
585 }
586
CollectSample()587 void CpuProfiler::CollectSample() {
588 if (processor_) {
589 processor_->AddCurrentStack();
590 }
591 }
592
593 // static
GetAllProfilersMemorySize(Isolate * isolate)594 size_t CpuProfiler::GetAllProfilersMemorySize(Isolate* isolate) {
595 return GetProfilersManager()->GetAllProfilersMemorySize(isolate);
596 }
597
GetEstimatedMemoryUsage() const598 size_t CpuProfiler::GetEstimatedMemoryUsage() const {
599 return code_observer_->GetEstimatedMemoryUsage();
600 }
601
StartProfiling(CpuProfilingOptions options,std::unique_ptr<DiscardedSamplesDelegate> delegate)602 CpuProfilingResult CpuProfiler::StartProfiling(
603 CpuProfilingOptions options,
604 std::unique_ptr<DiscardedSamplesDelegate> delegate) {
605 return StartProfiling(nullptr, options, std::move(delegate));
606 }
607
StartProfiling(const char * title,CpuProfilingOptions options,std::unique_ptr<DiscardedSamplesDelegate> delegate)608 CpuProfilingResult CpuProfiler::StartProfiling(
609 const char* title, CpuProfilingOptions options,
610 std::unique_ptr<DiscardedSamplesDelegate> delegate) {
611 CpuProfilingResult result =
612 profiles_->StartProfiling(title, options, std::move(delegate));
613
614 // TODO(nicodubus): Revisit logic for if we want to do anything different for
615 // kAlreadyStarted
616 if (result.status == CpuProfilingStatus::kStarted ||
617 result.status == CpuProfilingStatus::kAlreadyStarted) {
618 TRACE_EVENT0("v8", "CpuProfiler::StartProfiling");
619 AdjustSamplingInterval();
620 StartProcessorIfNotStarted();
621 }
622
623 return result;
624 }
625
StartProfiling(String title,CpuProfilingOptions options,std::unique_ptr<DiscardedSamplesDelegate> delegate)626 CpuProfilingResult CpuProfiler::StartProfiling(
627 String title, CpuProfilingOptions options,
628 std::unique_ptr<DiscardedSamplesDelegate> delegate) {
629 return StartProfiling(profiles_->GetName(title), options,
630 std::move(delegate));
631 }
632
StartProcessorIfNotStarted()633 void CpuProfiler::StartProcessorIfNotStarted() {
634 if (processor_) {
635 processor_->AddCurrentStack();
636 return;
637 }
638
639 if (!profiling_scope_) {
640 DCHECK_EQ(logging_mode_, kLazyLogging);
641 EnableLogging();
642 }
643
644 if (!symbolizer_) {
645 symbolizer_ = std::make_unique<Symbolizer>(code_observer_->code_map());
646 }
647
648 base::TimeDelta sampling_interval = ComputeSamplingInterval();
649 processor_.reset(new SamplingEventsProcessor(
650 isolate_, symbolizer_.get(), code_observer_.get(), profiles_.get(),
651 sampling_interval, use_precise_sampling_));
652 is_profiling_ = true;
653
654 // Enable stack sampling.
655 processor_->AddCurrentStack();
656 processor_->StartSynchronously();
657 }
658
StopProfiling(const char * title)659 CpuProfile* CpuProfiler::StopProfiling(const char* title) {
660 CpuProfile* profile = profiles_->Lookup(title);
661 if (profile) {
662 return StopProfiling(profile->id());
663 }
664 return nullptr;
665 }
666
StopProfiling(ProfilerId id)667 CpuProfile* CpuProfiler::StopProfiling(ProfilerId id) {
668 if (!is_profiling_) return nullptr;
669 const bool last_profile = profiles_->IsLastProfileLeft(id);
670 if (last_profile) StopProcessor();
671
672 CpuProfile* profile = profiles_->StopProfiling(id);
673
674 AdjustSamplingInterval();
675
676 DCHECK(profiling_scope_);
677 if (last_profile && logging_mode_ == kLazyLogging) {
678 DisableLogging();
679 }
680
681 return profile;
682 }
683
StopProfiling(String title)684 CpuProfile* CpuProfiler::StopProfiling(String title) {
685 return StopProfiling(profiles_->GetName(title));
686 }
687
StopProcessor()688 void CpuProfiler::StopProcessor() {
689 is_profiling_ = false;
690 processor_->StopSynchronously();
691 processor_.reset();
692 }
693 } // namespace internal
694 } // namespace v8
695