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 26208 : 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 26208 : rec->instruction_start = code->InstructionStart();
43 : rec->entry = new CodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName,
44 : CpuProfileNode::kNoLineNumberInfo,
45 : CpuProfileNode::kNoColumnNumberInfo, nullptr,
46 52416 : code->InstructionStart());
47 26208 : rec->instruction_size = code->InstructionSize();
48 : DispatchCodeEvent(evt_rec);
49 26208 : }
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 603827 : 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 603827 : 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 1207654 : if (shared->script()->IsScript()) {
111 603827 : Script script = Script::cast(shared->script());
112 603827 : line_table.reset(new SourcePositionTable());
113 603827 : 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 3317616 : for (SourcePositionTableIterator it(abstract_code->source_position_table());
123 2713789 : !it.done(); it.Advance()) {
124 : int position = it.source_position().ScriptOffset();
125 2713789 : int line_number = script->GetLineNumber(position) + 1;
126 2713789 : int inlining_id = it.source_position().InliningId();
127 2713789 : line_table->SetPosition(it.code_offset(), line_number, inlining_id);
128 :
129 2713789 : if (inlining_id != SourcePosition::kNotInlined) {
130 : DCHECK(abstract_code->IsCode());
131 54 : Code code = abstract_code->GetCode();
132 : std::vector<SourcePositionInfo> stack =
133 108 : it.source_position().InliningStack(handle(code, isolate_));
134 : DCHECK(!stack.empty());
135 :
136 : std::vector<CodeEntryAndLineNumber> inline_stack;
137 206 : for (SourcePositionInfo& pos_info : stack) {
138 152 : if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
139 152 : if (pos_info.script.is_null()) continue;
140 :
141 : int line_number =
142 304 : pos_info.script->GetLineNumber(pos_info.position.ScriptOffset()) +
143 152 : 1;
144 :
145 : const char* resource_name =
146 : (pos_info.script->name()->IsName())
147 : ? GetName(Name::cast(pos_info.script->name()))
148 227 : : CodeEntry::kEmptyResourceName;
149 :
150 : bool inline_is_shared_cross_origin =
151 152 : pos_info.script->origin_options().IsSharedCrossOrigin();
152 :
153 : // We need the start line number and column number of the function for
154 : // kLeafNodeLineNumbers mode. Creating a SourcePositionInfo is a handy
155 : // way of getting both easily.
156 : SourcePositionInfo start_pos_info(
157 : SourcePosition(pos_info.shared->StartPosition()),
158 304 : pos_info.shared);
159 :
160 : std::unique_ptr<CodeEntry> inline_entry =
161 : base::make_unique<CodeEntry>(
162 304 : tag, GetName(pos_info.shared->DebugName()), resource_name,
163 304 : start_pos_info.line + 1, start_pos_info.column + 1, nullptr,
164 912 : code->InstructionStart(), inline_is_shared_cross_origin);
165 152 : inline_entry->FillFunctionInfo(*pos_info.shared);
166 :
167 : // Create a canonical CodeEntry for each inlined frame and then re-use
168 : // them for subsequent inline stacks to avoid a lot of duplication.
169 152 : CodeEntry* cached_entry = GetOrInsertCachedEntry(
170 152 : &cached_inline_entries, std::move(inline_entry));
171 :
172 304 : inline_stack.push_back(
173 : CodeEntryAndLineNumber{cached_entry, line_number});
174 : }
175 : DCHECK(!inline_stack.empty());
176 : inline_stacks.emplace(inlining_id, std::move(inline_stack));
177 : }
178 : }
179 : }
180 : rec->entry =
181 1207654 : new CodeEntry(tag, GetName(shared->DebugName()),
182 603827 : GetName(InferScriptName(script_name, shared)), line, column,
183 : std::move(line_table), abstract_code->InstructionStart(),
184 1207654 : is_shared_cross_origin);
185 603827 : if (!inline_stacks.empty()) {
186 39 : rec->entry->SetInlineStacks(std::move(cached_inline_entries),
187 13 : std::move(inline_stacks));
188 : }
189 :
190 603827 : rec->entry->FillFunctionInfo(shared);
191 603827 : rec->instruction_size = abstract_code->InstructionSize();
192 : DispatchCodeEvent(evt_rec);
193 603827 : }
194 :
195 11 : void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,
196 : const wasm::WasmCode* code,
197 : wasm::WasmName name) {
198 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
199 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
200 11 : rec->instruction_start = code->instruction_start();
201 : rec->entry = new CodeEntry(
202 : tag, GetName(name.start()), CodeEntry::kWasmResourceNamePrefix,
203 : CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo,
204 22 : nullptr, code->instruction_start(), true);
205 11 : rec->instruction_size = code->instructions().length();
206 : DispatchCodeEvent(evt_rec);
207 11 : }
208 :
209 5 : void ProfilerListener::CodeMoveEvent(AbstractCode from, AbstractCode to) {
210 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE);
211 : CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
212 5 : rec->from_instruction_start = from->InstructionStart();
213 5 : rec->to_instruction_start = to->InstructionStart();
214 : DispatchCodeEvent(evt_rec);
215 5 : }
216 :
217 0 : void ProfilerListener::CodeDisableOptEvent(AbstractCode code,
218 : SharedFunctionInfo shared) {
219 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT);
220 : CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
221 0 : rec->instruction_start = code->InstructionStart();
222 0 : rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason());
223 : DispatchCodeEvent(evt_rec);
224 0 : }
225 :
226 6 : void ProfilerListener::CodeDeoptEvent(Code code, DeoptimizeKind kind,
227 : Address pc, int fp_to_sp_delta) {
228 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT);
229 : CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
230 6 : Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc);
231 6 : rec->instruction_start = code->InstructionStart();
232 6 : rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
233 6 : rec->deopt_id = info.deopt_id;
234 6 : rec->pc = pc;
235 6 : rec->fp_to_sp_delta = fp_to_sp_delta;
236 :
237 : // When a function is deoptimized, we store the deoptimized frame information
238 : // for the use of GetDeoptInfos().
239 6 : AttachDeoptInlinedFrames(code, rec);
240 : DispatchCodeEvent(evt_rec);
241 6 : }
242 :
243 9113 : void ProfilerListener::GetterCallbackEvent(Name name, Address entry_point) {
244 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
245 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
246 9113 : rec->instruction_start = entry_point;
247 : rec->entry =
248 18226 : new CodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("get ", name));
249 9113 : rec->instruction_size = 1;
250 : DispatchCodeEvent(evt_rec);
251 9113 : }
252 :
253 0 : void ProfilerListener::RegExpCodeCreateEvent(AbstractCode code, String source) {
254 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
255 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
256 0 : rec->instruction_start = code->InstructionStart();
257 : rec->entry = new CodeEntry(
258 : CodeEventListener::REG_EXP_TAG, GetConsName("RegExp: ", source),
259 : CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo,
260 0 : CpuProfileNode::kNoColumnNumberInfo, nullptr, code->InstructionStart());
261 0 : rec->instruction_size = code->InstructionSize();
262 : DispatchCodeEvent(evt_rec);
263 0 : }
264 :
265 9113 : void ProfilerListener::SetterCallbackEvent(Name name, Address entry_point) {
266 : CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION);
267 : CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
268 9113 : rec->instruction_start = entry_point;
269 : rec->entry =
270 18226 : new CodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("set ", name));
271 9113 : rec->instruction_size = 1;
272 : DispatchCodeEvent(evt_rec);
273 9113 : }
274 :
275 603827 : Name ProfilerListener::InferScriptName(Name name, SharedFunctionInfo info) {
276 1207654 : if (name->IsString() && String::cast(name)->length()) return name;
277 1206432 : if (!info->script()->IsScript()) return name;
278 603216 : Object source_url = Script::cast(info->script())->source_url();
279 603216 : return source_url->IsName() ? Name::cast(source_url) : name;
280 : }
281 :
282 6 : void ProfilerListener::AttachDeoptInlinedFrames(Code code,
283 : CodeDeoptEventRecord* rec) {
284 6 : int deopt_id = rec->deopt_id;
285 6 : SourcePosition last_position = SourcePosition::Unknown();
286 : int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) |
287 : RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) |
288 : RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID);
289 :
290 6 : rec->deopt_frames = nullptr;
291 6 : rec->deopt_frame_count = 0;
292 :
293 9 : for (RelocIterator it(code, mask); !it.done(); it.next()) {
294 : RelocInfo* info = it.rinfo();
295 6 : if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
296 3 : int script_offset = static_cast<int>(info->data());
297 3 : it.next();
298 : DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
299 3 : int inlining_id = static_cast<int>(it.rinfo()->data());
300 3 : last_position = SourcePosition(script_offset, inlining_id);
301 3 : continue;
302 : }
303 3 : if (info->rmode() == RelocInfo::DEOPT_ID) {
304 3 : if (deopt_id != static_cast<int>(info->data())) continue;
305 : DCHECK(last_position.IsKnown());
306 :
307 : // SourcePosition::InliningStack allocates a handle for the SFI of each
308 : // frame. These don't escape this function, but quickly add up. This
309 : // scope limits their lifetime.
310 3 : HandleScope scope(isolate_);
311 : std::vector<SourcePositionInfo> stack =
312 6 : last_position.InliningStack(handle(code, isolate_));
313 : CpuProfileDeoptFrame* deopt_frames =
314 3 : new CpuProfileDeoptFrame[stack.size()];
315 :
316 : int deopt_frame_count = 0;
317 12 : for (SourcePositionInfo& pos_info : stack) {
318 9 : if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
319 9 : if (pos_info.script.is_null()) continue;
320 : int script_id = pos_info.script->id();
321 9 : size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
322 9 : deopt_frames[deopt_frame_count++] = {script_id, offset};
323 : }
324 3 : rec->deopt_frames = deopt_frames;
325 3 : rec->deopt_frame_count = deopt_frame_count;
326 : break;
327 : }
328 : }
329 6 : }
330 :
331 : } // namespace internal
332 122004 : } // namespace v8
|