• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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