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