profiler-listener.cc 14.1 KB
Newer Older
lpy's avatar
lpy committed
1 2 3 4 5 6 7
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/profiler/profiler-listener.h"

#include "src/deoptimizer.h"
8
#include "src/objects-inl.h"
lpy's avatar
lpy committed
9 10
#include "src/profiler/cpu-profiler.h"
#include "src/profiler/profile-generator-inl.h"
11
#include "src/reloc-info.h"
12
#include "src/snapshot/embedded-data.h"
13
#include "src/source-position-table.h"
14
#include "src/wasm/wasm-code-manager.h"
lpy's avatar
lpy committed
15 16 17 18

namespace v8 {
namespace internal {

19 20
ProfilerListener::ProfilerListener(Isolate* isolate,
                                   CodeEventObserver* observer)
Yang Guo's avatar
Yang Guo committed
21
    : isolate_(isolate), observer_(observer) {}
lpy's avatar
lpy committed
22

23
ProfilerListener::~ProfilerListener() = default;
lpy's avatar
lpy committed
24

25
void ProfilerListener::CallbackEvent(Name name, Address entry_point) {
lpy's avatar
lpy committed
26 27
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
28
  rec->instruction_start = entry_point;
29
  rec->entry = new CodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name));
30
  rec->instruction_size = 1;
lpy's avatar
lpy committed
31 32 33 34
  DispatchCodeEvent(evt_rec);
}

void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
35
                                       AbstractCode code, const char* name) {
lpy's avatar
lpy committed
36 37
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
38
  rec->instruction_start = code->InstructionStart();
39 40 41 42
  rec->entry = new CodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName,
                             CpuProfileNode::kNoLineNumberInfo,
                             CpuProfileNode::kNoColumnNumberInfo, nullptr,
                             code->InstructionStart());
43
  rec->instruction_size = code->InstructionSize();
lpy's avatar
lpy committed
44 45 46 47
  DispatchCodeEvent(evt_rec);
}

void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
48
                                       AbstractCode code, Name name) {
lpy's avatar
lpy committed
49 50
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
51
  rec->instruction_start = code->InstructionStart();
52 53 54 55
  rec->entry = new CodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName,
                             CpuProfileNode::kNoLineNumberInfo,
                             CpuProfileNode::kNoColumnNumberInfo, nullptr,
                             code->InstructionStart());
56
  rec->instruction_size = code->InstructionSize();
lpy's avatar
lpy committed
57 58 59 60
  DispatchCodeEvent(evt_rec);
}

void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
61
                                       AbstractCode code,
62
                                       SharedFunctionInfo shared,
63
                                       Name script_name) {
lpy's avatar
lpy committed
64 65
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
66
  rec->instruction_start = code->InstructionStart();
67 68 69 70 71
  rec->entry = new CodeEntry(tag, GetName(shared->DebugName()),
                             GetName(InferScriptName(script_name, shared)),
                             CpuProfileNode::kNoLineNumberInfo,
                             CpuProfileNode::kNoColumnNumberInfo, nullptr,
                             code->InstructionStart());
72
  DCHECK(!code->IsCode());
lpy's avatar
lpy committed
73
  rec->entry->FillFunctionInfo(shared);
74
  rec->instruction_size = code->InstructionSize();
lpy's avatar
lpy committed
75 76 77
  DispatchCodeEvent(evt_rec);
}

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
namespace {

CodeEntry* GetOrInsertCachedEntry(
    std::unordered_set<std::unique_ptr<CodeEntry>, CodeEntry::Hasher,
                       CodeEntry::Equals>* entries,
    std::unique_ptr<CodeEntry> search_value) {
  auto it = entries->find(search_value);
  if (it != entries->end()) return it->get();
  CodeEntry* ret = search_value.get();
  entries->insert(std::move(search_value));
  return ret;
}

}  // namespace

lpy's avatar
lpy committed
93
void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
94
                                       AbstractCode abstract_code,
95
                                       SharedFunctionInfo shared,
