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/v8.h" 6 7 #include "src/cpu-profiler-inl.h" 8 9 #include "src/compiler.h" 10 #include "src/frames-inl.h" 11 #include "src/hashmap.h" 12 #include "src/log-inl.h" 13 #include "src/vm-state-inl.h" 14 15 #include "include/v8-profiler.h" 16 17 namespace v8 { 18 namespace internal { 19 20 static const int kProfilerStackSize = 64 * KB; 21 22 23 ProfilerEventsProcessor::ProfilerEventsProcessor( 24 ProfileGenerator* generator, 25 Sampler* sampler, 26 base::TimeDelta period) 27 : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)), 28 generator_(generator), 29 sampler_(sampler), 30 running_(true), 31 period_(period), 32 last_code_event_id_(0), last_processed_code_event_id_(0) { 33 } 34 35 36 void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) { 37 event.generic.order = ++last_code_event_id_; 38 events_buffer_.Enqueue(event); 39 } 40 41 42 void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) { 43 TickSampleEventRecord record(last_code_event_id_); 44 RegisterState regs; 45 StackFrameIterator it(isolate); 46 if (!it.done()) { 47 StackFrame* frame = it.frame(); 48 regs.sp = frame->sp(); 49 regs.fp = frame->fp(); 50 regs.pc = frame->pc(); 51 } 52 record.sample.Init(isolate, regs); 53 ticks_from_vm_buffer_.Enqueue(record); 54 } 55 56 57 void ProfilerEventsProcessor::StopSynchronously() { 58 if (!running_) return; 59 running_ = false; 60 Join(); 61 } 62 63 64 bool ProfilerEventsProcessor::ProcessCodeEvent() { 65 CodeEventsContainer record; 66 if (events_buffer_.Dequeue(&record)) { 67 switch (record.generic.type) { 68 #define PROFILER_TYPE_CASE(type, clss) \ 69 case CodeEventRecord::type: \ 70 record.clss##_.UpdateCodeMap(generator_->code_map()); \ 71 break; 72 73 CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE) 74 75 #undef PROFILER_TYPE_CASE 76 default: return true; // Skip record. 77 } 78 last_processed_code_event_id_ = record.generic.order; 79 return true; 80 } 81 return false; 82 } 83 84 ProfilerEventsProcessor::SampleProcessingResult 85 ProfilerEventsProcessor::ProcessOneSample() { 86 if (!ticks_from_vm_buffer_.IsEmpty() 87 && ticks_from_vm_buffer_.Peek()->order == 88 last_processed_code_event_id_) { 89 TickSampleEventRecord record; 90 ticks_from_vm_buffer_.Dequeue(&record); 91 generator_->RecordTickSample(record.sample); 92 return OneSampleProcessed; 93 } 94 95 const TickSampleEventRecord* record = ticks_buffer_.Peek(); 96 if (record == NULL) { 97 if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue; 98 return FoundSampleForNextCodeEvent; 99 } 100 if (record->order != last_processed_code_event_id_) { 101 return FoundSampleForNextCodeEvent; 102 } 103 generator_->RecordTickSample(record->sample); 104 ticks_buffer_.Remove(); 105 return OneSampleProcessed; 106 } 107 108 109 void ProfilerEventsProcessor::Run() { 110 while (running_) { 111 base::ElapsedTimer timer; 112 timer.Start(); 113 // Keep processing existing events until we need to do next sample. 114 do { 115 if (FoundSampleForNextCodeEvent == ProcessOneSample()) { 116 // All ticks of the current last_processed_code_event_id_ are 117 // processed, proceed to the next code event. 118 ProcessCodeEvent(); 119 } 120 } while (!timer.HasExpired(period_)); 121 122 // Schedule next sample. sampler_ is NULL in tests. 123 if (sampler_) sampler_->DoSample(); 124 } 125 126 // Process remaining tick events. 127 do { 128 SampleProcessingResult result; 129 do { 130 result = ProcessOneSample(); 131 } while (result == OneSampleProcessed); 132 } while (ProcessCodeEvent()); 133 } 134 135 136 void* ProfilerEventsProcessor::operator new(size_t size) { 137 return AlignedAlloc(size, V8_ALIGNOF(ProfilerEventsProcessor)); 138 } 139 140 141 void ProfilerEventsProcessor::operator delete(void* ptr) { 142 AlignedFree(ptr); 143 } 144 145 146 int CpuProfiler::GetProfilesCount() { 147 // The count of profiles doesn't depend on a security token. 148 return profiles_->profiles()->length(); 149 } 150 151 152 CpuProfile* CpuProfiler::GetProfile(int index) { 153 return profiles_->profiles()->at(index); 154 } 155 156 157 void CpuProfiler::DeleteAllProfiles() { 158 if (is_profiling_) StopProcessor(); 159 ResetProfiles(); 160 } 161 162 163 void CpuProfiler::DeleteProfile(CpuProfile* profile) { 164 profiles_->RemoveProfile(profile); 165 delete profile; 166 if (profiles_->profiles()->is_empty() && !is_profiling_) { 167 // If this was the last profile, clean up all accessory data as well. 168 ResetProfiles(); 169 } 170 } 171 172 173 static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag) { 174 return FLAG_prof_browser_mode 175 && (tag != Logger::CALLBACK_TAG 176 && tag != Logger::FUNCTION_TAG 177 && tag != Logger::LAZY_COMPILE_TAG 178 && tag != Logger::REG_EXP_TAG 179 && tag != Logger::SCRIPT_TAG); 180 } 181 182 183 void CpuProfiler::CallbackEvent(Name* name, Address entry_point) { 184 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return; 185 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 186 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 187 rec->start = entry_point; 188 rec->entry = profiles_->NewCodeEntry( 189 Logger::CALLBACK_TAG, 190 profiles_->GetName(name)); 191 rec->size = 1; 192 rec->shared = NULL; 193 processor_->Enqueue(evt_rec); 194 } 195 196 197 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 198 Code* code, 199 const char* name) { 200 if (FilterOutCodeCreateEvent(tag)) return; 201 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 202 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 203 rec->start = code->address(); 204 rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name)); 205 rec->size = code->ExecutableSize(); 206 rec->shared = NULL; 207 processor_->Enqueue(evt_rec); 208 } 209 210 211 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 212 Code* code, 213 Name* name) { 214 if (FilterOutCodeCreateEvent(tag)) return; 215 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 216 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 217 rec->start = code->address(); 218 rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name)); 219 rec->size = code->ExecutableSize(); 220 rec->shared = NULL; 221 processor_->Enqueue(evt_rec); 222 } 223 224 225 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code, 226 SharedFunctionInfo* shared, 227 CompilationInfo* info, Name* script_name) { 228 if (FilterOutCodeCreateEvent(tag)) return; 229 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 230 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 231 rec->start = code->address(); 232 rec->entry = profiles_->NewCodeEntry( 233 tag, profiles_->GetFunctionName(shared->DebugName()), 234 CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name)); 235 if (info) { 236 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges()); 237 } 238 if (shared->script()->IsScript()) { 239 DCHECK(Script::cast(shared->script())); 240 Script* script = Script::cast(shared->script()); 241 rec->entry->set_script_id(script->id()->value()); 242 rec->entry->set_bailout_reason( 243 GetBailoutReason(shared->DisableOptimizationReason())); 244 } 245 rec->size = code->ExecutableSize(); 246 rec->shared = shared->address(); 247 processor_->Enqueue(evt_rec); 248 } 249 250 251 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, Code* code, 252 SharedFunctionInfo* shared, 253 CompilationInfo* info, Name* script_name, 254 int line, int column) { 255 if (FilterOutCodeCreateEvent(tag)) return; 256 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 257 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 258 rec->start = code->address(); 259 rec->entry = profiles_->NewCodeEntry( 260 tag, profiles_->GetFunctionName(shared->DebugName()), 261 CodeEntry::kEmptyNamePrefix, profiles_->GetName(script_name), line, 262 column); 263 if (info) { 264 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges()); 265 } 266 DCHECK(Script::cast(shared->script())); 267 Script* script = Script::cast(shared->script()); 268 rec->entry->set_script_id(script->id()->value()); 269 rec->size = code->ExecutableSize(); 270 rec->shared = shared->address(); 271 rec->entry->set_bailout_reason( 272 GetBailoutReason(shared->DisableOptimizationReason())); 273 processor_->Enqueue(evt_rec); 274 } 275 276 277 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 278 Code* code, 279 int args_count) { 280 if (FilterOutCodeCreateEvent(tag)) return; 281 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 282 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 283 rec->start = code->address(); 284 rec->entry = profiles_->NewCodeEntry( 285 tag, 286 profiles_->GetName(args_count), 287 "args_count: "); 288 rec->size = code->ExecutableSize(); 289 rec->shared = NULL; 290 processor_->Enqueue(evt_rec); 291 } 292 293 294 void CpuProfiler::CodeMoveEvent(Address from, Address to) { 295 CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE); 296 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_; 297 rec->from = from; 298 rec->to = to; 299 processor_->Enqueue(evt_rec); 300 } 301 302 303 void CpuProfiler::CodeDisableOptEvent(Code* code, SharedFunctionInfo* shared) { 304 CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT); 305 CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_; 306 rec->start = code->address(); 307 rec->bailout_reason = GetBailoutReason(shared->DisableOptimizationReason()); 308 processor_->Enqueue(evt_rec); 309 } 310 311 312 void CpuProfiler::CodeDeleteEvent(Address from) { 313 } 314 315 316 void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) { 317 CodeEventsContainer evt_rec(CodeEventRecord::SHARED_FUNC_MOVE); 318 SharedFunctionInfoMoveEventRecord* rec = 319 &evt_rec.SharedFunctionInfoMoveEventRecord_; 320 rec->from = from; 321 rec->to = to; 322 processor_->Enqueue(evt_rec); 323 } 324 325 326 void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) { 327 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return; 328 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 329 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 330 rec->start = entry_point; 331 rec->entry = profiles_->NewCodeEntry( 332 Logger::CALLBACK_TAG, 333 profiles_->GetName(name), 334 "get "); 335 rec->size = 1; 336 rec->shared = NULL; 337 processor_->Enqueue(evt_rec); 338 } 339 340 341 void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) { 342 if (FilterOutCodeCreateEvent(Logger::REG_EXP_TAG)) return; 343 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 344 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 345 rec->start = code->address(); 346 rec->entry = profiles_->NewCodeEntry( 347 Logger::REG_EXP_TAG, 348 profiles_->GetName(source), 349 "RegExp: "); 350 rec->size = code->ExecutableSize(); 351 processor_->Enqueue(evt_rec); 352 } 353 354 355 void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) { 356 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return; 357 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 358 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 359 rec->start = entry_point; 360 rec->entry = profiles_->NewCodeEntry( 361 Logger::CALLBACK_TAG, 362 profiles_->GetName(name), 363 "set "); 364 rec->size = 1; 365 rec->shared = NULL; 366 processor_->Enqueue(evt_rec); 367 } 368 369 370 CpuProfiler::CpuProfiler(Isolate* isolate) 371 : isolate_(isolate), 372 sampling_interval_(base::TimeDelta::FromMicroseconds( 373 FLAG_cpu_profiler_sampling_interval)), 374 profiles_(new CpuProfilesCollection(isolate->heap())), 375 generator_(NULL), 376 processor_(NULL), 377 is_profiling_(false) { 378 } 379 380 381 CpuProfiler::CpuProfiler(Isolate* isolate, 382 CpuProfilesCollection* test_profiles, 383 ProfileGenerator* test_generator, 384 ProfilerEventsProcessor* test_processor) 385 : isolate_(isolate), 386 sampling_interval_(base::TimeDelta::FromMicroseconds( 387 FLAG_cpu_profiler_sampling_interval)), 388 profiles_(test_profiles), 389 generator_(test_generator), 390 processor_(test_processor), 391 is_profiling_(false) { 392 } 393 394 395 CpuProfiler::~CpuProfiler() { 396 DCHECK(!is_profiling_); 397 delete profiles_; 398 } 399 400 401 void CpuProfiler::set_sampling_interval(base::TimeDelta value) { 402 DCHECK(!is_profiling_); 403 sampling_interval_ = value; 404 } 405 406 407 void CpuProfiler::ResetProfiles() { 408 delete profiles_; 409 profiles_ = new CpuProfilesCollection(isolate()->heap()); 410 } 411 412 413 void CpuProfiler::StartProfiling(const char* title, bool record_samples) { 414 if (profiles_->StartProfiling(title, record_samples)) { 415 StartProcessorIfNotStarted(); 416 } 417 } 418 419 420 void CpuProfiler::StartProfiling(String* title, bool record_samples) { 421 StartProfiling(profiles_->GetName(title), record_samples); 422 } 423 424 425 void CpuProfiler::StartProcessorIfNotStarted() { 426 if (processor_ != NULL) { 427 processor_->AddCurrentStack(isolate_); 428 return; 429 } 430 Logger* logger = isolate_->logger(); 431 // Disable logging when using the new implementation. 432 saved_is_logging_ = logger->is_logging_; 433 logger->is_logging_ = false; 434 generator_ = new ProfileGenerator(profiles_); 435 Sampler* sampler = logger->sampler(); 436 processor_ = new ProfilerEventsProcessor( 437 generator_, sampler, sampling_interval_); 438 is_profiling_ = true; 439 // Enumerate stuff we already have in the heap. 440 DCHECK(isolate_->heap()->HasBeenSetUp()); 441 if (!FLAG_prof_browser_mode) { 442 logger->LogCodeObjects(); 443 } 444 logger->LogCompiledFunctions(); 445 logger->LogAccessorCallbacks(); 446 LogBuiltins(); 447 // Enable stack sampling. 448 sampler->SetHasProcessingThread(true); 449 sampler->IncreaseProfilingDepth(); 450 processor_->AddCurrentStack(isolate_); 451 processor_->StartSynchronously(); 452 } 453 454 455 CpuProfile* CpuProfiler::StopProfiling(const char* title) { 456 if (!is_profiling_) return NULL; 457 StopProcessorIfLastProfile(title); 458 CpuProfile* result = profiles_->StopProfiling(title); 459 if (result != NULL) { 460 result->Print(); 461 } 462 return result; 463 } 464 465 466 CpuProfile* CpuProfiler::StopProfiling(String* title) { 467 if (!is_profiling_) return NULL; 468 const char* profile_title = profiles_->GetName(title); 469 StopProcessorIfLastProfile(profile_title); 470 return profiles_->StopProfiling(profile_title); 471 } 472 473 474 void CpuProfiler::StopProcessorIfLastProfile(const char* title) { 475 if (profiles_->IsLastProfile(title)) StopProcessor(); 476 } 477 478 479 void CpuProfiler::StopProcessor() { 480 Logger* logger = isolate_->logger(); 481 Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_); 482 is_profiling_ = false; 483 processor_->StopSynchronously(); 484 delete processor_; 485 delete generator_; 486 processor_ = NULL; 487 generator_ = NULL; 488 sampler->SetHasProcessingThread(false); 489 sampler->DecreaseProfilingDepth(); 490 logger->is_logging_ = saved_is_logging_; 491 } 492 493 494 void CpuProfiler::LogBuiltins() { 495 Builtins* builtins = isolate_->builtins(); 496 DCHECK(builtins->is_initialized()); 497 for (int i = 0; i < Builtins::builtin_count; i++) { 498 CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN); 499 ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_; 500 Builtins::Name id = static_cast<Builtins::Name>(i); 501 rec->start = builtins->builtin(id)->address(); 502 rec->builtin_id = id; 503 processor_->Enqueue(evt_rec); 504 } 505 } 506 507 508 } } // namespace v8::internal 509