• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 namespace v8 {
14 namespace internal {
15 
ProfilerListener(Isolate * isolate)16 ProfilerListener::ProfilerListener(Isolate* isolate)
17     : function_and_resource_names_(isolate->heap()) {}
18 
~ProfilerListener()19 ProfilerListener::~ProfilerListener() {
20   for (auto code_entry : code_entries_) {
21     delete code_entry;
22   }
23 }
24 
CallbackEvent(Name * name,Address entry_point)25 void 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 
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,const char * name)34 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   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 
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,Name * name)48 void 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 
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,SharedFunctionInfo * shared,Name * script_name)62 void 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 
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * abstract_code,SharedFunctionInfo * shared,Name * script_name,int line,int column)80 void 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 
CodeCreateEvent(CodeEventListener::LogEventsAndTags tag,AbstractCode * code,int args_count)117 void 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 
CodeMoveEvent(AbstractCode * from,Address to)131 void 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 
CodeDisableOptEvent(AbstractCode * code,SharedFunctionInfo * shared)139 void 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 
CodeDeoptEvent(Code * code,Address pc,int fp_to_sp_delta)148 void 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 
GetterCallbackEvent(Name * name,Address entry_point)161 void 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 
RegExpCodeCreateEvent(AbstractCode * code,String * source)171 void 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 
SetterCallbackEvent(Name * name,Address entry_point)184 void 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 
InferScriptName(Name * name,SharedFunctionInfo * info)194 Name* 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 
RecordInliningInfo(CodeEntry * entry,AbstractCode * abstract_code)201 void 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 
RecordDeoptInlinedFrames(CodeEntry * entry,AbstractCode * abstract_code)248 void 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 
NewCodeEntry(CodeEventListener::LogEventsAndTags tag,const char * name,const char * name_prefix,const char * resource_name,int line_number,int column_number,JITLineInfoTable * line_info,Address instruction_start)286 CodeEntry* 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 
AddObserver(CodeEventObserver * observer)297 void 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 
RemoveObserver(CodeEventObserver * observer)305 void 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