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#include "include/v8-profiler.h" 16 17namespace v8 { 18namespace internal { 19 20static const int kProfilerStackSize = 64 * KB; 21 22 23ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator, 24 sampler::Sampler* sampler, 25 base::TimeDelta period) 26 : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)), 27 generator_(generator), 28 sampler_(sampler), 29 running_(1), 30 period_(period), 31 last_code_event_id_(0), 32 last_processed_code_event_id_(0) {} 33 34 35ProfilerEventsProcessor::~ProfilerEventsProcessor() {} 36 37 38void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) { 39 event.generic.order = last_code_event_id_.Increment(1); 40 events_buffer_.Enqueue(event); 41} 42 43 44void ProfilerEventsProcessor::AddDeoptStack(Isolate* isolate, Address from, 45 int fp_to_sp_delta) { 46 TickSampleEventRecord record(last_code_event_id_.Value()); 47 RegisterState regs; 48 Address fp = isolate->c_entry_fp(isolate->thread_local_top()); 49 regs.sp = fp - fp_to_sp_delta; 50 regs.fp = fp; 51 regs.pc = from; 52 record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, false); 53 ticks_from_vm_buffer_.Enqueue(record); 54} 55 56void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate, 57 bool update_stats) { 58 TickSampleEventRecord record(last_code_event_id_.Value()); 59 RegisterState regs; 60 StackFrameIterator it(isolate); 61 if (!it.done()) { 62 StackFrame* frame = it.frame(); 63 regs.sp = frame->sp(); 64 regs.fp = frame->fp(); 65 regs.pc = frame->pc(); 66 } 67 record.sample.Init(isolate, regs, TickSample::kSkipCEntryFrame, update_stats); 68 ticks_from_vm_buffer_.Enqueue(record); 69} 70 71 72void ProfilerEventsProcessor::StopSynchronously() { 73 if (!base::NoBarrier_AtomicExchange(&running_, 0)) return; 74 Join(); 75} 76 77 78bool ProfilerEventsProcessor::ProcessCodeEvent() { 79 CodeEventsContainer record; 80 if (events_buffer_.Dequeue(&record)) { 81 switch (record.generic.type) { 82#define PROFILER_TYPE_CASE(type, clss) \ 83 case CodeEventRecord::type: \ 84 record.clss##_.UpdateCodeMap(generator_->code_map()); \ 85 break; 86 87 CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE) 88 89#undef PROFILER_TYPE_CASE 90 default: return true; // Skip record. 91 } 92 last_processed_code_event_id_ = record.generic.order; 93 return true; 94 } 95 return false; 96} 97 98ProfilerEventsProcessor::SampleProcessingResult 99 ProfilerEventsProcessor::ProcessOneSample() { 100 TickSampleEventRecord record1; 101 if (ticks_from_vm_buffer_.Peek(&record1) && 102 (record1.order == last_processed_code_event_id_)) { 103 TickSampleEventRecord record; 104 ticks_from_vm_buffer_.Dequeue(&record); 105 generator_->RecordTickSample(record.sample); 106 return OneSampleProcessed; 107 } 108 109 const TickSampleEventRecord* record = ticks_buffer_.Peek(); 110 if (record == NULL) { 111 if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue; 112 return FoundSampleForNextCodeEvent; 113 } 114 if (record->order != last_processed_code_event_id_) { 115 return FoundSampleForNextCodeEvent; 116 } 117 generator_->RecordTickSample(record->sample); 118 ticks_buffer_.Remove(); 119 return OneSampleProcessed; 120} 121 122 123void ProfilerEventsProcessor::Run() { 124 while (!!base::NoBarrier_Load(&running_)) { 125 base::TimeTicks nextSampleTime = 126 base::TimeTicks::HighResolutionNow() + period_; 127 base::TimeTicks now; 128 SampleProcessingResult result; 129 // Keep processing existing events until we need to do next sample 130 // or the ticks buffer is empty. 131 do { 132 result = ProcessOneSample(); 133 if (result == FoundSampleForNextCodeEvent) { 134 // All ticks of the current last_processed_code_event_id_ are 135 // processed, proceed to the next code event. 136 ProcessCodeEvent(); 137 } 138 now = base::TimeTicks::HighResolutionNow(); 139 } while (result != NoSamplesInQueue && now < nextSampleTime); 140 141 if (nextSampleTime > now) { 142#if V8_OS_WIN 143 // Do not use Sleep on Windows as it is very imprecise. 144 // Could be up to 16ms jitter, which is unacceptable for the purpose. 145 while (base::TimeTicks::HighResolutionNow() < nextSampleTime) { 146 } 147#else 148 base::OS::Sleep(nextSampleTime - now); 149#endif 150 } 151 152 // Schedule next sample. sampler_ is NULL in tests. 153 if (sampler_) sampler_->DoSample(); 154 } 155 156 // Process remaining tick events. 157 do { 158 SampleProcessingResult result; 159 do { 160 result = ProcessOneSample(); 161 } while (result == OneSampleProcessed); 162 } while (ProcessCodeEvent()); 163} 164 165 166void* ProfilerEventsProcessor::operator new(size_t size) { 167 return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor)); 168} 169 170 171void ProfilerEventsProcessor::operator delete(void* ptr) { 172 AlignedFree(ptr); 173} 174 175 176int CpuProfiler::GetProfilesCount() { 177 // The count of profiles doesn't depend on a security token. 178 return profiles_->profiles()->length(); 179} 180 181 182CpuProfile* CpuProfiler::GetProfile(int index) { 183 return profiles_->profiles()->at(index); 184} 185 186 187void CpuProfiler::DeleteAllProfiles() { 188 if (is_profiling_) StopProcessor(); 189 ResetProfiles(); 190} 191 192 193void CpuProfiler::DeleteProfile(CpuProfile* profile) { 194 profiles_->RemoveProfile(profile); 195 delete profile; 196 if (profiles_->profiles()->is_empty() && !is_profiling_) { 197 // If this was the last profile, clean up all accessory data as well. 198 ResetProfiles(); 199 } 200} 201 202void CpuProfiler::CodeEventHandler(const CodeEventsContainer& evt_rec) { 203 switch (evt_rec.generic.type) { 204 case CodeEventRecord::CODE_CREATION: 205 case CodeEventRecord::CODE_MOVE: 206 case CodeEventRecord::CODE_DISABLE_OPT: 207 processor_->Enqueue(evt_rec); 208 break; 209 case CodeEventRecord::CODE_DEOPT: { 210 const CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_; 211 Address pc = reinterpret_cast<Address>(rec->pc); 212 int fp_to_sp_delta = rec->fp_to_sp_delta; 213 processor_->Enqueue(evt_rec); 214 processor_->AddDeoptStack(isolate_, pc, fp_to_sp_delta); 215 break; 216 } 217 default: 218 UNREACHABLE(); 219 } 220} 221 222CpuProfiler::CpuProfiler(Isolate* isolate) 223 : isolate_(isolate), 224 sampling_interval_(base::TimeDelta::FromMicroseconds( 225 FLAG_cpu_profiler_sampling_interval)), 226 profiles_(new CpuProfilesCollection(isolate)), 227 is_profiling_(false) { 228 profiles_->set_cpu_profiler(this); 229} 230 231CpuProfiler::CpuProfiler(Isolate* isolate, CpuProfilesCollection* test_profiles, 232 ProfileGenerator* test_generator, 233 ProfilerEventsProcessor* test_processor) 234 : isolate_(isolate), 235 sampling_interval_(base::TimeDelta::FromMicroseconds( 236 FLAG_cpu_profiler_sampling_interval)), 237 profiles_(test_profiles), 238 generator_(test_generator), 239 processor_(test_processor), 240 is_profiling_(false) { 241 profiles_->set_cpu_profiler(this); 242} 243 244CpuProfiler::~CpuProfiler() { 245 DCHECK(!is_profiling_); 246} 247 248void CpuProfiler::set_sampling_interval(base::TimeDelta value) { 249 DCHECK(!is_profiling_); 250 sampling_interval_ = value; 251} 252 253void CpuProfiler::ResetProfiles() { 254 profiles_.reset(new CpuProfilesCollection(isolate_)); 255 profiles_->set_cpu_profiler(this); 256} 257 258void CpuProfiler::CollectSample() { 259 if (processor_) { 260 processor_->AddCurrentStack(isolate_); 261 } 262} 263 264void CpuProfiler::StartProfiling(const char* title, bool record_samples) { 265 if (profiles_->StartProfiling(title, record_samples)) { 266 StartProcessorIfNotStarted(); 267 } 268} 269 270 271void CpuProfiler::StartProfiling(String* title, bool record_samples) { 272 StartProfiling(profiles_->GetName(title), record_samples); 273 isolate_->debug()->feature_tracker()->Track(DebugFeatureTracker::kProfiler); 274} 275 276 277void CpuProfiler::StartProcessorIfNotStarted() { 278 if (processor_) { 279 processor_->AddCurrentStack(isolate_); 280 return; 281 } 282 Logger* logger = isolate_->logger(); 283 // Disable logging when using the new implementation. 284 saved_is_logging_ = logger->is_logging_; 285 logger->is_logging_ = false; 286 sampler::Sampler* sampler = logger->sampler(); 287 generator_.reset(new ProfileGenerator(profiles_.get())); 288 processor_.reset(new ProfilerEventsProcessor(generator_.get(), sampler, 289 sampling_interval_)); 290 logger->SetUpProfilerListener(); 291 ProfilerListener* profiler_listener = logger->profiler_listener(); 292 profiler_listener->AddObserver(this); 293 is_profiling_ = true; 294 isolate_->set_is_profiling(true); 295 // Enumerate stuff we already have in the heap. 296 DCHECK(isolate_->heap()->HasBeenSetUp()); 297 if (!FLAG_prof_browser_mode) { 298 logger->LogCodeObjects(); 299 } 300 logger->LogCompiledFunctions(); 301 logger->LogAccessorCallbacks(); 302 LogBuiltins(); 303 // Enable stack sampling. 304 sampler->SetHasProcessingThread(true); 305 sampler->IncreaseProfilingDepth(); 306 processor_->AddCurrentStack(isolate_); 307 processor_->StartSynchronously(); 308} 309 310 311CpuProfile* CpuProfiler::StopProfiling(const char* title) { 312 if (!is_profiling_) return nullptr; 313 StopProcessorIfLastProfile(title); 314 CpuProfile* result = profiles_->StopProfiling(title); 315 if (result) { 316 result->Print(); 317 } 318 return result; 319} 320 321 322CpuProfile* CpuProfiler::StopProfiling(String* title) { 323 if (!is_profiling_) return nullptr; 324 const char* profile_title = profiles_->GetName(title); 325 StopProcessorIfLastProfile(profile_title); 326 return profiles_->StopProfiling(profile_title); 327} 328 329 330void CpuProfiler::StopProcessorIfLastProfile(const char* title) { 331 if (profiles_->IsLastProfile(title)) { 332 StopProcessor(); 333 } 334} 335 336 337void CpuProfiler::StopProcessor() { 338 Logger* logger = isolate_->logger(); 339 sampler::Sampler* sampler = 340 reinterpret_cast<sampler::Sampler*>(logger->ticker_); 341 is_profiling_ = false; 342 isolate_->set_is_profiling(false); 343 ProfilerListener* profiler_listener = logger->profiler_listener(); 344 profiler_listener->RemoveObserver(this); 345 processor_->StopSynchronously(); 346 logger->TearDownProfilerListener(); 347 processor_.reset(); 348 generator_.reset(); 349 sampler->SetHasProcessingThread(false); 350 sampler->DecreaseProfilingDepth(); 351 logger->is_logging_ = saved_is_logging_; 352} 353 354 355void CpuProfiler::LogBuiltins() { 356 Builtins* builtins = isolate_->builtins(); 357 DCHECK(builtins->is_initialized()); 358 for (int i = 0; i < Builtins::builtin_count; i++) { 359 CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN); 360 ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_; 361 Builtins::Name id = static_cast<Builtins::Name>(i); 362 rec->start = builtins->builtin(id)->address(); 363 rec->builtin_id = id; 364 processor_->Enqueue(evt_rec); 365 } 366} 367 368} // namespace internal 369} // namespace v8 370