V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
profiler-listener.cc
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/snapshot/embedded-data.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, int column) {
83  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
84  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
85  rec->instruction_start = abstract_code->InstructionStart();
86  std::unique_ptr<SourcePositionTable> line_table;
87  if (shared->script()->IsScript()) {
88  Script* script = Script::cast(shared->script());
89  line_table.reset(new SourcePositionTable());
90  for (SourcePositionTableIterator it(abstract_code->source_position_table());
91  !it.done(); it.Advance()) {
92  // TODO(alph,tebbi) Skipping inlined positions for now, because they might
93  // refer to a different script.
94  if (it.source_position().InliningId() != SourcePosition::kNotInlined)
95  continue;
96  int position = it.source_position().ScriptOffset();
97  int line_number = script->GetLineNumber(position) + 1;
98  line_table->SetPosition(it.code_offset(), line_number);
99  }
100  }
101  rec->entry =
102  NewCodeEntry(tag, GetName(shared->DebugName()),
103  GetName(InferScriptName(script_name, shared)), line, column,
104  std::move(line_table), abstract_code->InstructionStart());
105  RecordInliningInfo(rec->entry, abstract_code);
106  rec->entry->FillFunctionInfo(shared);
107  rec->instruction_size = abstract_code->InstructionSize();
108  DispatchCodeEvent(evt_rec);
109 }
110 
111 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
112  const wasm::WasmCode* code,
113  wasm::WasmName name) {
114  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
115  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
116  rec->instruction_start = code->instruction_start();
117  rec->entry = NewCodeEntry(
118  tag, GetName(name.start()), CodeEntry::kWasmResourceNamePrefix,
119  CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
120  nullptr, code->instruction_start());
121  rec->instruction_size = code->instructions().length();
122  DispatchCodeEvent(evt_rec);
123 }
124 
125 void ProfilerListener::CodeMoveEvent(AbstractCode from, AbstractCode to) {
126  CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
127  CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
128  rec->from_instruction_start = from->InstructionStart();
129  rec->to_instruction_start = to->InstructionStart();
130  DispatchCodeEvent(evt_rec);
131 }
132 
133 void ProfilerListener::CodeDisableOptEvent(AbstractCode code,
134  SharedFunctionInfo* shared) {
135  CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
136  CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
137  rec->instruction_start = code->InstructionStart();
138  rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
139  DispatchCodeEvent(evt_rec);
140 }
141 
142 void ProfilerListener::CodeDeoptEvent(Code code, DeoptimizeKind kind,
143  Address pc, int fp_to_sp_delta) {
144  CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
145  CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
146  Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
147  rec->instruction_start = code->InstructionStart();
148  rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
149  rec->deopt_id = info.deopt_id;
150  rec->pc = pc;
151  rec->fp_to_sp_delta = fp_to_sp_delta;
152 
153  // When a function is deoptimized, we store the deoptimized frame information
154  // for the use of GetDeoptInfos().
155  AttachDeoptInlinedFrames(code, rec);
156  DispatchCodeEvent(evt_rec);
157 }
158 
159 void ProfilerListener::GetterCallbackEvent(Name name, Address entry_point) {
160  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
161  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
162  rec->instruction_start = entry_point;
163  rec->entry =
164  NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("get ", name));
165  rec->instruction_size = 1;
166  DispatchCodeEvent(evt_rec);
167 }
168 
169 void ProfilerListener::RegExpCodeCreateEvent(AbstractCode code, String source) {
170  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
171  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
172  rec->instruction_start = code->InstructionStart();
173  rec->entry = NewCodeEntry(
174  CodeEventListener::REG_EXP_TAG, GetConsName("RegExp: ", source),
175  CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
176  CpuProfileNode::kNoColumnNumberInfo, nullptr, code->InstructionStart());
177  rec->instruction_size = code->InstructionSize();
178  DispatchCodeEvent(evt_rec);
179 }
180 
181 void ProfilerListener::SetterCallbackEvent(Name name, Address entry_point) {
182  CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
183  CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
184  rec->instruction_start = entry_point;
185  rec->entry =
186  NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("set ", name));
187  rec->instruction_size = 1;
188  DispatchCodeEvent(evt_rec);
189 }
190 
191 Name ProfilerListener::InferScriptName(Name name, SharedFunctionInfo* info) {
192  if (name->IsString() && String::cast(name)->length()) return name;
193  if (!info->script()->IsScript()) return name;
194  Object* source_url = Script::cast(info->script())->source_url();
195  return source_url->IsName() ? Name::cast(source_url) : name;
196 }
197 
198 void ProfilerListener::RecordInliningInfo(CodeEntry* entry,
199  AbstractCode abstract_code) {
200  if (!abstract_code->IsCode()) return;
201  Code code = abstract_code->GetCode();
202  if (code->kind() != Code::OPTIMIZED_FUNCTION) return;
203  DeoptimizationData deopt_input_data =
204  DeoptimizationData::cast(code->deoptimization_data());
205  int deopt_count = deopt_input_data->DeoptCount();
206  for (int i = 0; i < deopt_count; i++) {
207  int pc_offset = deopt_input_data->Pc(i)->value();
208  if (pc_offset == -1) continue;
209  int translation_index = deopt_input_data->TranslationIndex(i)->value();
210  TranslationIterator it(deopt_input_data->TranslationByteArray(),
211  translation_index);
212  Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
213  DCHECK_EQ(Translation::BEGIN, opcode);
214  it.Skip(Translation::NumberOfOperandsFor(opcode));
215  int depth = 0;
216  std::vector<std::unique_ptr<CodeEntry>> inline_stack;
217  while (it.HasNext() &&
218  Translation::BEGIN !=
219  (opcode = static_cast<Translation::Opcode>(it.Next()))) {
220  if (opcode != Translation::INTERPRETED_FRAME) {
221  it.Skip(Translation::NumberOfOperandsFor(opcode));
222  continue;
223  }
224  it.Next(); // Skip ast_id
225  int shared_info_id = it.Next();
226  it.Next(); // Skip height
227  it.Next(); // Skip return value offset
228  it.Next(); // Skip return value count
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
Definition: libplatform.h:13