96
                                       Name script_name, int line, int column) {
lpy's avatar
lpy committed
97 98
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
99
  rec->instruction_start = abstract_code->InstructionStart();
100
  std::unique_ptr<SourcePositionTable> line_table;
101 102 103 104
  std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> inline_stacks;
  std::unordered_set<std::unique_ptr<CodeEntry>, CodeEntry::Hasher,
                     CodeEntry::Equals>
      cached_inline_entries;
105
  bool is_shared_cross_origin = false;
106
  if (shared->script()->IsScript()) {
107
    Script script = Script::cast(shared->script());
108
    line_table.reset(new SourcePositionTable());
109 110
    HandleScope scope(isolate_);

111 112
    is_shared_cross_origin = script.origin_options().IsSharedCrossOrigin();

113 114 115 116 117
    // Add each position to the source position table and store inlining stacks
    // for inline positions. We store almost the same information in the
    // profiler as is stored on the code object, except that we transform source
    // positions to line numbers here, because we only care about attributing
    // ticks to a given line.
118 119
    for (SourcePositionTableIterator it(abstract_code->source_position_table());
         !it.done(); it.Advance()) {
120
      int position = it.source_position().ScriptOffset();
121
      int line_number = script->GetLineNumber(position) + 1;
122 123 124 125 126 127 128 129 130 131
      int inlining_id = it.source_position().InliningId();
      line_table->SetPosition(it.code_offset(), line_number, inlining_id);

      if (inlining_id != SourcePosition::kNotInlined) {
        DCHECK(abstract_code->IsCode());
        Code code = abstract_code->GetCode();
        std::vector<SourcePositionInfo> stack =
            it.source_position().InliningStack(handle(code, isolate_));
        DCHECK(!stack.empty());

132
        std::vector<CodeEntryAndLineNumber> inline_stack;
133 134 135 136 137 138 139 140 141 142 143 144 145
        for (SourcePositionInfo& pos_info : stack) {
          if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
          if (pos_info.script.is_null()) continue;

          int line_number =
              pos_info.script->GetLineNumber(pos_info.position.ScriptOffset()) +
              1;

          const char* resource_name =
              (pos_info.script->name()->IsName())
                  ? GetName(Name::cast(pos_info.script->name()))
                  : CodeEntry::kEmptyResourceName;

146 147 148
          bool inline_is_shared_cross_origin =
              pos_info.script->origin_options().IsSharedCrossOrigin();

149 150 151 152 153 154 155 156 157 158 159
          // We need the start line number and column number of the function for
          // kLeafNodeLineNumbers mode. Creating a SourcePositionInfo is a handy
          // way of getting both easily.
          SourcePositionInfo start_pos_info(
              SourcePosition(pos_info.shared->StartPosition()),
              pos_info.shared);

          std::unique_ptr<CodeEntry> inline_entry =
              base::make_unique<CodeEntry>(
                  tag, GetName(pos_info.shared->DebugName()), resource_name,
                  start_pos_info.line + 1, start_pos_info.column + 1, nullptr,
160
                  code->InstructionStart(), inline_is_shared_cross_origin);
161
          inline_entry->FillFunctionInfo(*pos_info.shared);
162 163 164 165 166 167

          // Create a canonical CodeEntry for each inlined frame and then re-use
          // them for subsequent inline stacks to avoid a lot of duplication.
          CodeEntry* cached_entry = GetOrInsertCachedEntry(
              &cached_inline_entries, std::move(inline_entry));

168
          inline_stack.push_back(
169
              CodeEntryAndLineNumber{cached_entry, line_number});
170 171 172 173
        }
        DCHECK(!inline_stack.empty());
        inline_stacks.emplace(inlining_id, std::move(inline_stack));
      }
lpy's avatar
lpy committed
174 175
    }
  }
176
  rec->entry =
177 178 179 180
      new CodeEntry(tag, GetName(shared->DebugName()),
                    GetName(InferScriptName(script_name, shared)), line, column,
                    std::move(line_table), abstract_code->InstructionStart(),
                    is_shared_cross_origin);
181
  if (!inline_stacks.empty()) {
182 183
    rec->entry->SetInlineStacks(std::move(cached_inline_entries),
                                std::move(inline_stacks));
184 185
  }

lpy's avatar
lpy committed
186
  rec->entry->FillFunctionInfo(shared);
187
  rec->instruction_size = abstract_code->InstructionSize();
lpy's avatar
lpy committed
188 189 190
  DispatchCodeEvent(evt_rec);
}

191
void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
192
                                       const wasm::WasmCode* code,
193 194 195
                                       wasm::WasmName name) {
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
196
  rec->instruction_start = code->instruction_start();
197
  rec->entry = new CodeEntry(
198
      tag, GetName(name.start()), CodeEntry::kWasmResourceNamePrefix,
199
      CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
200
      nullptr, code->instruction_start(), true);
201
  rec->instruction_size = code->instructions().length();
202 203 204
  DispatchCodeEvent(evt_rec);
}

205
void ProfilerListener::CodeMoveEvent(AbstractCode from, AbstractCode to) {
lpy's avatar
lpy committed
206 207
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
  CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
208 209
  rec->from_instruction_start = from->InstructionStart();
  rec->to_instruction_start = to->InstructionStart();
lpy's avatar
lpy committed
210 211 212
  DispatchCodeEvent(evt_rec);
}

213
void ProfilerListener::CodeDisableOptEvent(AbstractCode code,
214
                                           SharedFunctionInfo shared) {
lpy's avatar
lpy committed
215 216
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
  CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
217
  rec->instruction_start = code->InstructionStart();
lpy's avatar
lpy committed
218 219 220 221
  rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
  DispatchCodeEvent(evt_rec);
}

