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