Home | History | Annotate | Download | only in src
      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