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/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 :
16 288 : ProfilerListener::ProfilerListener(Isolate* isolate)
17 576 : : function_and_resource_names_(isolate->heap()) {}
18 :
19 1042 : ProfilerListener::~ProfilerListener() {
20 181603 : for (auto code_entry : code_entries_) {
21 181067 : delete code_entry;
22 : }
23 506 : }
24 :
25 6950 : void ProfilerListener::CallbackEvent(Name* name, Address entry_point) {
26 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
27 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
28 6950 : rec->start = entry_point;
29 13900 : rec->entry = NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name));
30 6950 : rec->size = 1;
31 : DispatchCodeEvent(evt_rec);
32 6950 : }
33 :
34 29485 : 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 29485 : rec->start = code->address();
39 : rec->entry = NewCodeEntry(
40 : tag, GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
41 : CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
42 58970 : CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
43 29485 : RecordInliningInfo(rec->entry, code);
44 29485 : rec->size = code->ExecutableSize();
45 : DispatchCodeEvent(evt_rec);
46 29485 : }
47 :
48 6 : 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 6 : rec->start = code->address();
53 : rec->entry = NewCodeEntry(
54 : tag, GetFunctionName(name), CodeEntry::kEmptyNamePrefix,
55 : CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
56 12 : CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
57 6 : RecordInliningInfo(rec->entry, code);
58 6 : rec->size = code->ExecutableSize();
59 : DispatchCodeEvent(evt_rec);
60 6 : }
61 :
62 129802 : 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 129802 : rec->start = code->address();
69 : rec->entry = NewCodeEntry(
70 129802 : tag, GetFunctionName(shared->DebugName()), CodeEntry::kEmptyNamePrefix,
71 : GetName(InferScriptName(script_name, shared)),
72 : CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
73 389406 : NULL, code->instruction_start());
74 129802 : RecordInliningInfo(rec->entry, code);
75 129802 : rec->entry->FillFunctionInfo(shared);
76 129802 : rec->size = code->ExecutableSize();
77 : DispatchCodeEvent(evt_rec);
78 129802 : }
79 :
80 3476 : 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 3476 : rec->start = abstract_code->address();
88 : JITLineInfoTable* line_table = NULL;
89 3476 : if (shared->script()->IsScript()) {
90 : Script* script = Script::cast(shared->script());
91 3476 : line_table = new JITLineInfoTable();
92 : int offset = abstract_code->IsCode() ? Code::kHeaderSize
93 3476 : : BytecodeArray::kHeaderSize;
94 62268 : for (SourcePositionTableIterator it(abstract_code->source_position_table());
95 55316 : !it.done(); it.Advance()) {
96 : // TODO(alph,tebbi) Skipping inlined positions for now, because they might
97 : // refer to a different script.
98 110632 : if (it.source_position().InliningId() != SourcePosition::kNotInlined)
99 : continue;
100 : int position = it.source_position().ScriptOffset();
101 55284 : int line_number = script->GetLineNumber(position) + 1;
102 55284 : int pc_offset = it.code_offset() + offset;
103 55284 : line_table->SetPosition(pc_offset, line_number);
104 : }
105 : }
106 : rec->entry = NewCodeEntry(
107 3476 : tag, GetFunctionName(shared->DebugName()), CodeEntry::kEmptyNamePrefix,
108 : GetName(InferScriptName(script_name, shared)), line, column, line_table,
109 10428 : abstract_code->instruction_start());
110 3476 : RecordInliningInfo(rec->entry, abstract_code);
111 3476 : RecordDeoptInlinedFrames(rec->entry, abstract_code);
112 3476 : rec->entry->FillFunctionInfo(shared);
113 3476 : rec->size = abstract_code->ExecutableSize();
114 : DispatchCodeEvent(evt_rec);
115 3476 : }
116 :
117 24 : 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 24 : rec->start = code->address();
122 : rec->entry = NewCodeEntry(
123 : tag, GetName(args_count), "args_count: ", CodeEntry::kEmptyResourceName,
124 : CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
125 48 : NULL, code->instruction_start());
126 24 : RecordInliningInfo(rec->entry, code);
127 24 : rec->size = code->ExecutableSize();
128 : DispatchCodeEvent(evt_rec);
129 24 : }
130 :
131 6 : void ProfilerListener::CodeMoveEvent(AbstractCode* from, Address to) {
132 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
133 : CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
134 6 : rec->from = from->address();
135 6 : rec->to = to;
136 : DispatchCodeEvent(evt_rec);
137 6 : }
138 :
139 22 : void ProfilerListener::CodeDisableOptEvent(AbstractCode* code,
140 : SharedFunctionInfo* shared) {
141 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
142 : CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
143 22 : rec->start = code->address();
144 22 : rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
145 : DispatchCodeEvent(evt_rec);
146 22 : }
147 :
148 41 : void ProfilerListener::CodeDeoptEvent(Code* code, DeoptKind kind, Address pc,
149 : int fp_to_sp_delta) {
150 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
151 : CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
152 41 : Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
153 41 : rec->start = code->address();
154 41 : rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
155 41 : rec->deopt_id = info.deopt_id;
156 41 : rec->pc = reinterpret_cast<void*>(pc);
157 41 : rec->fp_to_sp_delta = fp_to_sp_delta;
158 : DispatchCodeEvent(evt_rec);
159 41 : }
160 :
161 17442 : void ProfilerListener::GetterCallbackEvent(Name* name, Address entry_point) {
162 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
163 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
164 17442 : rec->start = entry_point;
165 : rec->entry =
166 34884 : NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name), "get ");
167 17442 : rec->size = 1;
168 : DispatchCodeEvent(evt_rec);
169 17442 : }
170 :
171 0 : void ProfilerListener::RegExpCodeCreateEvent(AbstractCode* code,
172 : String* source) {
173 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
174 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
175 0 : rec->start = code->address();
176 : rec->entry = NewCodeEntry(
177 : CodeEventListener::REG_EXP_TAG, GetName(source), "RegExp: ",
178 : CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
179 0 : CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
180 0 : rec->size = code->ExecutableSize();
181 : DispatchCodeEvent(evt_rec);
182 0 : }
183 :
184 17442 : void ProfilerListener::SetterCallbackEvent(Name* name, Address entry_point) {
185 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
186 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
187 17442 : rec->start = entry_point;
188 : rec->entry =
189 34884 : NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name), "set ");
190 17442 : rec->size = 1;
191 : DispatchCodeEvent(evt_rec);
192 17442 : }
193 :
194 133278 : Name* ProfilerListener::InferScriptName(Name* name, SharedFunctionInfo* info) {
195 266556 : if (name->IsString() && String::cast(name)->length()) return name;
196 132203 : if (!info->script()->IsScript()) return name;
197 : Object* source_url = Script::cast(info->script())->source_url();
198 2652 : return source_url->IsName() ? Name::cast(source_url) : name;
199 : }
200 :
201 163059 : void ProfilerListener::RecordInliningInfo(CodeEntry* entry,
202 : AbstractCode* abstract_code) {
203 162793 : if (!abstract_code->IsCode()) return;
204 : Code* code = abstract_code->GetCode();
205 160902 : 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 10359 : for (int i = 0; i < deopt_count; i++) {
210 9692 : int pc_offset = deopt_input_data->Pc(i)->value();
211 12947 : if (pc_offset == -1) continue;
212 6437 : int translation_index = deopt_input_data->TranslationIndex(i)->value();
213 : TranslationIterator it(deopt_input_data->TranslationByteArray(),
214 : translation_index);
215 6437 : Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next());
216 : DCHECK_EQ(Translation::BEGIN, opcode);
217 6437 : it.Skip(Translation::NumberOfOperandsFor(opcode));
218 : int depth = 0;
219 : std::vector<CodeEntry*> inline_stack;
220 264868 : while (it.HasNext() &&
221 : Translation::BEGIN !=
222 87920 : (opcode = static_cast<Translation::Opcode>(it.Next()))) {
223 82037 : if (opcode != Translation::JS_FRAME &&
224 : opcode != Translation::INTERPRETED_FRAME) {
225 75467 : it.Skip(Translation::NumberOfOperandsFor(opcode));
226 81904 : continue;
227 : }
228 6570 : it.Next(); // Skip ast_id
229 6570 : int shared_info_id = it.Next();
230 6570 : it.Next(); // Skip height
231 : SharedFunctionInfo* shared_info = SharedFunctionInfo::cast(
232 : deopt_input_data->LiteralArray()->get(shared_info_id));
233 6570 : if (!depth++) continue; // Skip the current function itself.
234 : CodeEntry* inline_entry = new CodeEntry(
235 133 : entry->tag(), GetFunctionName(shared_info->DebugName()),
236 : CodeEntry::kEmptyNamePrefix, entry->resource_name(),
237 : CpuProfileNode::kNoLineNumberInfo,
238 266 : CpuProfileNode::kNoColumnNumberInfo, NULL, code->instruction_start());
239 133 : inline_entry->FillFunctionInfo(shared_info);
240 133 : inline_stack.push_back(inline_entry);
241 : }
242 6437 : if (!inline_stack.empty()) {
243 220 : entry->AddInlineStack(pc_offset, std::move(inline_stack));
244 : }
245 : }
246 : }
247 :
248 3476 : void ProfilerListener::RecordDeoptInlinedFrames(CodeEntry* entry,
249 : AbstractCode* abstract_code) {
250 6295 : if (abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION) return;
251 : Handle<Code> code(abstract_code->GetCode());
252 :
253 657 : 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 1289 : for (RelocIterator it(*code, mask); !it.done(); it.next()) {
258 1264 : RelocInfo* info = it.rinfo();
259 632 : if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
260 316 : int script_offset = static_cast<int>(info->data());
261 316 : it.next();
262 : DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
263 316 : int inlining_id = static_cast<int>(it.rinfo()->data());
264 316 : last_position = SourcePosition(script_offset, inlining_id);
265 316 : continue;
266 : }
267 316 : if (info->rmode() == RelocInfo::DEOPT_ID) {
268 316 : int deopt_id = static_cast<int>(info->data());
269 : DCHECK(last_position.IsKnown());
270 : std::vector<CpuProfileDeoptFrame> inlined_frames;
271 1312 : for (SourcePositionInfo& pos_info : last_position.InliningStack(code)) {
272 : DCHECK(pos_info.position.ScriptOffset() != kNoSourcePosition);
273 364 : if (!pos_info.function->script()->IsScript()) continue;
274 : int script_id = Script::cast(pos_info.function->script())->id();
275 364 : size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
276 728 : inlined_frames.push_back(CpuProfileDeoptFrame({script_id, offset}));
277 : }
278 632 : if (!inlined_frames.empty() &&
279 316 : !entry->HasDeoptInlinedFramesFor(deopt_id)) {
280 520 : entry->AddDeoptInlinedFrames(deopt_id, std::move(inlined_frames));
281 : }
282 : }
283 : }
284 : }
285 :
286 204627 : 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 409254 : column_number, line_info, instruction_start);
293 204627 : code_entries_.push_back(code_entry);
294 204627 : return code_entry;
295 : }
296 :
297 324 : void ProfilerListener::AddObserver(CodeEventObserver* observer) {
298 324 : base::LockGuard<base::Mutex> guard(&mutex_);
299 324 : if (std::find(observers_.begin(), observers_.end(), observer) !=
300 : observers_.end())
301 324 : return;
302 324 : observers_.push_back(observer);
303 : }
304 :
305 324 : void ProfilerListener::RemoveObserver(CodeEventObserver* observer) {
306 324 : base::LockGuard<base::Mutex> guard(&mutex_);
307 : auto it = std::find(observers_.begin(), observers_.end(), observer);
308 648 : if (it == observers_.end()) return;
309 324 : observers_.erase(it);
310 : }
311 :
312 : } // namespace internal
313 : } // namespace v8
|