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