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
13namespace v8 {
14namespace internal {
15
16ProfilerListener::ProfilerListener(Isolate* isolate)
17    : function_and_resource_names_(isolate->heap()) {}
18
19ProfilerListener::~ProfilerListener() {
20  for (auto code_entry : code_entries_) {
21    delete code_entry;
22  }
23}
24
25void 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
34void 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
48void 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
62void 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
80void 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
117void 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
131void 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
139void 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
148void 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
161void 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
171void 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
184void 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
194Name* 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
201void 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
248void 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
286CodeEntry* 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
297void 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
305void 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