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     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