LCOV - code coverage report
Current view: top level - src/profiler - profiler-listener.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 117 134 87.3 %
Date: 2019-04-17 Functions: 14 17 82.4 %

          Line data    Source code
       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/handles-inl.h"
       9             : #include "src/objects-inl.h"
      10             : #include "src/objects/code-inl.h"
      11             : #include "src/objects/script-inl.h"
      12             : #include "src/objects/shared-function-info-inl.h"
      13             : #include "src/objects/string-inl.h"
      14             : #include "src/profiler/cpu-profiler.h"
      15             : #include "src/profiler/profile-generator-inl.h"
      16             : #include "src/reloc-info.h"
      17             : #include "src/source-position-table.h"
      18             : #include "src/wasm/wasm-code-manager.h"
      19             : 
      20             : namespace v8 {
      21             : namespace internal {
      22             : 
      23         787 : ProfilerListener::ProfilerListener(Isolate* isolate,
      24             :                                    CodeEventObserver* observer)
      25         787 :     : isolate_(isolate), observer_(observer) {}
      26             : 
      27             : ProfilerListener::~ProfilerListener() = default;
      28             : 
      29       15660 : void ProfilerListener::CallbackEvent(Name name, Address entry_point) {
      30             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
      31             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
      32       15660 :   rec->instruction_start = entry_point;
      33       31320 :   rec->entry = new CodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name));
      34       15660 :   rec->instruction_size = 1;
      35             :   DispatchCodeEvent(evt_rec);
      36       15660 : }
      37             : 
      38       26208 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
      39             :                                        AbstractCode code, const char* name) {
      40             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
      41             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
      42       26208 :   rec->instruction_start = code->InstructionStart();
      43             :   rec->entry = new CodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName,
      44             :                              CpuProfileNode::kNoLineNumberInfo,
      45             :                              CpuProfileNode::kNoColumnNumberInfo, nullptr,
      46       52416 :                              code->InstructionStart());
      47       26208 :   rec->instruction_size = code->InstructionSize();
      48             :   DispatchCodeEvent(evt_rec);
      49       26208 : }
      50             : 
      51           9 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
      52             :                                        AbstractCode code, Name name) {
      53             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
      54             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
      55           9 :   rec->instruction_start = code->InstructionStart();
      56             :   rec->entry = new CodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName,
      57             :                              CpuProfileNode::kNoLineNumberInfo,
      58             :                              CpuProfileNode::kNoColumnNumberInfo, nullptr,
      59          18 :                              code->InstructionStart());
      60           9 :   rec->instruction_size = code->InstructionSize();
      61             :   DispatchCodeEvent(evt_rec);
      62           9 : }
      63             : 
      64           0 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
      65             :                                        AbstractCode code,
      66             :                                        SharedFunctionInfo shared,
      67             :                                        Name script_name) {
      68             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
      69             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
      70           0 :   rec->instruction_start = code->InstructionStart();
      71           0 :   rec->entry = new CodeEntry(tag, GetName(shared->DebugName()),
      72           0 :                              GetName(InferScriptName(script_name, shared)),
      73             :                              CpuProfileNode::kNoLineNumberInfo,
      74             :                              CpuProfileNode::kNoColumnNumberInfo, nullptr,
      75           0 :                              code->InstructionStart());
      76             :   DCHECK(!code->IsCode());
      77           0 :   rec->entry->FillFunctionInfo(shared);
      78           0 :   rec->instruction_size = code->InstructionSize();
      79             :   DispatchCodeEvent(evt_rec);
      80           0 : }
      81             : 
      82             : namespace {
      83             : 
      84         152 : CodeEntry* GetOrInsertCachedEntry(
      85             :     std::unordered_set<std::unique_ptr<CodeEntry>, CodeEntry::Hasher,
      86             :                        CodeEntry::Equals>* entries,
      87             :     std::unique_ptr<CodeEntry> search_value) {
      88             :   auto it = entries->find(search_value);
      89         265 :   if (it != entries->end()) return it->get();
      90             :   CodeEntry* ret = search_value.get();
      91             :   entries->insert(std::move(search_value));
      92          39 :   return ret;
      93             : }
      94             : 
      95             : }  // namespace
      96             : 
      97      603827 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
      98             :                                        AbstractCode abstract_code,
      99             :                                        SharedFunctionInfo shared,
     100             :                                        Name script_name, int line, int column) {
     101             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     102             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     103      603827 :   rec->instruction_start = abstract_code->InstructionStart();
     104             :   std::unique_ptr<SourcePositionTable> line_table;
     105             :   std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> inline_stacks;
     106             :   std::unordered_set<std::unique_ptr<CodeEntry>, CodeEntry::Hasher,
     107             :                      CodeEntry::Equals>
     108             :       cached_inline_entries;
     109             :   bool is_shared_cross_origin = false;
     110     1207654 :   if (shared->script()->IsScript()) {
     111      603827 :     Script script = Script::cast(shared->script());
     112      603827 :     line_table.reset(new SourcePositionTable());
     113      603827 :     HandleScope scope(isolate_);
     114             : 
     115             :     is_shared_cross_origin = script.origin_options().IsSharedCrossOrigin();
     116             : 
     117             :     // Add each position to the source position table and store inlining stacks
     118             :     // for inline positions. We store almost the same information in the
     119             :     // profiler as is stored on the code object, except that we transform source
     120             :     // positions to line numbers here, because we only care about attributing
     121             :     // ticks to a given line.
     122     3317616 :     for (SourcePositionTableIterator it(abstract_code->source_position_table());
     123     2713789 :          !it.done(); it.Advance()) {
     124             :       int position = it.source_position().ScriptOffset();
     125     2713789 :       int line_number = script->GetLineNumber(position) + 1;
     126     2713789 :       int inlining_id = it.source_position().InliningId();
     127     2713789 :       line_table->SetPosition(it.code_offset(), line_number, inlining_id);
     128             : 
     129     2713789 :       if (inlining_id != SourcePosition::kNotInlined) {
     130             :         DCHECK(abstract_code->IsCode());
     131          54 :         Code code = abstract_code->GetCode();
     132             :         std::vector<SourcePositionInfo> stack =
     133         108 :             it.source_position().InliningStack(handle(code, isolate_));
     134             :         DCHECK(!stack.empty());
     135             : 
     136             :         std::vector<CodeEntryAndLineNumber> inline_stack;
     137         206 :         for (SourcePositionInfo& pos_info : stack) {
     138         152 :           if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
     139         152 :           if (pos_info.script.is_null()) continue;
     140             : 
     141             :           int line_number =
     142         304 :               pos_info.script->GetLineNumber(pos_info.position.ScriptOffset()) +
     143         152 :               1;
     144             : 
     145             :           const char* resource_name =
     146             :               (pos_info.script->name()->IsName())
     147             :                   ? GetName(Name::cast(pos_info.script->name()))
     148         227 :                   : CodeEntry::kEmptyResourceName;
     149             : 
     150             :           bool inline_is_shared_cross_origin =
     151         152 :               pos_info.script->origin_options().IsSharedCrossOrigin();
     152             : 
     153             :           // We need the start line number and column number of the function for
     154             :           // kLeafNodeLineNumbers mode. Creating a SourcePositionInfo is a handy
     155             :           // way of getting both easily.
     156             :           SourcePositionInfo start_pos_info(
     157             :               SourcePosition(pos_info.shared->StartPosition()),
     158         304 :               pos_info.shared);
     159             : 
     160             :           std::unique_ptr<CodeEntry> inline_entry =
     161             :               base::make_unique<CodeEntry>(
     162         304 :                   tag, GetName(pos_info.shared->DebugName()), resource_name,
     163         304 :                   start_pos_info.line + 1, start_pos_info.column + 1, nullptr,
     164         912 :                   code->InstructionStart(), inline_is_shared_cross_origin);
     165         152 :           inline_entry->FillFunctionInfo(*pos_info.shared);
     166             : 
     167             :           // Create a canonical CodeEntry for each inlined frame and then re-use
     168             :           // them for subsequent inline stacks to avoid a lot of duplication.
     169         152 :           CodeEntry* cached_entry = GetOrInsertCachedEntry(
     170         152 :               &cached_inline_entries, std::move(inline_entry));
     171             : 
     172         304 :           inline_stack.push_back(
     173             :               CodeEntryAndLineNumber{cached_entry, line_number});
     174             :         }
     175             :         DCHECK(!inline_stack.empty());
     176             :         inline_stacks.emplace(inlining_id, std::move(inline_stack));
     177             :       }
     178             :     }
     179             :   }
     180             :   rec->entry =
     181     1207654 :       new CodeEntry(tag, GetName(shared->DebugName()),
     182      603827 :                     GetName(InferScriptName(script_name, shared)), line, column,
     183             :                     std::move(line_table), abstract_code->InstructionStart(),
     184     1207654 :                     is_shared_cross_origin);
     185      603827 :   if (!inline_stacks.empty()) {
     186          39 :     rec->entry->SetInlineStacks(std::move(cached_inline_entries),
     187          13 :                                 std::move(inline_stacks));
     188             :   }
     189             : 
     190      603827 :   rec->entry->FillFunctionInfo(shared);
     191      603827 :   rec->instruction_size = abstract_code->InstructionSize();
     192             :   DispatchCodeEvent(evt_rec);
     193      603827 : }
     194             : 
     195          11 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
     196             :                                        const wasm::WasmCode* code,
     197             :                                        wasm::WasmName name) {
     198             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     199             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     200          11 :   rec->instruction_start = code->instruction_start();
     201             :   rec->entry = new CodeEntry(
     202             :       tag, GetName(name.start()), CodeEntry::kWasmResourceNamePrefix,
     203             :       CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
     204          22 :       nullptr, code->instruction_start(), true);
     205          11 :   rec->instruction_size = code->instructions().length();
     206             :   DispatchCodeEvent(evt_rec);
     207          11 : }
     208             : 
     209           5 : void ProfilerListener::CodeMoveEvent(AbstractCode from, AbstractCode to) {
     210             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
     211             :   CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
     212           5 :   rec->from_instruction_start = from->InstructionStart();
     213           5 :   rec->to_instruction_start = to->InstructionStart();
     214             :   DispatchCodeEvent(evt_rec);
     215           5 : }
     216             : 
     217           0 : void ProfilerListener::CodeDisableOptEvent(AbstractCode code,
     218             :                                            SharedFunctionInfo shared) {
     219             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
     220             :   CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
     221           0 :   rec->instruction_start = code->InstructionStart();
     222           0 :   rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
     223             :   DispatchCodeEvent(evt_rec);
     224           0 : }
     225             : 
     226           6 : void ProfilerListener::CodeDeoptEvent(Code code, DeoptimizeKind kind,
     227             :                                       Address pc, int fp_to_sp_delta) {
     228             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
     229             :   CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
     230           6 :   Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
     231           6 :   rec->instruction_start = code->InstructionStart();
     232           6 :   rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
     233           6 :   rec->deopt_id = info.deopt_id;
     234           6 :   rec->pc = pc;
     235           6 :   rec->fp_to_sp_delta = fp_to_sp_delta;
     236             : 
     237             :   // When a function is deoptimized, we store the deoptimized frame information
     238             :   // for the use of GetDeoptInfos().
     239           6 :   AttachDeoptInlinedFrames(code, rec);
     240             :   DispatchCodeEvent(evt_rec);
     241           6 : }
     242             : 
     243        9113 : void ProfilerListener::GetterCallbackEvent(Name name, Address entry_point) {
     244             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     245             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     246        9113 :   rec->instruction_start = entry_point;
     247             :   rec->entry =
     248       18226 :       new CodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("get ", name));
     249        9113 :   rec->instruction_size = 1;
     250             :   DispatchCodeEvent(evt_rec);
     251        9113 : }
     252             : 
     253           0 : void ProfilerListener::RegExpCodeCreateEvent(AbstractCode code, String source) {
     254             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     255             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     256           0 :   rec->instruction_start = code->InstructionStart();
     257             :   rec->entry = new CodeEntry(
     258             :       CodeEventListener::REG_EXP_TAG, GetConsName("RegExp: ", source),
     259             :       CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
     260           0 :       CpuProfileNode::kNoColumnNumberInfo, nullptr, code->InstructionStart());
     261           0 :   rec->instruction_size = code->InstructionSize();
     262             :   DispatchCodeEvent(evt_rec);
     263           0 : }
     264             : 
     265        9113 : void ProfilerListener::SetterCallbackEvent(Name name, Address entry_point) {
     266             :   CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
     267             :   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
     268        9113 :   rec->instruction_start = entry_point;
     269             :   rec->entry =
     270       18226 :       new CodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("set ", name));
     271        9113 :   rec->instruction_size = 1;
     272             :   DispatchCodeEvent(evt_rec);
     273        9113 : }
     274             : 
     275      603827 : Name ProfilerListener::InferScriptName(Name name, SharedFunctionInfo info) {
     276     1207654 :   if (name->IsString() && String::cast(name)->length()) return name;
     277     1206432 :   if (!info->script()->IsScript()) return name;
     278      603216 :   Object source_url = Script::cast(info->script())->source_url();
     279      603216 :   return source_url->IsName() ? Name::cast(source_url) : name;
     280             : }
     281             : 
     282           6 : void ProfilerListener::AttachDeoptInlinedFrames(Code code,
     283             :                                                 CodeDeoptEventRecord* rec) {
     284           6 :   int deopt_id = rec->deopt_id;
     285           6 :   SourcePosition last_position = SourcePosition::Unknown();
     286             :   int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
     287             :              RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
     288             :              RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
     289             : 
     290           6 :   rec->deopt_frames = nullptr;
     291           6 :   rec->deopt_frame_count = 0;
     292             : 
     293           9 :   for (RelocIterator it(code, mask); !it.done(); it.next()) {
     294             :     RelocInfo* info = it.rinfo();
     295           6 :     if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
     296           3 :       int script_offset = static_cast<int>(info->data());
     297           3 :       it.next();
     298             :       DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
     299           3 :       int inlining_id = static_cast<int>(it.rinfo()->data());
     300           3 :       last_position = SourcePosition(script_offset, inlining_id);
     301           3 :       continue;
     302             :     }
     303           3 :     if (info->rmode() == RelocInfo::DEOPT_ID) {
     304           3 :       if (deopt_id != static_cast<int>(info->data())) continue;
     305             :       DCHECK(last_position.IsKnown());
     306             : 
     307             :       // SourcePosition::InliningStack allocates a handle for the SFI of each
     308             :       // frame. These don't escape this function, but quickly add up. This
     309             :       // scope limits their lifetime.
     310           3 :       HandleScope scope(isolate_);
     311             :       std::vector<SourcePositionInfo> stack =
     312           6 :           last_position.InliningStack(handle(code, isolate_));
     313             :       CpuProfileDeoptFrame* deopt_frames =
     314           3 :           new CpuProfileDeoptFrame[stack.size()];
     315             : 
     316             :       int deopt_frame_count = 0;
     317          12 :       for (SourcePositionInfo& pos_info : stack) {
     318           9 :         if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
     319           9 :         if (pos_info.script.is_null()) continue;
     320             :         int script_id = pos_info.script->id();
     321           9 :         size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
     322           9 :         deopt_frames[deopt_frame_count++] = {script_id, offset};
     323             :       }
     324           3 :       rec->deopt_frames = deopt_frames;
     325           3 :       rec->deopt_frame_count = deopt_frame_count;
     326             :       break;
     327             :     }
     328             :   }
     329           6 : }
     330             : 
     331             : }  // namespace internal
     332      122004 : }  // namespace v8

Generated by: LCOV version 1.10