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