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/instruction-stream.h" 9 #include "src/objects-inl.h" 10 #include "src/profiler/cpu-profiler.h" 11 #include "src/profiler/profile-generator-inl.h" 12 #include "src/source-position-table.h" 13 #include "src/wasm/wasm-code-manager.h" 14 15 namespace v8 { 16 namespace internal { 17 18 ProfilerListener::ProfilerListener(Isolate* isolate, 19 CodeEventObserver* observer) 20 : isolate_(isolate), observer_(observer) {} 21 22 ProfilerListener::~ProfilerListener() = default; 23 24 void ProfilerListener::CallbackEvent(Name* name, Address entry_point) { 25 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 26 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 27 rec->instruction_start = entry_point; 28 rec->entry = NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetName(name)); 29 rec->instruction_size = 1; 30 DispatchCodeEvent(evt_rec); 31 } 32 33 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 34 AbstractCode* code, const char* name) { 35 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 36 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 37 rec->instruction_start = code->InstructionStart(); 38 rec->entry = NewCodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName, 39 CpuProfileNode::kNoLineNumberInfo, 40 CpuProfileNode::kNoColumnNumberInfo, nullptr, 41 code->InstructionStart()); 42 RecordInliningInfo(rec->entry, code); 43 rec->instruction_size = code->InstructionSize(); 44 DispatchCodeEvent(evt_rec); 45 } 46 47 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 rec->instruction_start = code->InstructionStart(); 52 rec->entry = NewCodeEntry(tag, GetName(name), CodeEntry::kEmptyResourceName, 53 CpuProfileNode::kNoLineNumberInfo, 54 CpuProfileNode::kNoColumnNumberInfo, nullptr, 55 code->InstructionStart()); 56 RecordInliningInfo(rec->entry, code); 57 rec->instruction_size = code->InstructionSize(); 58 DispatchCodeEvent(evt_rec); 59 } 60 61 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 62 AbstractCode* code, 63 SharedFunctionInfo* shared, 64 Name* script_name) { 65 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 66 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 67 rec->instruction_start = code->InstructionStart(); 68 rec->entry = NewCodeEntry(tag, GetName(shared->DebugName()), 69 GetName(InferScriptName(script_name, shared)), 70 CpuProfileNode::kNoLineNumberInfo, 71 CpuProfileNode::kNoColumnNumberInfo, nullptr, 72 code->InstructionStart()); 73 RecordInliningInfo(rec->entry, code); 74 rec->entry->FillFunctionInfo(shared); 75 rec->instruction_size = code->InstructionSize(); 76 DispatchCodeEvent(evt_rec); 77 } 78 79 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 80 AbstractCode* abstract_code, 81 SharedFunctionInfo* shared, 82 Name* script_name, int line, 83 int column) { 84 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 85 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 86 rec->instruction_start = abstract_code->InstructionStart(); 87 std::unique_ptr<SourcePositionTable> line_table; 88 if (shared->script()->IsScript()) { 89 Script* script = Script::cast(shared->script()); 90 line_table.reset(new SourcePositionTable()); 91 for (SourcePositionTableIterator it(abstract_code->source_position_table()); 92 !it.done(); it.Advance()) { 93 // TODO(alph,tebbi) Skipping inlined positions for now, because they might 94 // refer to a different script. 95 if (it.source_position().InliningId() != SourcePosition::kNotInlined) 96 continue; 97 int position = it.source_position().ScriptOffset(); 98 int line_number = script->GetLineNumber(position) + 1; 99 line_table->SetPosition(it.code_offset(), line_number); 100 } 101 } 102 rec->entry = 103 NewCodeEntry(tag, GetName(shared->DebugName()), 104 GetName(InferScriptName(script_name, shared)), line, column, 105 std::move(line_table), abstract_code->InstructionStart()); 106 RecordInliningInfo(rec->entry, abstract_code); 107 rec->entry->FillFunctionInfo(shared); 108 rec->instruction_size = abstract_code->InstructionSize(); 109 DispatchCodeEvent(evt_rec); 110 } 111 112 void ProfilerListener::CodeCreateEvent(CodeEventListener::LogEventsAndTags tag, 113 const wasm::WasmCode* code, 114 wasm::WasmName name) { 115 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 116 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 117 rec->instruction_start = code->instruction_start(); 118 rec->entry = NewCodeEntry( 119 tag, GetName(name.start()), CodeEntry::kWasmResourceNamePrefix, 120 CpuProfileNode::kNoLineNumberInfo, CpuProfileNode::kNoColumnNumberInfo, 121 nullptr, code->instruction_start()); 122 rec->instruction_size = code->instructions().length(); 123 DispatchCodeEvent(evt_rec); 124 } 125 126 void ProfilerListener::CodeMoveEvent(AbstractCode* from, AbstractCode* to) { 127 CodeEventsContainer evt_rec(CodeEventRecord::CODE_MOVE); 128 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_; 129 rec->from_instruction_start = from->InstructionStart(); 130 rec->to_instruction_start = to->InstructionStart(); 131 DispatchCodeEvent(evt_rec); 132 } 133 134 void ProfilerListener::CodeDisableOptEvent(AbstractCode* code, 135 SharedFunctionInfo* shared) { 136 CodeEventsContainer evt_rec(CodeEventRecord::CODE_DISABLE_OPT); 137 CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_; 138 rec->instruction_start = code->InstructionStart(); 139 rec->bailout_reason = GetBailoutReason(shared->disable_optimization_reason()); 140 DispatchCodeEvent(evt_rec); 141 } 142 143 void ProfilerListener::CodeDeoptEvent(Code* code, DeoptimizeKind kind, 144 Address pc, int fp_to_sp_delta) { 145 CodeEventsContainer evt_rec(CodeEventRecord::CODE_DEOPT); 146 CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_; 147 Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(code, pc); 148 rec->instruction_start = code->InstructionStart(); 149 rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason); 150 rec->deopt_id = info.deopt_id; 151 rec->pc = pc; 152 rec->fp_to_sp_delta = fp_to_sp_delta; 153 154 // When a function is deoptimized, we store the deoptimized frame information 155 // for the use of GetDeoptInfos(). 156 AttachDeoptInlinedFrames(code, rec); 157 DispatchCodeEvent(evt_rec); 158 } 159 160 void ProfilerListener::GetterCallbackEvent(Name* name, Address entry_point) { 161 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 162 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 163 rec->instruction_start = entry_point; 164 rec->entry = 165 NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("get ", name)); 166 rec->instruction_size = 1; 167 DispatchCodeEvent(evt_rec); 168 } 169 170 void ProfilerListener::RegExpCodeCreateEvent(AbstractCode* code, 171 String* source) { 172 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 173 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 174 rec->instruction_start = code->InstructionStart(); 175 rec->entry = NewCodeEntry( 176 CodeEventListener::REG_EXP_TAG, GetConsName("RegExp: ", source), 177 CodeEntry::kEmptyResourceName, CpuProfileNode::kNoLineNumberInfo, 178 CpuProfileNode::kNoColumnNumberInfo, nullptr, code->InstructionStart()); 179 rec->instruction_size = code->InstructionSize(); 180 DispatchCodeEvent(evt_rec); 181 } 182 183 void ProfilerListener::SetterCallbackEvent(Name* name, Address entry_point) { 184 CodeEventsContainer evt_rec(CodeEventRecord::CODE_CREATION); 185 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_; 186 rec->instruction_start = entry_point; 187 rec->entry = 188 NewCodeEntry(CodeEventListener::CALLBACK_TAG, GetConsName("set ", name)); 189 rec->instruction_size = 1; 190 DispatchCodeEvent(evt_rec); 191 } 192 193 Name* ProfilerListener::InferScriptName(Name* name, SharedFunctionInfo* info) { 194 if (name->IsString() && String::cast(name)->length()) return name; 195 if (!info->script()->IsScript()) return name; 196 Object* source_url = Script::cast(info->script())->source_url(); 197 return source_url->IsName() ? Name::cast(source_url) : name; 198 } 199 200 void ProfilerListener::RecordInliningInfo(CodeEntry* entry, 201 AbstractCode* abstract_code) { 202 if (!abstract_code->IsCode()) return; 203 Code* code = abstract_code->GetCode(); 204 if (code->kind() != Code::OPTIMIZED_FUNCTION) return; 205 DeoptimizationData* deopt_input_data = 206 DeoptimizationData::cast(code->deoptimization_data()); 207 int deopt_count = deopt_input_data->DeoptCount(); 208 for (int i = 0; i < deopt_count; i++) { 209 int pc_offset = deopt_input_data->Pc(i)->value(); 210 if (pc_offset == -1) continue; 211 int translation_index = deopt_input_data->TranslationIndex(i)->value(); 212 TranslationIterator it(deopt_input_data->TranslationByteArray(), 213 translation_index); 214 Translation::Opcode opcode = static_cast<Translation::Opcode>(it.Next()); 215 DCHECK_EQ(Translation::BEGIN, opcode); 216 it.Skip(Translation::NumberOfOperandsFor(opcode)); 217 int depth = 0; 218 std::vector<std::unique_ptr<CodeEntry>> inline_stack; 219 while (it.HasNext() && 220 Translation::BEGIN != 221 (opcode = static_cast<Translation::Opcode>(it.Next()))) { 222 if (opcode != Translation::INTERPRETED_FRAME) { 223 it.Skip(Translation::NumberOfOperandsFor(opcode)); 224 continue; 225 } 226 it.Next(); // Skip ast_id 227 int shared_info_id = it.Next(); 228 it.Next(); // Skip height 229 SharedFunctionInfo* shared_info = SharedFunctionInfo::cast( 230 deopt_input_data->LiteralArray()->get(shared_info_id)); 231 if (!depth++) continue; // Skip the current function itself. 232 233 const char* resource_name = 234 (shared_info->script()->IsScript() && 235 Script::cast(shared_info->script())->name()->IsName()) 236 ? GetName(Name::cast(Script::cast(shared_info->script())->name())) 237 : CodeEntry::kEmptyResourceName; 238 239 CodeEntry* inline_entry = 240 new CodeEntry(entry->tag(), GetName(shared_info->DebugName()), 241 resource_name, CpuProfileNode::kNoLineNumberInfo, 242 CpuProfileNode::kNoColumnNumberInfo, nullptr, 243 code->InstructionStart()); 244 inline_entry->FillFunctionInfo(shared_info); 245 inline_stack.emplace_back(inline_entry); 246 } 247 if (!inline_stack.empty()) { 248 entry->AddInlineStack(pc_offset, std::move(inline_stack)); 249 } 250 } 251 } 252 253 void ProfilerListener::AttachDeoptInlinedFrames(Code* code, 254 CodeDeoptEventRecord* rec) { 255 int deopt_id = rec->deopt_id; 256 SourcePosition last_position = SourcePosition::Unknown(); 257 int mask = RelocInfo::ModeMask(RelocInfo::DEOPT_ID) | 258 RelocInfo::ModeMask(RelocInfo::DEOPT_SCRIPT_OFFSET) | 259 RelocInfo::ModeMask(RelocInfo::DEOPT_INLINING_ID); 260 261 rec->deopt_frames = nullptr; 262 rec->deopt_frame_count = 0; 263 264 for (RelocIterator it(code, mask); !it.done(); it.next()) { 265 RelocInfo* info = it.rinfo(); 266 if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) { 267 int script_offset = static_cast<int>(info->data()); 268 it.next(); 269 DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID); 270 int inlining_id = static_cast<int>(it.rinfo()->data()); 271 last_position = SourcePosition(script_offset, inlining_id); 272 continue; 273 } 274 if (info->rmode() == RelocInfo::DEOPT_ID) { 275 if (deopt_id != static_cast<int>(info->data())) continue; 276 DCHECK(last_position.IsKnown()); 277 278 // SourcePosition::InliningStack allocates a handle for the SFI of each 279 // frame. These don't escape this function, but quickly add up. This 280 // scope limits their lifetime. 281 HandleScope scope(isolate_); 282 std::vector<SourcePositionInfo> stack = 283 last_position.InliningStack(handle(code, isolate_)); 284 CpuProfileDeoptFrame* deopt_frames = 285 new CpuProfileDeoptFrame[stack.size()]; 286 287 int deopt_frame_count = 0; 288 for (SourcePositionInfo& pos_info : stack) { 289 if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue; 290 if (pos_info.script.is_null()) continue; 291 int script_id = pos_info.script->id(); 292 size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset()); 293 deopt_frames[deopt_frame_count++] = {script_id, offset}; 294 } 295 rec->deopt_frames = deopt_frames; 296 rec->deopt_frame_count = deopt_frame_count; 297 break; 298 } 299 } 300 } 301 302 CodeEntry* ProfilerListener::NewCodeEntry( 303 CodeEventListener::LogEventsAndTags tag, const char* name, 304 const char* resource_name, int line_number, int column_number, 305 std::unique_ptr<SourcePositionTable> line_info, Address instruction_start) { 306 return new CodeEntry(tag, name, resource_name, line_number, column_number, 307 std::move(line_info), instruction_start); 308 } 309 310 } // namespace internal 311 } // namespace v8 312