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: 22 CpuSampler(Isolate* isolate, ProfilerEventsProcessor* processor) 23 : sampler::Sampler(reinterpret_cast<v8::Isolate*>(isolate)), 24 processor_(processor) {} 25 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 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 55 ProfilerEventsProcessor::~ProfilerEventsProcessor() { 56 sampler_->DecreaseProfilingDepth(); 57 } 58 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 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 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 94 void ProfilerEventsProcessor::StopSynchronously() { 95 if (!base::NoBarrier_AtomicExchange(&running_, 0)) return; 96 Join(); 97 } 98 99 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 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 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 188 void* ProfilerEventsProcessor::operator new(size_t size) { 189 return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor)); 190 } 191 192 193 void ProfilerEventsProcessor::operator delete(void* ptr) { 194 AlignedFree(ptr); 195 } 196 197 198 int CpuProfiler::GetProfilesCount() { 199 // The count of profiles doesn't depend on a security token. 200 return profiles_->profiles()->length(); 201 } 202 203 204 CpuProfile* CpuProfiler::GetProfile(int index) { 205 return profiles_->profiles()->at(index); 206 } 207 208 209 void CpuProfiler::DeleteAllProfiles() { 210 if (is_profiling_) StopProcessor(); 211 ResetProfiles(); 212 } 213 214 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 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 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 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 266 CpuProfiler::~CpuProfiler() { 267 DCHECK(!is_profiling_); 268 } 269 270 void CpuProfiler::set_sampling_interval(base::TimeDelta value) { 271 DCHECK(!is_profiling_); 272 sampling_interval_ = value; 273 } 274 275 void CpuProfiler::ResetProfiles() { 276 profiles_.reset(new CpuProfilesCollection(isolate_)); 277 profiles_->set_cpu_profiler(this); 278 } 279 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 295 void CpuProfiler::CollectSample() { 296 if (processor_) { 297 processor_->AddCurrentStack(isolate_); 298 } 299 } 300 301 void CpuProfiler::StartProfiling(const char* title, bool record_samples) { 302 if (profiles_->StartProfiling(title, record_samples)) { 303 StartProcessorIfNotStarted(); 304 } 305 } 306 307 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 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 345 CpuProfile* CpuProfiler::StopProfiling(const char* title) { 346 if (!is_profiling_) return nullptr; 347 StopProcessorIfLastProfile(title); 348 return profiles_->StopProfiling(title); 349 } 350 351 CpuProfile* CpuProfiler::StopProfiling(String* title) { 352 return StopProfiling(profiles_->GetName(title)); 353 } 354 355 void CpuProfiler::StopProcessorIfLastProfile(const char* title) { 356 if (!profiles_->IsLastProfile(title)) return; 357 StopProcessor(); 358 } 359 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 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