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