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
13 namespace v8 {
14 namespace internal {
15
ProfilerListener(Isolate * isolate)16 ProfilerListener::ProfilerListener(Isolate* isolate)
17 : function_and_resource_names_(isolate->heap()) {}
18
~ProfilerListener()19 ProfilerListener::~ProfilerListener() {
20 for (auto code_entry : code_entries_) {
21 delete code_entry;
22 }
23 }
24
CallbackEvent(Name * name,Address entry_point)25 void 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
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,const char * name)34 void 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
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,Name * name)48 void 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
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,SharedFunctionInfo * shared,Name * script_name)62 void 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
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * abstract_code,SharedFunctionInfo * shared,Name * script_name,int line,int column)80 void 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
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,int args_count)117 void 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
CodeMoveEvent(AbstractCode * from,Address to)131 void 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
CodeDisableOptEvent(AbstractCode * code,SharedFunctionInfo * shared)139 void 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
CodeDeoptEvent(Code * code,Address pc,int fp_to_sp_delta)148 void 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
GetterCallbackEvent(Name * name,Address entry_point)161 void 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
RegExpCodeCreateEvent(AbstractCode * code,String * source)171 void 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
SetterCallbackEvent(Name * name,Address entry_point)184 void 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
InferScriptName(Name * name,SharedFunctionInfo * info)194 Name* 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
RecordInliningInfo(CodeEntry * entry,AbstractCode * abstract_code)201 void 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
RecordDeoptInlinedFrames(CodeEntry * entry,AbstractCode * abstract_code)248 void 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
NewCodeEntry(CodeEventListener::LogEventsAndTags tag,const char * name,const char * name_prefix,const char * resource_name,int line_number,int column_number,JITLineInfoTable * line_info,Address instruction_start)286 CodeEntry* 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
AddObserver(CodeEventObserver * observer)297 void 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
RemoveObserver(CodeEventObserver * observer)305 void 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