v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
profiler-listener.cc
Go to the documentation of this file.
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
6
7#include <algorithm>
8
9#include "src/base/vector.h"
15#include "src/objects/code.h"
22
23#if V8_ENABLE_WEBASSEMBLY
25#endif // V8_ENABLE_WEBASSEMBLY
26
27namespace v8 {
28namespace internal {
29
31 CodeEventObserver* observer,
32 CodeEntryStorage& code_entry_storage,
33 WeakCodeRegistry& weak_code_registry,
34 CpuProfilingNamingMode naming_mode)
35 : isolate_(isolate),
36 observer_(observer),
37 code_entries_(code_entry_storage),
38 weak_code_registry_(weak_code_registry),
39 naming_mode_(naming_mode) {}
40
42
45 const char* name) {
46 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
47 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
48 PtrComprCageBase cage_base(isolate_);
49 rec->instruction_start = code->InstructionStart(cage_base);
50 rec->entry =
54 rec->instruction_size = code->InstructionSize(cage_base);
56 DispatchCodeEvent(evt_rec);
57}
58
61 DirectHandle<Name> name) {
62 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
63 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
64 PtrComprCageBase cage_base(isolate_);
65 rec->instruction_start = code->InstructionStart(cage_base);
66 rec->entry =
70 rec->instruction_size = code->InstructionSize(cage_base);
72 DispatchCodeEvent(evt_rec);
73}
74
78 DirectHandle<Name> script_name) {
79 PtrComprCageBase cage_base(isolate_);
80 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
81 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
82 rec->instruction_start = code->InstructionStart(cage_base);
83 rec->entry =
84 code_entries_.Create(tag, GetName(shared->DebugNameCStr().get()),
85 GetName(InferScriptName(*script_name, *shared)),
88 rec->entry->FillFunctionInfo(*shared);
89 rec->instruction_size = code->InstructionSize(cage_base);
91 DispatchCodeEvent(evt_rec);
92}
93
94namespace {
95
96CodeEntry* GetOrInsertCachedEntry(
97 std::unordered_set<CodeEntry*, CodeEntry::Hasher, CodeEntry::Equals>*
98 entries,
99 CodeEntry* search_value, CodeEntryStorage& storage) {
100 auto it = entries->find(search_value);
101 if (it != entries->end()) {
102 storage.DecRef(search_value);
103 return *it;
104 }
105 entries->insert(search_value);
106 return search_value;
107}
108
109} // namespace
110
112 DirectHandle<AbstractCode> abstract_code,
114 DirectHandle<Name> script_name, int line,
115 int column) {
116 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
117 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
118 PtrComprCageBase cage_base(isolate_);
119 rec->instruction_start = abstract_code->InstructionStart(cage_base);
120 std::unique_ptr<SourcePositionTable> line_table;
121 std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> inline_stacks;
122 std::unordered_set<CodeEntry*, CodeEntry::Hasher, CodeEntry::Equals>
123 cached_inline_entries;
124 bool is_shared_cross_origin = false;
125 if (IsScript(shared->script(cage_base), cage_base)) {
126 DirectHandle<Script> script(Cast<Script>(shared->script(cage_base)),
127 isolate_);
128 line_table.reset(new SourcePositionTable());
129
130 is_shared_cross_origin = script->origin_options().IsSharedCrossOrigin();
131
132 bool is_baseline = abstract_code->kind(cage_base) == CodeKind::BASELINE;
133 Handle<TrustedByteArray> source_position_table(
134 abstract_code->SourcePositionTable(isolate_, *shared), isolate_);
135 std::unique_ptr<baseline::BytecodeOffsetIterator> baseline_iterator;
136 if (is_baseline) {
137 Handle<BytecodeArray> bytecodes(shared->GetBytecodeArray(isolate_),
138 isolate_);
139 Handle<TrustedByteArray> bytecode_offsets(
140 abstract_code->GetCode()->bytecode_offset_table(), isolate_);
141 baseline_iterator = std::make_unique<baseline::BytecodeOffsetIterator>(
142 bytecode_offsets, bytecodes);
143 }
144 // Add each position to the source position table and store inlining stacks
145 // for inline positions. We store almost the same information in the
146 // profiler as is stored on the code object, except that we transform source
147 // positions to line numbers here, because we only care about attributing
148 // ticks to a given line.
149 for (SourcePositionTableIterator it(source_position_table); !it.done();
150 it.Advance()) {
151 int position = it.source_position().ScriptOffset();
152 int inlining_id = it.source_position().InliningId();
153 int code_offset = it.code_offset();
154 if (is_baseline) {
155 // Use the bytecode offset to calculate pc offset for baseline code.
156 baseline_iterator->AdvanceToBytecodeOffset(code_offset);
157 code_offset =
158 static_cast<int>(baseline_iterator->current_pc_start_offset());
159 }
160
161 if (inlining_id == SourcePosition::kNotInlined) {
162 int line_number = script->GetLineNumber(position) + 1;
163 line_table->SetPosition(code_offset, line_number, inlining_id);
164 } else {
165 DCHECK(!is_baseline);
166 DCHECK(IsCode(*abstract_code, cage_base));
167 std::vector<SourcePositionInfo> stack =
168 it.source_position().InliningStack(isolate_,
169 abstract_code->GetCode());
170 DCHECK(!stack.empty());
171
172 // When we have an inlining id and we are doing cross-script inlining,
173 // then the script of the inlined frames may be different to the script
174 // of |shared|.
175 int line_number = stack.front().line + 1;
176 line_table->SetPosition(code_offset, line_number, inlining_id);
177
178 std::vector<CodeEntryAndLineNumber> inline_stack;
179 for (SourcePositionInfo& pos_info : stack) {
180 if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
181 if (pos_info.script.is_null()) continue;
182
183 line_number =
184 pos_info.script->GetLineNumber(pos_info.position.ScriptOffset()) +
185 1;
186
187 const char* resource_name =
188 (IsName(pos_info.script->name()))
189 ? GetName(Cast<Name>(pos_info.script->name()))
191
192 bool inline_is_shared_cross_origin =
193 pos_info.script->origin_options().IsSharedCrossOrigin();
194
195 // We need the start line number and column number of the function for
196 // kLeafNodeLineNumbers mode. Creating a SourcePositionInfo is a handy
197 // way of getting both easily.
198 SourcePositionInfo start_pos_info(
199 isolate_, SourcePosition(pos_info.shared->StartPosition()),
200 pos_info.shared);
201
202 CodeEntry* inline_entry = code_entries_.Create(
203 tag, GetFunctionName(*pos_info.shared), resource_name,
204 start_pos_info.line + 1, start_pos_info.column + 1, nullptr,
205 inline_is_shared_cross_origin);
206 inline_entry->FillFunctionInfo(*pos_info.shared);
207
208 // Create a canonical CodeEntry for each inlined frame and then reuse
209 // them for subsequent inline stacks to avoid a lot of duplication.
210 CodeEntry* cached_entry = GetOrInsertCachedEntry(
211 &cached_inline_entries, inline_entry, code_entries_);
212
213 inline_stack.push_back({cached_entry, line_number});
214 }
215 DCHECK(!inline_stack.empty());
216 inline_stacks.emplace(inlining_id, std::move(inline_stack));
217 }
218 }
219 }
221 tag, GetFunctionName(*shared),
222 GetName(InferScriptName(*script_name, *shared)), line, column,
223 std::move(line_table), is_shared_cross_origin);
224 if (!inline_stacks.empty()) {
225 rec->entry->SetInlineStacks(std::move(cached_inline_entries),
226 std::move(inline_stacks));
227 }
228
229 rec->entry->FillFunctionInfo(*shared);
230 rec->instruction_size = abstract_code->InstructionSize(cage_base);
231 weak_code_registry_.Track(rec->entry, abstract_code);
232 DispatchCodeEvent(evt_rec);
233}
234
235#if V8_ENABLE_WEBASSEMBLY
236void ProfilerListener::CodeCreateEvent(CodeTag tag, const wasm::WasmCode* code,
237 wasm::WasmName name,
238 const char* source_url, int code_offset,
239 int script_id) {
240 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
241 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
242 rec->instruction_start = code->instruction_start();
243 rec->entry = code_entries_.Create(tag, GetName(name), GetName(source_url), 1,
244 code_offset + 1, nullptr, true,
246 rec->entry->set_script_id(script_id);
247 rec->entry->set_position(code_offset);
248 rec->instruction_size = code->instructions().length();
249 DispatchCodeEvent(evt_rec);
250}
251#endif // V8_ENABLE_WEBASSEMBLY
252
254 Address entry_point) {
255 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
256 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
257 rec->instruction_start = entry_point;
258 rec->entry = code_entries_.Create(LogEventListener::CodeTag::kCallback,
259 GetName(*name));
260 rec->instruction_size = 1;
261 DispatchCodeEvent(evt_rec);
262}
263
265 Address entry_point) {
266 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
267 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
268 rec->instruction_start = entry_point;
269 rec->entry = code_entries_.Create(LogEventListener::CodeTag::kCallback,
270 GetConsName("get ", *name));
271 rec->instruction_size = 1;
272 DispatchCodeEvent(evt_rec);
273}
274
276 Address entry_point) {
277 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
278 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
279 rec->instruction_start = entry_point;
280 rec->entry = code_entries_.Create(LogEventListener::CodeTag::kCallback,
281 GetConsName("set ", *name));
282 rec->instruction_size = 1;
283 DispatchCodeEvent(evt_rec);
284}
285
288 RegExpFlags flags) {
289 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeCreation);
290 CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
291 PtrComprCageBase cage_base(isolate_);
292 rec->instruction_start = code->InstructionStart(cage_base);
294 LogEventListener::CodeTag::kRegExp, GetConsName("RegExp: ", *source),
297 rec->instruction_size = code->InstructionSize(cage_base);
298 weak_code_registry_.Track(rec->entry, code);
299 DispatchCodeEvent(evt_rec);
300}
301
305 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeMove);
306 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
307 rec->from_instruction_start = from->instruction_start();
308 rec->to_instruction_start = to->instruction_start();
309 DispatchCodeEvent(evt_rec);
310}
311
315 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeMove);
316 CodeMoveEventRecord* rec = &evt_rec.CodeMoveEventRecord_;
317 rec->from_instruction_start = from->GetFirstBytecodeAddress();
318 rec->to_instruction_start = to->GetFirstBytecodeAddress();
319 DispatchCodeEvent(evt_rec);
320}
321
323 CodeEventsContainer evt_rec(CodeEventRecord::Type::kNativeContextMove);
324 evt_rec.NativeContextMoveEventRecord_.from_address = from;
325 evt_rec.NativeContextMoveEventRecord_.to_address = to;
326 DispatchCodeEvent(evt_rec);
327}
328
331 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeDisableOpt);
332 CodeDisableOptEventRecord* rec = &evt_rec.CodeDisableOptEventRecord_;
333 PtrComprCageBase cage_base(isolate_);
334 rec->instruction_start = code->InstructionStart(cage_base);
335 rec->bailout_reason =
336 GetBailoutReason(shared->disabled_optimization_reason());
337 DispatchCodeEvent(evt_rec);
338}
339
342 int fp_to_sp_delta) {
343 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeDeopt);
344 CodeDeoptEventRecord* rec = &evt_rec.CodeDeoptEventRecord_;
346 rec->instruction_start = code->instruction_start();
347 rec->deopt_reason = DeoptimizeReasonToString(info.deopt_reason);
348 rec->deopt_id = info.deopt_id;
349 rec->pc = pc;
350 rec->fp_to_sp_delta = fp_to_sp_delta;
351
352 // When a function is deoptimized, we store the deoptimized frame information
353 // for the use of GetDeoptInfos().
354 AttachDeoptInlinedFrames(code, rec);
355 DispatchCodeEvent(evt_rec);
356}
357
359
361 CodeEventsContainer evt_rec(CodeEventRecord::Type::kCodeDelete);
362 evt_rec.CodeDeleteEventRecord_.entry = entry;
363 DispatchCodeEvent(evt_rec);
364}
365
367
369 // TODO(all): Change {StringsStorage} to accept non-null-terminated strings.
370 base::OwnedVector<char> null_terminated =
371 base::OwnedVector<char>::New(name.size() + 1);
372#if defined(__GNUC__) && !defined(__clang__)
373 // Work around a spurious GCC-12 warning (-Werror=array-bounds).
374 if (name.end() < name.begin()) return nullptr;
375#endif
376 std::copy(name.begin(), name.end(), null_terminated.begin());
377 null_terminated[name.size()] = '\0';
378 return GetName(null_terminated.begin());
379}
380
383 if (IsString(name) && Cast<String>(name)->length()) return name;
384 if (!IsScript(info->script())) return name;
385 Tagged<Object> source_url = Cast<Script>(info->script())->source_url();
386 return IsName(source_url) ? Cast<Name>(source_url) : name;
387}
388
391 switch (naming_mode_) {
392 case kDebugNaming:
393 return GetName(shared->DebugNameCStr().get());
394 case kStandardNaming:
395 return GetName(shared->Name());
396 default:
397 UNREACHABLE();
398 }
399}
400
403 int deopt_id = rec->deopt_id;
404 SourcePosition last_position = SourcePosition::Unknown();
408
409 rec->deopt_frames = nullptr;
410 rec->deopt_frame_count = 0;
411
412 for (RelocIterator it(*code, mask); !it.done(); it.next()) {
413 RelocInfo* info = it.rinfo();
414 if (info->rmode() == RelocInfo::DEOPT_SCRIPT_OFFSET) {
415 int script_offset = static_cast<int>(info->data());
416 it.next();
417 DCHECK(it.rinfo()->rmode() == RelocInfo::DEOPT_INLINING_ID);
418 int inlining_id = static_cast<int>(it.rinfo()->data());
419 last_position = SourcePosition(script_offset, inlining_id);
420 continue;
421 }
422 if (info->rmode() == RelocInfo::DEOPT_ID) {
423 if (deopt_id != static_cast<int>(info->data())) continue;
424 DCHECK(last_position.IsKnown());
425
426 // SourcePosition::InliningStack allocates a handle for the SFI of each
427 // frame. These don't escape this function, but quickly add up. This
428 // scope limits their lifetime.
429 HandleScope scope(isolate_);
430 std::vector<SourcePositionInfo> stack =
431 last_position.InliningStack(isolate_, *code);
432 CpuProfileDeoptFrame* deopt_frames =
433 new CpuProfileDeoptFrame[stack.size()];
434
435 int deopt_frame_count = 0;
436 for (SourcePositionInfo& pos_info : stack) {
437 if (pos_info.position.ScriptOffset() == kNoSourcePosition) continue;
438 if (pos_info.script.is_null()) continue;
439 int script_id = pos_info.script->id();
440 size_t offset = static_cast<size_t>(pos_info.position.ScriptOffset());
441 deopt_frames[deopt_frame_count++] = {script_id, offset};
442 }
443 rec->deopt_frames = deopt_frames;
444 rec->deopt_frame_count = deopt_frame_count;
445 break;
446 }
447 }
448}
449
450} // namespace internal
451} // namespace v8
Isolate * isolate_
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
Builtins::Kind kind
Definition builtins.cc:40
static const int kNoColumnNumberInfo
static const int kNoLineNumberInfo
constexpr T * begin() const
Definition vector.h:251
static OwnedVector< T > New(size_t size)
Definition vector.h:287
CpuProfileDeoptFrame * deopt_frames
static CodeEntry * Create(Args &&... args)
void FillFunctionInfo(Tagged< SharedFunctionInfo > shared)
void set_script_id(int script_id)
void SetInlineStacks(std::unordered_set< CodeEntry *, Hasher, Equals > inline_entries, std::unordered_map< int, std::vector< CodeEntryAndLineNumber > > inline_stacks)
void set_position(int position)
static V8_EXPORT_PRIVATE const char *const kEmptyResourceName
DeoptInfo GetDeoptInfo() const
Definition deoptimizer.h:64
void GetterCallbackEvent(DirectHandle< Name > name, Address entry_point) override
void CodeMoveEvent(Tagged< InstructionStream > from, Tagged< InstructionStream > to) override
void CodeDisableOptEvent(DirectHandle< AbstractCode > code, DirectHandle< SharedFunctionInfo > shared) override
void BytecodeMoveEvent(Tagged< BytecodeArray > from, Tagged< BytecodeArray > to) override
void CallbackEvent(DirectHandle< Name > name, Address entry_point) override
void NativeContextMoveEvent(Address from, Address to) override
void SetterCallbackEvent(DirectHandle< Name > name, Address entry_point) override
void CodeCreateEvent(CodeTag tag, DirectHandle< AbstractCode > code, const char *name) override
void RegExpCodeCreateEvent(DirectHandle< AbstractCode > code, DirectHandle< String > source, RegExpFlags flags) override
const CpuProfilingNamingMode naming_mode_
void OnHeapObjectDeletion(CodeEntry *) override
void AttachDeoptInlinedFrames(DirectHandle< Code > code, CodeDeoptEventRecord *rec)
void CodeDeoptEvent(DirectHandle< Code > code, DeoptimizeKind kind, Address pc, int fp_to_sp_delta) override
const char * GetName(Tagged< Name > name)
ProfilerListener(Isolate *, CodeEventObserver *, CodeEntryStorage &code_entry_storage, WeakCodeRegistry &weak_code_registry, CpuProfilingNamingMode mode=kDebugNaming)
const char * GetConsName(const char *prefix, Tagged< Name > name)
V8_INLINE void DispatchCodeEvent(const CodeEventsContainer &evt_rec)
Tagged< Name > InferScriptName(Tagged< Name > name, Tagged< SharedFunctionInfo > info)
const char * GetFunctionName(Tagged< SharedFunctionInfo >)
static constexpr int ModeMask(Mode mode)
Definition reloc-info.h:272
std::vector< SourcePositionInfo > InliningStack(Isolate *isolate, Tagged< Code > code) const
static SourcePosition Unknown()
void Sweep(Listener *listener)
void Track(CodeEntry *entry, DirectHandle< AbstractCode > code)
int32_t offset
ZoneVector< Entry > entries
Point from
Point to
int position
Definition liveedit.cc:290
uint32_t const mask
constexpr int kNoSourcePosition
Definition globals.h:850
char const * DeoptimizeReasonToString(DeoptimizeReason reason)
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
const char * GetBailoutReason(BailoutReason reason)
kInterpreterTrampolineOffset script
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
CpuProfilingNamingMode
@ kStandardNaming
@ kDebugNaming
#define DCHECK(condition)
Definition logging.h:482