Home | History | Annotate | Download | only in profiler
      1 // Copyright 2016 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/profiler/profiler-listener.h"
      6 
      7 #include "src/deoptimizer.h"
      8 #include "src/objects-inl.h"
      9 #include "src/profiler/cpu-profiler.h"
     10 #include "src/profiler/profile-generator-inl.h"
     11 #include "src/source-position-table.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 ProfilerListener::ProfilerListener(Isolate* isolate)
     17     : function_and_resource_names_(isolate->heap()) {}
     18 
     19 ProfilerListener::~ProfilerListener() {
     20   for (auto code_entry : code_entries_) {
     21     delete code_entry;
     22   }
     23 }
     24 
     25 void ProfilerListener::CallbackEvent(Name* name, Address entry_point) {
     26   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     27   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     28   rec->start = entry_point;
     29   rec->entry = NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name));
     30   rec->size = 1;
     31   DispatchCodeEvent(evt_rec);
     32 }
     33 
     34 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
     35                                        AbstractCode* code, const char* name) {
     36   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     37   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     38   rec->start = code->address();
     39   rec->entry = NewCodeEntry(
     40       tag, GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
     41       CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
     42       CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
     43   RecordInliningInfo(rec->entry, code);
     44   rec->size = code->ExecutableSize();
     45   DispatchCodeEvent(evt_rec);
     46 }
     47 
     48 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
     49                                        AbstractCode* code, Name* name) {
     50   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     51   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     52   rec->start = code->address();
     53   rec->entry = NewCodeEntry(
     54       tag, GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
     55       CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
     56       CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
     57   RecordInliningInfo(rec->entry, code);
     58   rec->size = code->ExecutableSize();
     59   DispatchCodeEvent(evt_rec);
     60 }
     61 
     62 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
     63                                        AbstractCode* code,
     64                                        SharedFunctionInfo* shared,
     65                                        Name* script_name) {
     66   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     67   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     68   rec->start = code->address();
     69   rec->entry = NewCodeEntry(
     70       tag, GetFunctionName(shared->DebugName()), CodeEntry::kEmptyNamePrefix,
     71       GetName(InferScriptName(script_name, shared)),
     72       CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
     73       NULL, code->instruction_start());
     74   RecordInliningInfo(rec->entry, code);
     75   rec->entry->FillFunctionInfo(shared);
     76   rec->size = code->ExecutableSize();
     77   DispatchCodeEvent(evt_rec);
     78 }
     79 
     80 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
     81                                        AbstractCode* abstract_code,
     82                                        SharedFunctionInfo* shared,
     83                                        Name* script_name, int line,
     84                                        int column) {
     85   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     86   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     87   rec->start = abstract_code->address();
     88   JITLineInfoTable* line_table = NULL;
     89   if (shared->script()->IsScript()) {
     90     Script* script = Script::cast(shared->script());
     91     line_table = new JITLineInfoTable();
     92     int offset = abstract_code->IsCode() ? Code::kHeaderSize
     93                                          : BytecodeArray::kHeaderSize;
     94     for (SourcePositionTableIterator it(abstract_code->source_position_table());
     95          !it.done(); it.Advance()) {
     96       // TODO(alph,tebbi) Skipping inlined positions for now, because they might
     97       // refer to a different script.
     98       if (it.source_position().InliningId() != SourcePosition::kNotInlined)
     99         continue;
    100       int position = it.source_position().ScriptOffset();
    101       int line_number = script->GetLineNumber(position) + 1;
    102       int pc_offset = it.code_offset() + offset;
    103       line_table->SetPosition(pc_offset, line_number);
    104     }
    105   }
    106   rec->entry = NewCodeEntry(
    107       tag, GetFunctionName(shared->DebugName()), CodeEntry::kEmptyNamePrefix,
    108       GetName(InferScriptName(script_name, shared)), line, column, line_table,
    109       abstract_code->instruction_start());
    110   RecordInliningInfo(rec->entry, abstract_code);
    111   RecordDeoptInlinedFrames(rec->entry, abstract_code);
    112   rec->entry->FillFunctionInfo(shared);
    113   rec->size = abstract_code->ExecutableSize();
    114   DispatchCodeEvent(evt_rec);
    115 }
    116 
    117 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
    118                                        AbstractCode* code, int args_count) {
    119   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
    120   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
    121   rec->start = code->address();
    122   rec->entry = NewCodeEntry(
    123       tag, GetName(args_count), "args_count: ", CodeEntry::kEmptyResourceName,
    124       CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
    125       NULL, code->instruction_start());
    126   RecordInliningInfo(rec->entry, code);
    127   rec->size = code->ExecutableSize();
    128   DispatchCodeEvent(evt_rec);
    129 }
    130 
    131 void ProfilerListener::CodeMoveEvent(AbstractCode* from, Address to) {
    132   CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
    133   CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
    134   rec->from = from->address();
    135   rec->to = to;
    136   DispatchCodeEvent(evt_rec);
    137 }
    138 
    139 void ProfilerListener::CodeDisableOptEvent(AbstractCode* code,
    140                                            SharedFunctionInfo* shared) {
    141   CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
    142   CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
    143   rec->start = code->address();
    144   rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
    145   DispatchCodeEvent(evt_rec);
    146 }
    147 
    148 void ProfilerListener::CodeDeoptEvent(Code* code, Address pc,
    149                                       int fp_to_sp_delta) {
    150   CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
    151   CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
    152   Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
    153   rec->start = code->address();
    154   rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
    155   rec->deopt_id = info.deopt_id;
    156   rec->pc = reinterpret_cast<void*>(pc);
    157   rec->fp_to_sp_delta = fp_to_sp_delta;
    158   DispatchCodeEvent(evt_rec);
    159 }
    160 
    161 void ProfilerListener::GetterCallbackEvent(Name* name, Address entry_point) {
    162   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
    163   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
    164   rec->start = entry_point;
    165   rec->entry =
    166       NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name), "get ");
    167   rec->size = 1;
    168   DispatchCodeEvent(evt_rec);
    169 }
    170 
    171 void ProfilerListener::RegExpCodeCreateEvent(AbstractCode* code,
    172                                              String* source) {
    173   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
    174   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
    175   rec->start = code->address();
    176   rec->entry = NewCodeEntry(
    177       CodeEventListener::REG_EXP_TAG, GetName(source), "RegExp: ",
    178       CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
    179       CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
    180   rec->size = code->ExecutableSize();
    181   DispatchCodeEvent(evt_rec);
    182 }
    183 
    184 void ProfilerListener::SetterCallbackEvent(Name* name, Address entry_point) {
    185   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
    186   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
    187   rec->start = entry_point;
    188   rec->entry =
    189       NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name), "set ");
    190   rec->size = 1;
    191   DispatchCodeEvent(evt_rec);
    192 }
    193 
    194 Name* ProfilerListener::InferScriptName(Name* name, SharedFunctionInfo* info) {
    195   if (name->IsString() && String::cast(name)->length()) return name;
    196   if (!info->script()->IsScript()) return name;
    197   Object* source_url = Script::cast(info->script())->source_url();
    198   return source_url->IsName() ? Name::cast(source_url) : name;
    199 }
    200 
    201 void ProfilerListener::RecordInliningInfo(CodeEntry* entry,
    202                                           AbstractCode* abstract_code) {
    203   if (!abstract_code->IsCode()) return;
    204   Code* code = abstract_code->GetCode();
    205   if (code->kind() != Code::OPTIMIZED_FUNCTION) return;
    206   DeoptimizationInputData* deopt_input_data =
    207       DeoptimizationInputData::cast(code->deoptimization_data());
    208   int deopt_count = deopt_input_data->DeoptCount();
    209   for (int i = 0; i < deopt_count; i++) {
    210     int pc_offset = deopt_input_data->Pc(i)->value();
    211     if (pc_offset == -1) continue;
    212     int translation_index = deopt_input_data->TranslationIndex(i)->value();
    213     TranslationIterator it(deopt_input_data->TranslationByteArray(),
    214                            translation_index);
    215     Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
    216     DCHECK_EQ(Translation::BEGIN, opcode);
    217     it.Skip(Translation::NumberOfOperandsFor(opcode));
    218     int depth = 0;
    219     std::vector<CodeEntry*> inline_stack;
    220     while (it.HasNext() &&
    221            Translation::BEGIN !=
    222                (opcode = static_cast<Translation::Opcode>(it.Next()))) {
    223       if (opcode != Translation::JS_FRAME &&
    224           opcode != Translation::INTERPRETED_FRAME) {
    225         it.Skip(Translation::NumberOfOperandsFor(opcode));
    226         continue;
    227       }
    228       it.Next();  // Skip ast_id
    229       int shared_info_id = it.Next();
    230       it.Next();  // Skip height
    231       SharedFunctionInfo* shared_info = SharedFunctionInfo::cast(
    232           deopt_input_data->LiteralArray()->get(shared_info_id));
    233       if (!depth++) continue;  // Skip the current function itself.
    234       CodeEntry* inline_entry = new CodeEntry(
    235           entry->tag(), GetFunctionName(shared_info->DebugName()),
    236           CodeEntry::kEmptyNamePrefix, entry->resource_name(),
    237           CpuProfileNode::kNoLineNumberInfo,
    238           CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
    239       inline_entry->FillFunctionInfo(shared_info);
    240       inline_stack.push_back(inline_entry);
    241     }
    242     if (!inline_stack.empty()) {
    243       entry->AddInlineStack(pc_offset, std::move(inline_stack));
    244     }
    245   }
    246 }
    247 
    248 void ProfilerListener::RecordDeoptInlinedFrames(CodeEntry* entry,
    249                                                 AbstractCode* abstract_code) {
    250   if (abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION) return;
    251   Handle<Code> code(abstract_code->GetCode());
    252 
    253   SourcePosition last_position = SourcePosition::Unknown();
    254   int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
    255              RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
    256              RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
    257   for (RelocIterator it(*code, mask); !it.done(); it.next()) {
    258     RelocInfo* info = it.rinfo();
    259     if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
    260       int script_offset = static_cast<int>(info->data());
    261       it.next();
    262       DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
    263       int inlining_id = static_cast<int>(it.rinfo()->data());
    264       last_position = SourcePosition(script_offset, inlining_id);
    265       continue;
    266     }
    267     if (info->rmode() == RelocInfo::DEOPT_ID) {
    268       int deopt_id = static_cast<int>(info->data());
    269       DCHECK(last_position.IsKnown());
    270       std::vector<CpuProfileDeoptFrame> inlined_frames;
    271       for (SourcePositionInfo& pos_info : last_position.InliningStack(code)) {
    272         DCHECK(pos_info.position.ScriptOffset() != kNoSourcePosition);
    273         if (!pos_info.function->script()->IsScript()) continue;
    274         int script_id = Script::cast(pos_info.function->script())->id();
    275         size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
    276         inlined_frames.push_back(CpuProfileDeoptFrame({script_id, offset}));
    277       }
    278       if (!inlined_frames.empty() &&
    279           !entry->HasDeoptInlinedFramesFor(deopt_id)) {
    280         entry->AddDeoptInlinedFrames(deopt_id, std::move(inlined_frames));
    281       }
    282     }
    283   }
    284 }
    285 
    286 CodeEntry* ProfilerListener::NewCodeEntry(
    287     CodeEventListener::LogEventsAndTags tag, const char* name,
    288     const char* name_prefix, const char* resource_name, int line_number,
    289     int column_number, JITLineInfoTable* line_info, Address instruction_start) {
    290   CodeEntry* code_entry =
    291       new CodeEntry(tag, name, name_prefix, resource_name, line_number,
    292                     column_number, line_info, instruction_start);
    293   code_entries_.push_back(code_entry);
    294   return code_entry;
    295 }
    296 
    297 void ProfilerListener::AddObserver(CodeEventObserver* observer) {
    298   base::LockGuard<base::Mutex> guard(&mutex_);
    299   if (std::find(observers_.begin(), observers_.end(), observer) !=
    300       observers_.end())
    301     return;
    302   observers_.push_back(observer);
    303 }
    304 
    305 void ProfilerListener::RemoveObserver(CodeEventObserver* observer) {
    306   base::LockGuard<base::Mutex> guard(&mutex_);
    307   auto it = std::find(observers_.begin(), observers_.end(), observer);
    308   if (it == observers_.end()) return;
    309   observers_.erase(it);
    310 }
    311 
    312 }  // namespace internal
    313 }  // namespace v8
    314