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 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 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, 226 Code* code, 227 SharedFunctionInfo* shared, 228 CompilationInfo* info, 229 Name* name) { 230 if (FilterOutCodeCreateEvent(tag)) return; 231 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 232 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 233 rec->start = code->address(); 234 rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name)); 235 if (info) { 236 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges()); 237 } 238 if (shared->script()->IsScript()) { 239 ASSERT(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, 252 Code* code, 253 SharedFunctionInfo* shared, 254 CompilationInfo* info, 255 Name* source, int line, int column) { 256 if (FilterOutCodeCreateEvent(tag)) return; 257 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 258 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 259 rec->start = code->address(); 260 rec->entry = profiles_->NewCodeEntry( 261 tag, 262 profiles_->GetFunctionName(shared->DebugName()), 263 CodeEntry::kEmptyNamePrefix, 264 profiles_->GetName(source), 265 line, 266 column); 267 if (info) { 268 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges()); 269 } 270 ASSERT(Script::cast(shared->script())); 271 Script* script = Script::cast(shared->script()); 272 rec->entry->set_script_id(script->id()->value()); 273 rec->size = code->ExecutableSize(); 274 rec->shared = shared->address(); 275 rec->entry->set_bailout_reason( 276 GetBailoutReason(shared->DisableOptimizationReason())); 277 processor_->Enqueue(evt_rec); 278 } 279 280 281 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 282 Code* code, 283 int args_count) { 284 if (FilterOutCodeCreateEvent(tag)) return; 285 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 286 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 287 rec->start = code->address(); 288 rec->entry = profiles_->NewCodeEntry( 289 tag, 290 profiles_->GetName(args_count), 291 "args_count: "); 292 rec->size = code->ExecutableSize(); 293 rec->shared = NULL; 294 processor_->Enqueue(evt_rec); 295 } 296 297 298 void CpuProfiler::CodeMoveEvent(Address from, Address to) { 299 CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE); 300 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_; 301 rec->from = from; 302 rec->to = to; 303 processor_->Enqueue(evt_rec); 304 } 305 306 307 void CpuProfiler::CodeDisableOptEvent(Code* code, SharedFunctionInfo* shared) { 308 CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT); 309 CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_; 310 rec->start = code->address(); 311 rec->bailout_reason = GetBailoutReason(shared->DisableOptimizationReason()); 312 processor_->Enqueue(evt_rec); 313 } 314 315 316 void CpuProfiler::CodeDeleteEvent(Address from) { 317 } 318 319 320 void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) { 321 CodeEventsContainer evt_rec(CodeEventRecord::SHARED_FUNC_MOVE); 322 SharedFunctionInfoMoveEventRecord* rec = 323 &evt_rec.SharedFunctionInfoMoveEventRecord_; 324 rec->from = from; 325 rec->to = to; 326 processor_->Enqueue(evt_rec); 327 } 328 329 330 void CpuProfiler::GetterCallbackEvent(Name* name, Address entry_point) { 331 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return; 332 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 333 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 334 rec->start = entry_point; 335 rec->entry = profiles_->NewCodeEntry( 336 Logger::CALLBACK_TAG, 337 profiles_->GetName(name), 338 "get "); 339 rec->size = 1; 340 rec->shared = NULL; 341 processor_->Enqueue(evt_rec); 342 } 343 344 345 void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) { 346 if (FilterOutCodeCreateEvent(Logger::REG_EXP_TAG)) return; 347 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 348 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 349 rec->start = code->address(); 350 rec->entry = profiles_->NewCodeEntry( 351 Logger::REG_EXP_TAG, 352 profiles_->GetName(source), 353 "RegExp: "); 354 rec->size = code->ExecutableSize(); 355 processor_->Enqueue(evt_rec); 356 } 357 358 359 void CpuProfiler::SetterCallbackEvent(Name* name, Address entry_point) { 360 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return; 361 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 362 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 363 rec->start = entry_point; 364 rec->entry = profiles_->NewCodeEntry( 365 Logger::CALLBACK_TAG, 366 profiles_->GetName(name), 367 "set "); 368 rec->size = 1; 369 rec->shared = NULL; 370 processor_->Enqueue(evt_rec); 371 } 372 373 374 CpuProfiler::CpuProfiler(Isolate* isolate) 375 : isolate_(isolate), 376 sampling_interval_(TimeDelta::FromMicroseconds( 377 FLAG_cpu_profiler_sampling_interval)), 378 profiles_(new CpuProfilesCollection(isolate->heap())), 379 generator_(NULL), 380 processor_(NULL), 381 is_profiling_(false) { 382 } 383 384 385 CpuProfiler::CpuProfiler(Isolate* isolate, 386 CpuProfilesCollection* test_profiles, 387 ProfileGenerator* test_generator, 388 ProfilerEventsProcessor* test_processor) 389 : isolate_(isolate), 390 sampling_interval_(TimeDelta::FromMicroseconds( 391 FLAG_cpu_profiler_sampling_interval)), 392 profiles_(test_profiles), 393 generator_(test_generator), 394 processor_(test_processor), 395 is_profiling_(false) { 396 } 397 398 399 CpuProfiler::~CpuProfiler() { 400 ASSERT(!is_profiling_); 401 delete profiles_; 402 } 403 404 405 void CpuProfiler::set_sampling_interval(TimeDelta value) { 406 ASSERT(!is_profiling_); 407 sampling_interval_ = value; 408 } 409 410 411 void CpuProfiler::ResetProfiles() { 412 delete profiles_; 413 profiles_ = new CpuProfilesCollection(isolate()->heap()); 414 } 415 416 417 void CpuProfiler::StartProfiling(const char* title, bool record_samples) { 418 if (profiles_->StartProfiling(title, record_samples)) { 419 StartProcessorIfNotStarted(); 420 } 421 } 422 423 424 void CpuProfiler::StartProfiling(String* title, bool record_samples) { 425 StartProfiling(profiles_->GetName(title), record_samples); 426 } 427 428 429 void CpuProfiler::StartProcessorIfNotStarted() { 430 if (processor_ != NULL) { 431 processor_->AddCurrentStack(isolate_); 432 return; 433 } 434 Logger* logger = isolate_->logger(); 435 // Disable logging when using the new implementation. 436 saved_is_logging_ = logger->is_logging_; 437 logger->is_logging_ = false; 438 generator_ = new ProfileGenerator(profiles_); 439 Sampler* sampler = logger->sampler(); 440 processor_ = new ProfilerEventsProcessor( 441 generator_, sampler, sampling_interval_); 442 is_profiling_ = true; 443 // Enumerate stuff we already have in the heap. 444 ASSERT(isolate_->heap()->HasBeenSetUp()); 445 if (!FLAG_prof_browser_mode) { 446 logger->LogCodeObjects(); 447 } 448 logger->LogCompiledFunctions(); 449 logger->LogAccessorCallbacks(); 450 LogBuiltins(); 451 // Enable stack sampling. 452 sampler->SetHasProcessingThread(true); 453 sampler->IncreaseProfilingDepth(); 454 processor_->AddCurrentStack(isolate_); 455 processor_->StartSynchronously(); 456 } 457 458 459 CpuProfile* CpuProfiler::StopProfiling(const char* title) { 460 if (!is_profiling_) return NULL; 461 StopProcessorIfLastProfile(title); 462 CpuProfile* result = profiles_->StopProfiling(title); 463 if (result != NULL) { 464 result->Print(); 465 } 466 return result; 467 } 468 469 470 CpuProfile* CpuProfiler::StopProfiling(String* title) { 471 if (!is_profiling_) return NULL; 472 const char* profile_title = profiles_->GetName(title); 473 StopProcessorIfLastProfile(profile_title); 474 return profiles_->StopProfiling(profile_title); 475 } 476 477 478 void CpuProfiler::StopProcessorIfLastProfile(const char* title) { 479 if (profiles_->IsLastProfile(title)) StopProcessor(); 480 } 481 482 483 void CpuProfiler::StopProcessor() { 484 Logger* logger = isolate_->logger(); 485 Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_); 486 is_profiling_ = false; 487 processor_->StopSynchronously(); 488 delete processor_; 489 delete generator_; 490 processor_ = NULL; 491 generator_ = NULL; 492 sampler->SetHasProcessingThread(false); 493 sampler->DecreaseProfilingDepth(); 494 logger->is_logging_ = saved_is_logging_; 495 } 496 497 498 void CpuProfiler::LogBuiltins() { 499 Builtins* builtins = isolate_->builtins(); 500 ASSERT(builtins->is_initialized()); 501 for (int i = 0; i < Builtins::builtin_count; i++) { 502 CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN); 503 ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_; 504 Builtins::Name id = static_cast<Builtins::Name>(i); 505 rec->start = builtins->builtin(id)->address(); 506 rec->builtin_id = id; 507 processor_->Enqueue(evt_rec); 508 } 509 } 510 511 512 } } // namespace v8::internal 513