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/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"
15 :
16 : namespace v8 {
17 : namespace internal {
18 :
19 768 : ProfilerListener::ProfilerListener(Isolate* isolate,
20 : CodeEventObserver* observer)
21 768 : : isolate_(isolate), observer_(observer) {}
22 :
23 : ProfilerListener::~ProfilerListener() = default;
24 :
25 36474 : void ProfilerListener::CallbackEvent(Name name, Address entry_point) {
26 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
27 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
28 18237 : rec->instruction_start = entry_point;
29 36474 : rec->entry = NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name));
30 18237 : rec->instruction_size = 1;
31 : DispatchCodeEvent(evt_rec);
32 18237 : }
33 :
34 25908 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
35 25908 : AbstractCode code, const char* name) {
36 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
37 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
38 25908 : rec->instruction_start = code->InstructionStart();
39 : rec->entry = NewCodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName,
40 : CpuProfileNode::kNoLineNumberInfo,
41 : CpuProfileNode::kNoColumnNumberInfo, nullptr,
42 51816 : code->InstructionStart());
43 25908 : rec->instruction_size = code->InstructionSize();
44 : DispatchCodeEvent(evt_rec);
45 25908 : }
46 :
47 5 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
48 5 : AbstractCode code, Name name) {
49 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
50 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
51 5 : rec->instruction_start = code->InstructionStart();
52 : rec->entry = NewCodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName,
53 : CpuProfileNode::kNoLineNumberInfo,
54 : CpuProfileNode::kNoColumnNumberInfo, nullptr,
55 10 : code->InstructionStart());
56 5 : rec->instruction_size = code->InstructionSize();
57 : DispatchCodeEvent(evt_rec);
58 5 : }
59 :
60 0 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
61 : AbstractCode code,
62 : SharedFunctionInfo shared,
63 0 : Name script_name) {
64 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
65 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
66 0 : rec->instruction_start = code->InstructionStart();
67 : rec->entry = NewCodeEntry(tag, GetName(shared->DebugName()),
68 : GetName(InferScriptName(script_name, shared)),
69 : CpuProfileNode::kNoLineNumberInfo,
70 : CpuProfileNode::kNoColumnNumberInfo, nullptr,
71 0 : code->InstructionStart());
72 : DCHECK(!code->IsCode());
73 0 : rec->entry->FillFunctionInfo(shared);
74 0 : rec->instruction_size = code->InstructionSize();
75 : DispatchCodeEvent(evt_rec);
76 0 : }
77 :
78 : namespace {
79 :
80 134 : CodeEntry* GetOrInsertCachedEntry(
81 : std::unordered_set<std::unique_ptr<CodeEntry>, CodeEntry::Hasher,
82 : CodeEntry::Equals>* entries,
83 : std::unique_ptr<CodeEntry> search_value) {
84 : auto it = entries->find(search_value);
85 238 : if (it != entries->end()) return it->get();
86 : CodeEntry* ret = search_value.get();
87 : entries->insert(std::move(search_value));
88 30 : return ret;
89 : }
90 :
91 : } // namespace
92 :
93 603402 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
94 : AbstractCode abstract_code,
95 : SharedFunctionInfo shared,
96 603402 : Name script_name, int line, int column) {
97 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
98 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
99 603402 : rec->instruction_start = abstract_code->InstructionStart();
100 : std::unique_ptr<SourcePositionTable> line_table;
101 603402 : std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> inline_stacks;
102 : std::unordered_set<std::unique_ptr<CodeEntry>, CodeEntry::Hasher,
103 : CodeEntry::Equals>
104 603402 : cached_inline_entries;
105 1206804 : if (shared->script()->IsScript()) {
106 1206804 : Script script = Script::cast(shared->script());
107 603402 : line_table.reset(new SourcePositionTable());
108 603402 : HandleScope scope(isolate_);
109 :
110 : // Add each position to the source position table and store inlining stacks
111 : // for inline positions. We store almost the same information in the
112 : // profiler as is stored on the code object, except that we transform source
113 : // positions to line numbers here, because we only care about attributing
114 : // ticks to a given line.
115 3920101 : for (SourcePositionTableIterator it(abstract_code->source_position_table());
116 2713297 : !it.done(); it.Advance()) {
117 2713297 : int position = it.source_position().ScriptOffset();
118 2713297 : int line_number = script->GetLineNumber(position) + 1;
119 5426594 : int inlining_id = it.source_position().InliningId();
120 2713297 : line_table->SetPosition(it.code_offset(), line_number, inlining_id);
121 :
122 2713297 : if (inlining_id != SourcePosition::kNotInlined) {
123 : DCHECK(abstract_code->IsCode());
124 48 : Code code = abstract_code->GetCode();
125 : std::vector<SourcePositionInfo> stack =
126 144 : it.source_position().InliningStack(handle(code, isolate_));
127 : DCHECK(!stack.empty());
128 :
129 : std::vector<CodeEntryAndLineNumber> inline_stack;
130 230 : for (SourcePositionInfo& pos_info : stack) {
131 134 : if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
132 134 : if (pos_info.script.is_null()) continue;
133 :
134 : int line_number =
135 268 : pos_info.script->GetLineNumber(pos_info.position.ScriptOffset()) +
136 134 : 1;
137 :
138 : const char* resource_name =
139 268 : (pos_info.script->name()->IsName())
140 211 : ? GetName(Name::cast(pos_info.script->name()))
141 191 : : CodeEntry::kEmptyResourceName;
142 :
143 : // We need the start line number and column number of the function for
144 : // kLeafNodeLineNumbers mode. Creating a SourcePositionInfo is a handy
145 : // way of getting both easily.
146 : SourcePositionInfo start_pos_info(
147 : SourcePosition(pos_info.shared->StartPosition()),
148 268 : pos_info.shared);
149 :
150 : std::unique_ptr<CodeEntry> inline_entry =
151 : base::make_unique<CodeEntry>(
152 268 : tag, GetName(pos_info.shared->DebugName()), resource_name,
153 268 : start_pos_info.line + 1, start_pos_info.column + 1, nullptr,
154 670 : code->InstructionStart());
155 134 : inline_entry->FillFunctionInfo(*pos_info.shared);
156 :
157 : // Create a canonical CodeEntry for each inlined frame and then re-use
158 : // them for subsequent inline stacks to avoid a lot of duplication.
159 : CodeEntry* cached_entry = GetOrInsertCachedEntry(
160 268 : &cached_inline_entries, std::move(inline_entry));
161 :
162 : inline_stack.push_back(
163 268 : CodeEntryAndLineNumber{cached_entry, line_number});
164 : }
165 : DCHECK(!inline_stack.empty());
166 : inline_stacks.emplace(inlining_id, std::move(inline_stack));
167 : }
168 : }
169 : }
170 : rec->entry =
171 : NewCodeEntry(tag, GetName(shared->DebugName()),
172 : GetName(InferScriptName(script_name, shared)), line, column,
173 2413608 : std::move(line_table), abstract_code->InstructionStart());
174 603402 : if (!inline_stacks.empty()) {
175 : rec->entry->SetInlineStacks(std::move(cached_inline_entries),
176 30 : std::move(inline_stacks));
177 : }
178 :
179 603402 : rec->entry->FillFunctionInfo(shared);
180 603402 : rec->instruction_size = abstract_code->InstructionSize();
181 : DispatchCodeEvent(evt_rec);
182 603402 : }
183 :
184 0 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
185 : const wasm::WasmCode* code,
186 0 : wasm::WasmName name) {
187 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
188 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
189 0 : rec->instruction_start = code->instruction_start();
190 : rec->entry = NewCodeEntry(
191 : tag, GetName(name.start()), CodeEntry::kWasmResourceNamePrefix,
192 : CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
193 0 : nullptr, code->instruction_start());
194 0 : rec->instruction_size = code->instructions().length();
195 : DispatchCodeEvent(evt_rec);
196 0 : }
197 :
198 10 : void ProfilerListener::CodeMoveEvent(AbstractCode from, AbstractCode to) {
199 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
200 : CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
201 5 : rec->from_instruction_start = from->InstructionStart();
202 5 : rec->to_instruction_start = to->InstructionStart();
203 : DispatchCodeEvent(evt_rec);
204 5 : }
205 :
206 0 : void ProfilerListener::CodeDisableOptEvent(AbstractCode code,
207 0 : SharedFunctionInfo shared) {
208 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
209 : CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
210 0 : rec->instruction_start = code->InstructionStart();
211 0 : rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
212 : DispatchCodeEvent(evt_rec);
213 0 : }
214 :
215 0 : void ProfilerListener::CodeDeoptEvent(Code code, DeoptimizeKind kind,
216 0 : Address pc, int fp_to_sp_delta) {
217 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
218 : CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
219 0 : Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
220 0 : rec->instruction_start = code->InstructionStart();
221 0 : rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
222 0 : rec->deopt_id = info.deopt_id;
223 0 : rec->pc = pc;
224 0 : rec->fp_to_sp_delta = fp_to_sp_delta;
225 :
226 : // When a function is deoptimized, we store the deoptimized frame information
227 : // for the use of GetDeoptInfos().
228 0 : AttachDeoptInlinedFrames(code, rec);
229 : DispatchCodeEvent(evt_rec);
230 0 : }
231 :
232 17666 : void ProfilerListener::GetterCallbackEvent(Name name, Address entry_point) {
233 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
234 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
235 8833 : rec->instruction_start = entry_point;
236 : rec->entry =
237 17666 : NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("get ", name));
238 8833 : rec->instruction_size = 1;
239 : DispatchCodeEvent(evt_rec);
240 8833 : }
241 :
242 0 : void ProfilerListener::RegExpCodeCreateEvent(AbstractCode code, String source) {
243 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
244 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
245 0 : rec->instruction_start = code->InstructionStart();
246 : rec->entry = NewCodeEntry(
247 : CodeEventListener::REG_EXP_TAG, GetConsName("RegExp: ", source),
248 : CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
249 0 : CpuProfileNode::kNoColumnNumberInfo, nullptr, code->InstructionStart());
250 0 : rec->instruction_size = code->InstructionSize();
251 : DispatchCodeEvent(evt_rec);
252 0 : }
253 :
254 17666 : void ProfilerListener::SetterCallbackEvent(Name name, Address entry_point) {
255 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
256 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
257 8833 : rec->instruction_start = entry_point;
258 : rec->entry =
259 17666 : NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("set ", name));
260 8833 : rec->instruction_size = 1;
261 : DispatchCodeEvent(evt_rec);
262 8833 : }
263 :
264 603402 : Name ProfilerListener::InferScriptName(Name name, SharedFunctionInfo info) {
265 1206804 : if (name->IsString() && String::cast(name)->length()) return name;
266 1205612 : if (!info->script()->IsScript()) return name;
267 1205612 : Object source_url = Script::cast(info->script())->source_url();
268 1205612 : return source_url->IsName() ? Name::cast(source_url) : name;
269 : }
270 :
271 0 : void ProfilerListener::AttachDeoptInlinedFrames(Code code,
272 : CodeDeoptEventRecord* rec) {
273 0 : int deopt_id = rec->deopt_id;
274 0 : SourcePosition last_position = SourcePosition::Unknown();
275 : int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
276 : RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
277 : RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
278 :
279 0 : rec->deopt_frames = nullptr;
280 0 : rec->deopt_frame_count = 0;
281 :
282 0 : for (RelocIterator it(code, mask); !it.done(); it.next()) {
283 0 : RelocInfo* info = it.rinfo();
284 0 : if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
285 0 : int script_offset = static_cast<int>(info->data());
286 0 : it.next();
287 : DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
288 0 : int inlining_id = static_cast<int>(it.rinfo()->data());
289 0 : last_position = SourcePosition(script_offset, inlining_id);
290 0 : continue;
291 : }
292 0 : if (info->rmode() == RelocInfo::DEOPT_ID) {
293 0 : if (deopt_id != static_cast<int>(info->data())) continue;
294 : DCHECK(last_position.IsKnown());
295 :
296 : // SourcePosition::InliningStack allocates a handle for the SFI of each
297 : // frame. These don't escape this function, but quickly add up. This
298 : // scope limits their lifetime.
299 0 : HandleScope scope(isolate_);
300 : std::vector<SourcePositionInfo> stack =
301 0 : last_position.InliningStack(handle(code, isolate_));
302 : CpuProfileDeoptFrame* deopt_frames =
303 0 : new CpuProfileDeoptFrame[stack.size()];
304 :
305 : int deopt_frame_count = 0;
306 0 : for (SourcePositionInfo& pos_info : stack) {
307 0 : if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
308 0 : if (pos_info.script.is_null()) continue;
309 : int script_id = pos_info.script->id();
310 0 : size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
311 0 : deopt_frames[deopt_frame_count++] = {script_id, offset};
312 : }
313 0 : rec->deopt_frames = deopt_frames;
314 0 : rec->deopt_frame_count = deopt_frame_count;
315 : break;
316 : }
317 : }
318 0 : }
319 :
320 0 : CodeEntry* ProfilerListener::NewCodeEntry(
321 : CodeEventListener::LogEventsAndTags tag, const char* name,
322 : const char* resource_name, int line_number, int column_number,
323 : std::unique_ptr<SourcePositionTable> line_info, Address instruction_start) {
324 : return new CodeEntry(tag, name, resource_name, line_number, column_number,
325 665218 : std::move(line_info), instruction_start);
326 : }
327 :
328 : } // namespace internal
329 183867 : } // namespace v8
|