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 "frames-inl.h" 33#include "hashmap.h" 34#include "log-inl.h" 35#include "vm-state-inl.h" 36 37#include "../include/v8-profiler.h" 38 39namespace v8 { 40namespace internal { 41 42static const int kEventsBufferSize = 256 * KB; 43static const int kTickSamplesBufferChunkSize = 64 * KB; 44static const int kTickSamplesBufferChunksCount = 16; 45static const int kProfilerStackSize = 64 * KB; 46 47 48ProfilerEventsProcessor::ProfilerEventsProcessor(ProfileGenerator* generator) 49 : Thread(Thread::Options("v8:ProfEvntProc", kProfilerStackSize)), 50 generator_(generator), 51 running_(true), 52 ticks_buffer_(sizeof(TickSampleEventRecord), 53 kTickSamplesBufferChunkSize, 54 kTickSamplesBufferChunksCount), 55 enqueue_order_(0) { 56} 57 58 59void ProfilerEventsProcessor::CallbackCreateEvent(Logger::LogEventsAndTags tag, 60 const char* prefix, 61 String* name, 62 Address start) { 63 if (FilterOutCodeCreateEvent(tag)) return; 64 CodeEventsContainer evt_rec; 65 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 66 rec->type = CodeEventRecord::CODE_CREATION; 67 rec->order = ++enqueue_order_; 68 rec->start = start; 69 rec->entry = generator_->NewCodeEntry(tag, prefix, name); 70 rec->size = 1; 71 rec->shared = NULL; 72 events_buffer_.Enqueue(evt_rec); 73} 74 75 76void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag, 77 String* name, 78 String* resource_name, 79 int line_number, 80 Address start, 81 unsigned size, 82 Address shared) { 83 if (FilterOutCodeCreateEvent(tag)) return; 84 CodeEventsContainer evt_rec; 85 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 86 rec->type = CodeEventRecord::CODE_CREATION; 87 rec->order = ++enqueue_order_; 88 rec->start = start; 89 rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number); 90 rec->size = size; 91 rec->shared = shared; 92 events_buffer_.Enqueue(evt_rec); 93} 94 95 96void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag, 97 const char* name, 98 Address start, 99 unsigned size) { 100 if (FilterOutCodeCreateEvent(tag)) return; 101 CodeEventsContainer evt_rec; 102 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 103 rec->type = CodeEventRecord::CODE_CREATION; 104 rec->order = ++enqueue_order_; 105 rec->start = start; 106 rec->entry = generator_->NewCodeEntry(tag, name); 107 rec->size = size; 108 rec->shared = NULL; 109 events_buffer_.Enqueue(evt_rec); 110} 111 112 113void ProfilerEventsProcessor::CodeCreateEvent(Logger::LogEventsAndTags tag, 114 int args_count, 115 Address start, 116 unsigned size) { 117 if (FilterOutCodeCreateEvent(tag)) return; 118 CodeEventsContainer evt_rec; 119 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 120 rec->type = CodeEventRecord::CODE_CREATION; 121 rec->order = ++enqueue_order_; 122 rec->start = start; 123 rec->entry = generator_->NewCodeEntry(tag, args_count); 124 rec->size = size; 125 rec->shared = NULL; 126 events_buffer_.Enqueue(evt_rec); 127} 128 129 130void ProfilerEventsProcessor::CodeMoveEvent(Address from, Address to) { 131 CodeEventsContainer evt_rec; 132 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_; 133 rec->type = CodeEventRecord::CODE_MOVE; 134 rec->order = ++enqueue_order_; 135 rec->from = from; 136 rec->to = to; 137 events_buffer_.Enqueue(evt_rec); 138} 139 140 141void ProfilerEventsProcessor::SharedFunctionInfoMoveEvent(Address from, 142 Address to) { 143 CodeEventsContainer evt_rec; 144 SharedFunctionInfoMoveEventRecord* rec = 145 &evt_rec.SharedFunctionInfoMoveEventRecord_; 146 rec->type = CodeEventRecord::SHARED_FUNC_MOVE; 147 rec->order = ++enqueue_order_; 148 rec->from = from; 149 rec->to = to; 150 events_buffer_.Enqueue(evt_rec); 151} 152 153 154void ProfilerEventsProcessor::RegExpCodeCreateEvent( 155 Logger::LogEventsAndTags tag, 156 const char* prefix, 157 String* name, 158 Address start, 159 unsigned size) { 160 if (FilterOutCodeCreateEvent(tag)) return; 161 CodeEventsContainer evt_rec; 162 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 163 rec->type = CodeEventRecord::CODE_CREATION; 164 rec->order = ++enqueue_order_; 165 rec->start = start; 166 rec->entry = generator_->NewCodeEntry(tag, prefix, name); 167 rec->size = size; 168 events_buffer_.Enqueue(evt_rec); 169} 170 171 172void ProfilerEventsProcessor::AddCurrentStack() { 173 TickSampleEventRecord record(enqueue_order_); 174 TickSample* sample = &record.sample; 175 Isolate* isolate = Isolate::Current(); 176 sample->state = isolate->current_vm_state(); 177 sample->pc = reinterpret_cast<Address>(sample); // Not NULL. 178 for (StackTraceFrameIterator it(isolate); 179 !it.done() && sample->frames_count < TickSample::kMaxFramesCount; 180 it.Advance()) { 181 sample->stack[sample->frames_count++] = it.frame()->pc(); 182 } 183 ticks_from_vm_buffer_.Enqueue(record); 184} 185 186 187bool ProfilerEventsProcessor::ProcessCodeEvent(unsigned* dequeue_order) { 188 if (!events_buffer_.IsEmpty()) { 189 CodeEventsContainer record; 190 events_buffer_.Dequeue(&record); 191 switch (record.generic.type) { 192#define PROFILER_TYPE_CASE(type, clss) \ 193 case CodeEventRecord::type: \ 194 record.clss##_.UpdateCodeMap(generator_->code_map()); \ 195 break; 196 197 CODE_EVENTS_TYPE_LIST(PROFILER_TYPE_CASE) 198 199#undef PROFILER_TYPE_CASE 200 default: return true; // Skip record. 201 } 202 *dequeue_order = record.generic.order; 203 return true; 204 } 205 return false; 206} 207 208 209bool ProfilerEventsProcessor::ProcessTicks(unsigned dequeue_order) { 210 while (true) { 211 if (!ticks_from_vm_buffer_.IsEmpty() 212 && ticks_from_vm_buffer_.Peek()->order == dequeue_order) { 213 TickSampleEventRecord record; 214 ticks_from_vm_buffer_.Dequeue(&record); 215 generator_->RecordTickSample(record.sample); 216 } 217 218 const TickSampleEventRecord* rec = 219 TickSampleEventRecord::cast(ticks_buffer_.StartDequeue()); 220 if (rec == NULL) return !ticks_from_vm_buffer_.IsEmpty(); 221 // Make a local copy of tick sample record to ensure that it won't 222 // be modified as we are processing it. This is possible as the 223 // sampler writes w/o any sync to the queue, so if the processor 224 // will get far behind, a record may be modified right under its 225 // feet. 226 TickSampleEventRecord record = *rec; 227 if (record.order == dequeue_order) { 228 // A paranoid check to make sure that we don't get a memory overrun 229 // in case of frames_count having a wild value. 230 if (record.sample.frames_count < 0 231 || record.sample.frames_count > TickSample::kMaxFramesCount) 232 record.sample.frames_count = 0; 233 generator_->RecordTickSample(record.sample); 234 ticks_buffer_.FinishDequeue(); 235 } else { 236 return true; 237 } 238 } 239} 240 241 242void ProfilerEventsProcessor::Run() { 243 unsigned dequeue_order = 0; 244 245 while (running_) { 246 // Process ticks until we have any. 247 if (ProcessTicks(dequeue_order)) { 248 // All ticks of the current dequeue_order are processed, 249 // proceed to the next code event. 250 ProcessCodeEvent(&dequeue_order); 251 } 252 YieldCPU(); 253 } 254 255 // Process remaining tick events. 256 ticks_buffer_.FlushResidualRecords(); 257 // Perform processing until we have tick events, skip remaining code events. 258 while (ProcessTicks(dequeue_order) && ProcessCodeEvent(&dequeue_order)) { } 259} 260 261 262void CpuProfiler::StartProfiling(const char* title) { 263 ASSERT(Isolate::Current()->cpu_profiler() != NULL); 264 Isolate::Current()->cpu_profiler()->StartCollectingProfile(title); 265} 266 267 268void CpuProfiler::StartProfiling(String* title) { 269 ASSERT(Isolate::Current()->cpu_profiler() != NULL); 270 Isolate::Current()->cpu_profiler()->StartCollectingProfile(title); 271} 272 273 274CpuProfile* CpuProfiler::StopProfiling(const char* title) { 275 Isolate* isolate = Isolate::Current(); 276 return is_profiling(isolate) ? 277 isolate->cpu_profiler()->StopCollectingProfile(title) : NULL; 278} 279 280 281CpuProfile* CpuProfiler::StopProfiling(Object* security_token, String* title) { 282 Isolate* isolate = Isolate::Current(); 283 return is_profiling(isolate) ? 284 isolate->cpu_profiler()->StopCollectingProfile( 285 security_token, title) : NULL; 286} 287 288 289int CpuProfiler::GetProfilesCount() { 290 ASSERT(Isolate::Current()->cpu_profiler() != NULL); 291 // The count of profiles doesn't depend on a security token. 292 return Isolate::Current()->cpu_profiler()->profiles_->Profiles( 293 TokenEnumerator::kNoSecurityToken)->length(); 294} 295 296 297CpuProfile* CpuProfiler::GetProfile(Object* security_token, int index) { 298 ASSERT(Isolate::Current()->cpu_profiler() != NULL); 299 CpuProfiler* profiler = Isolate::Current()->cpu_profiler(); 300 const int token = profiler->token_enumerator_->GetTokenId(security_token); 301 return profiler->profiles_->Profiles(token)->at(index); 302} 303 304 305CpuProfile* CpuProfiler::FindProfile(Object* security_token, unsigned uid) { 306 ASSERT(Isolate::Current()->cpu_profiler() != NULL); 307 CpuProfiler* profiler = Isolate::Current()->cpu_profiler(); 308 const int token = profiler->token_enumerator_->GetTokenId(security_token); 309 return profiler->profiles_->GetProfile(token, uid); 310} 311 312 313TickSample* CpuProfiler::TickSampleEvent(Isolate* isolate) { 314 if (CpuProfiler::is_profiling(isolate)) { 315 return isolate->cpu_profiler()->processor_->TickSampleEvent(); 316 } else { 317 return NULL; 318 } 319} 320 321 322void CpuProfiler::DeleteAllProfiles() { 323 Isolate* isolate = Isolate::Current(); 324 ASSERT(isolate->cpu_profiler() != NULL); 325 if (is_profiling(isolate)) { 326 isolate->cpu_profiler()->StopProcessor(); 327 } 328 isolate->cpu_profiler()->ResetProfiles(); 329} 330 331 332void CpuProfiler::DeleteProfile(CpuProfile* profile) { 333 ASSERT(Isolate::Current()->cpu_profiler() != NULL); 334 Isolate::Current()->cpu_profiler()->profiles_->RemoveProfile(profile); 335 delete profile; 336} 337 338 339bool CpuProfiler::HasDetachedProfiles() { 340 ASSERT(Isolate::Current()->cpu_profiler() != NULL); 341 return Isolate::Current()->cpu_profiler()->profiles_->HasDetachedProfiles(); 342} 343 344 345void CpuProfiler::CallbackEvent(String* name, Address entry_point) { 346 Isolate::Current()->cpu_profiler()->processor_->CallbackCreateEvent( 347 Logger::CALLBACK_TAG, CodeEntry::kEmptyNamePrefix, name, entry_point); 348} 349 350 351void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 352 Code* code, const char* comment) { 353 Isolate::Current()->cpu_profiler()->processor_->CodeCreateEvent( 354 tag, comment, code->address(), code->ExecutableSize()); 355} 356 357 358void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 359 Code* code, String* name) { 360 Isolate* isolate = Isolate::Current(); 361 isolate->cpu_profiler()->processor_->CodeCreateEvent( 362 tag, 363 name, 364 isolate->heap()->empty_string(), 365 v8::CpuProfileNode::kNoLineNumberInfo, 366 code->address(), 367 code->ExecutableSize(), 368 NULL); 369} 370 371 372void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 373 Code* code, 374 SharedFunctionInfo* shared, 375 String* name) { 376 Isolate* isolate = Isolate::Current(); 377 isolate->cpu_profiler()->processor_->CodeCreateEvent( 378 tag, 379 name, 380 isolate->heap()->empty_string(), 381 v8::CpuProfileNode::kNoLineNumberInfo, 382 code->address(), 383 code->ExecutableSize(), 384 shared->address()); 385} 386 387 388void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 389 Code* code, 390 SharedFunctionInfo* shared, 391 String* source, int line) { 392 Isolate::Current()->cpu_profiler()->processor_->CodeCreateEvent( 393 tag, 394 shared->DebugName(), 395 source, 396 line, 397 code->address(), 398 code->ExecutableSize(), 399 shared->address()); 400} 401 402 403void CpuProfiler::CodeCreateEvent(Logger::LogEventsAndTags tag, 404 Code* code, int args_count) { 405 Isolate::Current()->cpu_profiler()->processor_->CodeCreateEvent( 406 tag, 407 args_count, 408 code->address(), 409 code->ExecutableSize()); 410} 411 412 413void CpuProfiler::CodeMoveEvent(Address from, Address to) { 414 Isolate::Current()->cpu_profiler()->processor_->CodeMoveEvent(from, to); 415} 416 417 418void CpuProfiler::CodeDeleteEvent(Address from) { 419} 420 421 422void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) { 423 CpuProfiler* profiler = Isolate::Current()->cpu_profiler(); 424 profiler->processor_->SharedFunctionInfoMoveEvent(from, to); 425} 426 427 428void CpuProfiler::GetterCallbackEvent(String* name, Address entry_point) { 429 Isolate::Current()->cpu_profiler()->processor_->CallbackCreateEvent( 430 Logger::CALLBACK_TAG, "get ", name, entry_point); 431} 432 433 434void CpuProfiler::RegExpCodeCreateEvent(Code* code, String* source) { 435 Isolate::Current()->cpu_profiler()->processor_->RegExpCodeCreateEvent( 436 Logger::REG_EXP_TAG, 437 "RegExp: ", 438 source, 439 code->address(), 440 code->ExecutableSize()); 441} 442 443 444void CpuProfiler::SetterCallbackEvent(String* name, Address entry_point) { 445 Isolate::Current()->cpu_profiler()->processor_->CallbackCreateEvent( 446 Logger::CALLBACK_TAG, "set ", name, entry_point); 447} 448 449 450CpuProfiler::CpuProfiler() 451 : profiles_(new CpuProfilesCollection()), 452 next_profile_uid_(1), 453 token_enumerator_(new TokenEnumerator()), 454 generator_(NULL), 455 processor_(NULL), 456 need_to_stop_sampler_(false), 457 is_profiling_(false) { 458} 459 460 461CpuProfiler::~CpuProfiler() { 462 delete token_enumerator_; 463 delete profiles_; 464} 465 466 467void CpuProfiler::ResetProfiles() { 468 delete profiles_; 469 profiles_ = new CpuProfilesCollection(); 470} 471 472void CpuProfiler::StartCollectingProfile(const char* title) { 473 if (profiles_->StartProfiling(title, next_profile_uid_++)) { 474 StartProcessorIfNotStarted(); 475 } 476 processor_->AddCurrentStack(); 477} 478 479 480void CpuProfiler::StartCollectingProfile(String* title) { 481 StartCollectingProfile(profiles_->GetName(title)); 482} 483 484 485void CpuProfiler::StartProcessorIfNotStarted() { 486 if (processor_ == NULL) { 487 Isolate* isolate = Isolate::Current(); 488 489 // Disable logging when using the new implementation. 490 saved_logging_nesting_ = isolate->logger()->logging_nesting_; 491 isolate->logger()->logging_nesting_ = 0; 492 generator_ = new ProfileGenerator(profiles_); 493 processor_ = new ProfilerEventsProcessor(generator_); 494 NoBarrier_Store(&is_profiling_, true); 495 processor_->Start(); 496 // Enumerate stuff we already have in the heap. 497 if (isolate->heap()->HasBeenSetUp()) { 498 if (!FLAG_prof_browser_mode) { 499 bool saved_log_code_flag = FLAG_log_code; 500 FLAG_log_code = true; 501 isolate->logger()->LogCodeObjects(); 502 FLAG_log_code = saved_log_code_flag; 503 } 504 isolate->logger()->LogCompiledFunctions(); 505 isolate->logger()->LogAccessorCallbacks(); 506 } 507 // Enable stack sampling. 508 Sampler* sampler = reinterpret_cast<Sampler*>(isolate->logger()->ticker_); 509 if (!sampler->IsActive()) { 510 sampler->Start(); 511 need_to_stop_sampler_ = true; 512 } 513 sampler->IncreaseProfilingDepth(); 514 } 515} 516 517 518CpuProfile* CpuProfiler::StopCollectingProfile(const char* title) { 519 const double actual_sampling_rate = generator_->actual_sampling_rate(); 520 StopProcessorIfLastProfile(title); 521 CpuProfile* result = 522 profiles_->StopProfiling(TokenEnumerator::kNoSecurityToken, 523 title, 524 actual_sampling_rate); 525 if (result != NULL) { 526 result->Print(); 527 } 528 return result; 529} 530 531 532CpuProfile* CpuProfiler::StopCollectingProfile(Object* security_token, 533 String* title) { 534 const double actual_sampling_rate = generator_->actual_sampling_rate(); 535 const char* profile_title = profiles_->GetName(title); 536 StopProcessorIfLastProfile(profile_title); 537 int token = token_enumerator_->GetTokenId(security_token); 538 return profiles_->StopProfiling(token, profile_title, actual_sampling_rate); 539} 540 541 542void CpuProfiler::StopProcessorIfLastProfile(const char* title) { 543 if (profiles_->IsLastProfile(title)) StopProcessor(); 544} 545 546 547void CpuProfiler::StopProcessor() { 548 Logger* logger = Isolate::Current()->logger(); 549 Sampler* sampler = reinterpret_cast<Sampler*>(logger->ticker_); 550 sampler->DecreaseProfilingDepth(); 551 if (need_to_stop_sampler_) { 552 sampler->Stop(); 553 need_to_stop_sampler_ = false; 554 } 555 NoBarrier_Store(&is_profiling_, false); 556 processor_->Stop(); 557 processor_->Join(); 558 delete processor_; 559 delete generator_; 560 processor_ = NULL; 561 generator_ = NULL; 562 logger->logging_nesting_ = saved_logging_nesting_; 563} 564 565 566void CpuProfiler::SetUp() { 567 Isolate* isolate = Isolate::Current(); 568 if (isolate->cpu_profiler() == NULL) { 569 isolate->set_cpu_profiler(new CpuProfiler()); 570 } 571} 572 573 574void CpuProfiler::TearDown() { 575 Isolate* isolate = Isolate::Current(); 576 if (isolate->cpu_profiler() != NULL) { 577 delete isolate->cpu_profiler(); 578 } 579 isolate->set_cpu_profiler(NULL); 580} 581 582} } // namespace v8::internal 583