222
void ProfilerListener::CodeDeoptEvent(Code code, DeoptimizeKind kind,
223
                                      Address pc, int fp_to_sp_delta) {
lpy's avatar
lpy committed
224 225 226
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
  CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
  Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
227
  rec->instruction_start = code->InstructionStart();
228
  rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
lpy's avatar
lpy committed
229
  rec->deopt_id = info.deopt_id;
230
  rec->pc = pc;
lpy's avatar
lpy committed
231
  rec->fp_to_sp_delta = fp_to_sp_delta;
232 233 234 235

  // When a function is deoptimized, we store the deoptimized frame information
  // for the use of GetDeoptInfos().
  AttachDeoptInlinedFrames(code, rec);
lpy's avatar
lpy committed
236 237 238
  DispatchCodeEvent(evt_rec);
}

239
void ProfilerListener::GetterCallbackEvent(Name name, Address entry_point) {
lpy's avatar
lpy committed
240 241
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
242
  rec->instruction_start = entry_point;
lpy's avatar
lpy committed
243
  rec->entry =
244
      new CodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("get ", name));
245
  rec->instruction_size = 1;
lpy's avatar
lpy committed
246 247 248
  DispatchCodeEvent(evt_rec);
}

249
void ProfilerListener::RegExpCodeCreateEvent(AbstractCode code, String source) {
lpy's avatar
lpy committed
250 251
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
252
  rec->instruction_start = code->InstructionStart();
253
  rec->entry = new CodeEntry(
254 255
      CodeEventListener::REG_EXP_TAG, GetConsName("RegExp: ", source),
      CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
256 257
      CpuProfileNode::kNoColumnNumberInfo, nullptr, code->InstructionStart());
  rec->instruction_size = code->InstructionSize();
lpy's avatar
lpy committed
258 259 260
  DispatchCodeEvent(evt_rec);
}

261
void ProfilerListener::SetterCallbackEvent(Name name, Address entry_point) {
lpy's avatar
lpy committed
262 263
  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
264
  rec->instruction_start = entry_point;
lpy's avatar
lpy committed
265
  rec->entry =
266
      new CodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("set ", name));
267
  rec->instruction_size = 1;
lpy's avatar
lpy committed
268 269 270
  DispatchCodeEvent(evt_rec);
}

271
Name ProfilerListener::InferScriptName(Name name, SharedFunctionInfo info) {
lpy's avatar
lpy committed
272 273
  if (name->IsString() && String::cast(name)->length()) return name;
  if (!info->script()->IsScript()) return name;
274
  Object source_url = Script::cast(info->script())->source_url();
lpy's avatar
lpy committed
275 276 277
  return source_url->IsName() ? Name::cast(source_url) : name;
}

278
void ProfilerListener::AttachDeoptInlinedFrames(Code code,
279 280
                                                CodeDeoptEventRecord* rec) {
  int deopt_id = rec->deopt_id;
281 282 283 284
  SourcePosition last_position = SourcePosition::Unknown();
  int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
             RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
             RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
285 286 287 288 289

  rec->deopt_frames = nullptr;
  rec->deopt_frame_count = 0;

  for (RelocIterator it(code, mask); !it.done(); it.next()) {
290 291 292 293 294 295 296 297 298 299
    RelocInfo* info = it.rinfo();
    if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
      int script_offset = static_cast<int>(info->data());
      it.next();
      DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
      int inlining_id = static_cast<int>(it.rinfo()->data());
      last_position = SourcePosition(script_offset, inlining_id);
      continue;
    }
    if (info->rmode() == RelocInfo::DEOPT_ID) {
300
      if (deopt_id != static_cast<int>(info->data())) continue;
301
      DCHECK(last_position.IsKnown());
302 303 304 305 306

      // SourcePosition::InliningStack allocates a handle for the SFI of each
      // frame. These don't escape this function, but quickly add up. This
      // scope limits their lifetime.
      HandleScope scope(isolate_);
307
      std::vector<SourcePositionInfo> stack =
308
          last_position.InliningStack(handle(code, isolate_));
309 310 311 312 313
      CpuProfileDeoptFrame* deopt_frames =
          new CpuProfileDeoptFrame[stack.size()];

      int deopt_frame_count = 0;
      for (SourcePositionInfo& pos_info : stack) {
314
        if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
315 316
        if (pos_info.script.is_null()) continue;
        int script_id = pos_info.script->id();
317
        size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
318
        deopt_frames[deopt_frame_count++] = {script_id, offset};
lpy's avatar
lpy committed
319
      }
320 321 322
      rec->deopt_frames = deopt_frames;
      rec->deopt_frame_count = deopt_frame_count;
      break;
lpy's avatar
lpy committed
323 324 325 326 327 328
    }
  }
}

}  // namespace internal
}  // namespace v8