1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "v8.h" 29 30 #include "cpu-profiler-inl.h" 31 32 #include "compiler.h" 33 #include "frames-inl.h" 34 #include "hashmap.h" 35 #include "log-inl.h" 36 #include "vm-state-inl.h" 37 38 #include "../include/v8-profiler.h" 39 40 namespace v8 { 41 namespace internal { 42 43 static const int kProfilerStackSize = 64 * KB; 44 45 46 ProfilerEventsProcessor::ProfilerEventsProcessor( 47 ProfileGenerator* generator, 48 Sampler* sampler, 49 TimeDelta period) 50 : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)), 51 generator_(generator), 52 sampler_(sampler), 53 running_(true), 54 period_(period), 55 last_code_event_id_(0), last_processed_code_event_id_(0) { 56 } 57 58 59 void ProfilerEventsProcessor::Enqueue(const CodeEventsContainer& event) { 60 event.generic.order = ++last_code_event_id_; 61 events_buffer_.Enqueue(event); 62 } 63 64 65 void ProfilerEventsProcessor::AddCurrentStack(Isolate* isolate) { 66 TickSampleEventRecord record(last_code_event_id_); 67 RegisterState regs; 68 StackFrameIterator it(isolate); 69 if (!it.done()) { 70 StackFrame* frame = it.frame(); 71 regs.sp = frame->sp(); 72 regs.fp = frame->fp(); 73 regs.pc = frame->pc(); 74 } 75 record.sample.Init(isolate, regs); 76 ticks_from_vm_buffer_.Enqueue(record); 77 } 78 79 80 void ProfilerEventsProcessor::StopSynchronously() { 81 if (!running_) return; 82 running_ = false; 83 Join(); 84 } 85 86 87 bool ProfilerEventsProcessor::ProcessCodeEvent() { 88 CodeEventsContainer record; 89 if (events_buffer_.Dequeue(&record)) { 90 switch (record.generic.type) { 91 #define PROFILER_TYPE_CASE(type, clss) \ 92 case CodeEventRecord::type: \ 93 record.clss##_.UpdateCodeMap(generator_->code_map()); \ 94 break; 95 96 CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE) 97 98 #undef PROFILER_TYPE_CASE 99 default: return true; // Skip record. 100 } 101 last_processed_code_event_id_ = record.generic.order; 102 return true; 103 } 104 return false; 105 } 106 107 ProfilerEventsProcessor::SampleProcessingResult 108 ProfilerEventsProcessor::ProcessOneSample() { 109 if (!ticks_from_vm_buffer_.IsEmpty() 110 && ticks_from_vm_buffer_.Peek()->order == 111 last_processed_code_event_id_) { 112 TickSampleEventRecord record; 113 ticks_from_vm_buffer_.Dequeue(&record); 114 generator_->RecordTickSample(record.sample); 115 return OneSampleProcessed; 116 } 117 118 const TickSampleEventRecord* record = ticks_buffer_.Peek(); 119 if (record == NULL) { 120 if (ticks_from_vm_buffer_.IsEmpty()) return NoSamplesInQueue; 121 return FoundSampleForNextCodeEvent; 122 } 123 if (record->order != last_processed_code_event_id_) { 124 return FoundSampleForNextCodeEvent; 125 } 126 generator_->RecordTickSample(record->sample); 127 ticks_buffer_.Remove(); 128 return OneSampleProcessed; 129 } 130 131 132 void ProfilerEventsProcessor::Run() { 133 while (running_) { 134 ElapsedTimer timer; 135 timer.Start(); 136 // Keep processing existing events until we need to do next sample. 137 do { 138 if (FoundSampleForNextCodeEvent == ProcessOneSample()) { 139 // All ticks of the current last_processed_code_event_id_ are 140 // processed, proceed to the next code event. 141 ProcessCodeEvent(); 142 } 143 } while (!timer.HasExpired(period_)); 144 145 // Schedule next sample. sampler_ is NULL in tests. 146 if (sampler_) sampler_->DoSample(); 147 } 148 149 // Process remaining tick events. 150 do { 151 SampleProcessingResult result; 152 do { 153 result = ProcessOneSample(); 154 } while (result == OneSampleProcessed); 155 } while (ProcessCodeEvent()); 156 } 157 158 159 int CpuProfiler::GetProfilesCount() { 160 // The count of profiles doesn't depend on a security token. 161 return profiles_->profiles()->length(); 162 } 163 164 165 CpuProfile* CpuProfiler::GetProfile(int index) { 166 return profiles_->profiles()->at(index); 167 } 168 169 170 void CpuProfiler::DeleteAllProfiles() { 171 if (is_profiling_) StopProcessor(); 172 ResetProfiles(); 173 } 174 175 176 void CpuProfiler::DeleteProfile(CpuProfile* profile) { 177 profiles_->RemoveProfile(profile); 178 delete profile; 179 } 180 181 182 static bool FilterOutCodeCreateEvent(Logger::LogEventsAndTags tag) { 183 return FLAG_prof_browser_mode 184 && (tag != Logger::CALLBACK_TAG 185 && tag != Logger::FUNCTION_TAG 186 && tag != Logger::LAZY_COMPILE_TAG 187 && tag != Logger::REG_EXP_TAG 188 && tag != Logger::SCRIPT_TAG); 189 } 190 191 192 void CpuProfiler::CallbackEvent(Name* name, Address entry_point) { 193 if (FilterOutCodeCreateEvent(Logger::CALLBACK_TAG)) return; 194 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 195 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 196 rec->start = entry_point; 197 rec->entry = profiles_->NewCodeEntry( 198 Logger::CALLBACK_TAG, 199 profiles_->GetName(name)); 200 rec->size = 1; 201 rec->shared = NULL; 202 processor_->Enqueue(evt_rec); 203 } 204 205 206 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 207 Code* code, 208 const char* name) { 209 if (FilterOutCodeCreateEvent(tag)) return; 210 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 211 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 212 rec->start = code->address(); 213 rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name)); 214 rec->size = code->ExecutableSize(); 215 rec->shared = NULL; 216 processor_->Enqueue(evt_rec); 217 } 218 219 220 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 221 Code* code, 222 Name* name) { 223 if (FilterOutCodeCreateEvent(tag)) return; 224 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 225 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 226 rec->start = code->address(); 227 rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name)); 228 rec->size = code->ExecutableSize(); 229 rec->shared = NULL; 230 processor_->Enqueue(evt_rec); 231 } 232 233 234 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 235 Code* code, 236 SharedFunctionInfo* shared, 237 CompilationInfo* info, 238 Name* name) { 239 if (FilterOutCodeCreateEvent(tag)) return; 240 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 241 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 242 rec->start = code->address(); 243 rec->entry = profiles_->NewCodeEntry(tag, profiles_->GetFunctionName(name)); 244 if (info) { 245 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges()); 246 } 247 if (shared->script()->IsScript()) { 248 ASSERT(Script::cast(shared->script())); 249 Script* script = Script::cast(shared->script()); 250 rec->entry->set_script_id(script->id()->value()); 251 rec->entry->set_bailout_reason( 252 GetBailoutReason(shared->DisableOptimizationReason())); 253 } 254 rec->size = code->ExecutableSize(); 255 rec->shared = shared->address(); 256 processor_->Enqueue(evt_rec); 257 } 258 259 260 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 261 Code* code, 262 SharedFunctionInfo* shared, 263 CompilationInfo* info, 264 Name* source, int line, int column) { 265 if (FilterOutCodeCreateEvent(tag)) return; 266 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 267 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 268 rec->start = code->address(); 269 rec->entry = profiles_->NewCodeEntry( 270 tag, 271 profiles_->GetFunctionName(shared->DebugName()), 272 CodeEntry::kEmptyNamePrefix, 273 profiles_->GetName(source), 274 line, 275 column); 276 if (info) { 277 rec->entry->set_no_frame_ranges(info->ReleaseNoFrameRanges()); 278 } 279 ASSERT(Script::cast(shared->script())); 280 Script* script = Script::cast(shared->script()); 281 rec->entry->set_script_id(script->id()->value()); 282 rec->size = code->ExecutableSize(); 283 rec->shared = shared->address(); 284 rec->entry->set_bailout_reason( 285 GetBailoutReason(shared->DisableOptimizationReason())); 286 processor_->Enqueue(evt_rec); 287 } 288 289 290 void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 291 Code* code, 292 int args_count) { 293 if (FilterOutCodeCreateEvent(tag)) return; 294 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 295 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 296 rec->start = code->address(); 297 rec->entry = profiles_->NewCodeEntry( 298 tag, 299 profiles_->GetName(args_count), 300 "args_count: "); 301 rec->size = code->ExecutableSize(); 302 rec->shared = NULL; 303 processor_->Enqueue(evt_rec); 304 } 305 306 307 void CpuProfiler::CodeMoveEvent(Address from, Address to) { 308 CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE); 309 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_; 310 rec->from = from; 311 rec->to = to; 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 next_profile_uid_(1), 380 generator_(NULL), 381 processor_(NULL), 382 is_profiling_(false) { 383 } 384 385 386 CpuProfiler::CpuProfiler(Isolate* isolate, 387 CpuProfilesCollection* test_profiles, 388 ProfileGenerator* test_generator, 389 ProfilerEventsProcessor* test_processor) 390 : isolate_(isolate), 391 sampling_interval_(TimeDelta::FromMicroseconds( 392 FLAG_cpu_profiler_sampling_interval)), 393 profiles_(test_profiles), 394 next_profile_uid_(1), 395 generator_(test_generator), 396 processor_(test_processor), 397 is_profiling_(false) { 398 } 399 400 401 CpuProfiler::~CpuProfiler() { 402 ASSERT(!is_profiling_); 403 delete profiles_; 404 } 405 406 407 void CpuProfiler::set_sampling_interval(TimeDelta value) { 408 ASSERT(!is_profiling_); 409 sampling_interval_ = value; 410 } 411 412 413 void CpuProfiler::ResetProfiles() { 414 delete profiles_; 415 profiles_ = new CpuProfilesCollection(isolate()->heap()); 416 } 417 418 419 void CpuProfiler::StartProfiling(const char* title, bool record_samples) { 420 if (profiles_->StartProfiling(title, next_profile_uid_++, record_samples)) { 421 StartProcessorIfNotStarted(); 422 } 423 processor_->AddCurrentStack(isolate_); 424 } 425 426 427 void CpuProfiler::StartProfiling(String* title, bool record_samples) { 428 StartProfiling(profiles_->GetName(title), record_samples); 429 } 430 431 432 void CpuProfiler::StartProcessorIfNotStarted() { 433 if (processor_ == NULL) { 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 #if V8_CC_MSVC && (_MSC_VER >= 1800) 441 // VS2013 reports "warning C4316: 'v8::internal::ProfilerEventsProcessor' 442 // : object allocated on the heap may not be aligned 64". We need to 443 // figure out if this is a legitimate warning or a compiler bug. 444 #pragma warning(push) 445 #pragma warning(disable:4316) 446 #endif 447 processor_ = new ProfilerEventsProcessor( 448 generator_, sampler, sampling_interval_); 449 #if V8_CC_MSVC && (_MSC_VER >= 1800) 450 #pragma warning(pop) 451 #endif 452 is_profiling_ = true; 453 // Enumerate stuff we already have in the heap. 454 ASSERT(isolate_->heap()->HasBeenSetUp()); 455 if (!FLAG_prof_browser_mode) { 456 logger->LogCodeObjects(); 457 } 458 logger->LogCompiledFunctions(); 459 logger->LogAccessorCallbacks(); 460 LogBuiltins(); 461 // Enable stack sampling. 462 sampler->SetHasProcessingThread(true); 463 sampler->IncreaseProfilingDepth(); 464 processor_->StartSynchronously(); 465 } 466 } 467 468 469 CpuProfile* CpuProfiler::StopProfiling(const char* title) { 470 if (!is_profiling_) return NULL; 471 StopProcessorIfLastProfile(title); 472 CpuProfile* result = profiles_->StopProfiling(title); 473 if (result != NULL) { 474 result->Print(); 475 } 476 return result; 477 } 478 479 480 CpuProfile* CpuProfiler::StopProfiling(String* title) { 481 if (!is_profiling_) return NULL; 482 const char* profile_title = profiles_->GetName(title); 483 StopProcessorIfLastProfile(profile_title); 484 return profiles_->StopProfiling(profile_title); 485 } 486 487 488 void CpuProfiler::StopProcessorIfLastProfile(const char* title) { 489 if (profiles_->IsLastProfile(title)) StopProcessor(); 490 } 491 492 493 void CpuProfiler::StopProcessor() { 494 Logger* logger = isolate_->logger(); 495 Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_); 496 is_profiling_ = false; 497 processor_->StopSynchronously(); 498 delete processor_; 499 delete generator_; 500 processor_ = NULL; 501 generator_ = NULL; 502 sampler->SetHasProcessingThread(false); 503 sampler->DecreaseProfilingDepth(); 504 logger->is_logging_ = saved_is_logging_; 505 } 506 507 508 void CpuProfiler::LogBuiltins() { 509 Builtins* builtins = isolate_->builtins(); 510 ASSERT(builtins->is_initialized()); 511 for (int i = 0; i < Builtins::builtin_count; i++) { 512 CodeEventsContainer evt_rec(CodeEventRecord::REPORT_BUILTIN); 513 ReportBuiltinEventRecord* rec = &evt_rec.ReportBuiltinEventRecord_; 514 Builtins::Name id = static_cast<Builtins::Name>(i); 515 rec->start = builtins->builtin(id)->address(); 516 rec->builtin_id = id; 517 processor_->Enqueue(evt_rec); 518 } 519 } 520 521 522 } } // namespace v8::internal 523