v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
perfetto-logger.cc
Go to the documentation of this file.
1// Copyright 2024 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 <memory>
8
9#include "absl/container/flat_hash_map.h"
10#include "protos/perfetto/common/builtin_clock.pbzero.h"
11#include "protos/perfetto/trace/chrome/v8.pbzero.h"
12#include "protos/perfetto/trace/trace_packet.pbzero.h"
13#include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
14#include "src/base/logging.h"
20#include "src/handles/handles.h"
21#include "src/heap/spaces.h"
22#include "src/logging/log.h"
27#include "src/objects/oddball.h"
28#include "src/objects/script.h"
29#include "src/objects/string.h"
30#include "src/objects/tagged.h"
34
35#if V8_ENABLE_WEBASSEMBLY
37#endif // V8_ENABLE_WEBASSEMBLY
38
39namespace v8 {
40namespace internal {
41namespace {
42
43using ::perfetto::protos::pbzero::BuiltinClock;
44using ::perfetto::protos::pbzero::TracePacket;
45using ::perfetto::protos::pbzero::V8InternalCode;
46using ::perfetto::protos::pbzero::V8JsCode;
47
48CodeDataSource::TraceContext::TracePacketHandle NewTracePacket(
49 CodeDataSource::TraceContext& context) {
50 CodeDataSourceIncrementalState* inc_state = context.GetIncrementalState();
51 auto packet = context.NewTracePacket();
52 packet->set_timestamp(base::TimeTicks::Now().since_origin().InNanoseconds());
53
54 if (inc_state->is_initialized()) {
55 packet->set_sequence_flags(TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
56 return packet;
57 }
58
59 inc_state->Init(context);
60
61 packet->set_sequence_flags(TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
62
63 auto* defaults = packet->set_trace_packet_defaults();
64 defaults->set_timestamp_clock_id(BuiltinClock::BUILTIN_CLOCK_MONOTONIC);
65
66 auto* v8_defaults = defaults->set_v8_code_defaults();
67 v8_defaults->set_tid(base::OS::GetCurrentThreadId());
68
69 return packet;
70}
71
72CodeTraceContext NewCodeTraceContext(CodeDataSource::TraceContext& ctx) {
73 return CodeTraceContext(NewTracePacket(ctx), ctx.GetIncrementalState());
74}
75
76class IsolateRegistry {
77 public:
78 static IsolateRegistry& GetInstance() {
79 static IsolateRegistry* g_instance = new IsolateRegistry();
80 return *g_instance;
81 }
82
83 void Register(Isolate* isolate) {
84 auto logger = std::make_unique<PerfettoLogger>(isolate);
85 base::MutexGuard lock(&mutex_);
86 if (num_active_data_sources_ != 0) {
87 isolate->logger()->AddListener(logger.get());
88 }
89 CHECK(isolates_.emplace(isolate, std::move(logger)).second);
90 }
91
92 void Unregister(Isolate* isolate) {
93 base::MutexGuard lock(&mutex_);
94 auto it = isolates_.find(isolate);
95 CHECK(it != isolates_.end());
96 if (num_active_data_sources_ != 0) {
97 isolate->logger()->RemoveListener(it->second.get());
98 }
99 isolates_.erase(it);
100 }
101
102 void OnCodeDataSourceStart() {
103 base::MutexGuard lock(&mutex_);
105 if (num_active_data_sources_ == 1) {
106 StartLogging(lock);
107 }
108 LogExistingCodeForAllIsolates(lock);
109 }
110
111 void OnCodeDataSourceStop() {
112 base::MutexGuard lock(&mutex_);
113 DCHECK_LT(0, num_active_data_sources_);
115 if (num_active_data_sources_ == 0) {
116 StopLogging(lock);
117 }
118 }
119
120 private:
121 void StartLogging(const base::MutexGuard&) {
122 for (const auto& [isolate, logger] : isolates_) {
123 isolate->logger()->AddListener(logger.get());
124 }
125 }
126
127 void StopLogging(const base::MutexGuard&) {
128 for (const auto& [isolate, logger] : isolates_) {
129 isolate->logger()->RemoveListener(logger.get());
130 }
131 }
132
133 void LogExistingCodeForAllIsolates(const base::MutexGuard&) {
134 for (const auto& [isolate, listener] : isolates_) {
135 isolate->RequestInterrupt(
136 [](v8::Isolate*, void* data) {
137 PerfettoLogger* logger = reinterpret_cast<PerfettoLogger*>(data);
138 logger->LogExistingCode();
139 },
140 listener.get());
141 }
142 }
143
144 base::Mutex mutex_;
146 absl::flat_hash_map<Isolate*, std::unique_ptr<PerfettoLogger>> isolates_;
147};
148
149void WriteJsCode(Isolate* isolate, const CodeTraceContext& ctx,
150 Tagged<AbstractCode> abstract_code, V8JsCode& code_proto) {
151 if (IsBytecodeArray(abstract_code)) {
152 Tagged<BytecodeArray> bytecode = abstract_code->GetBytecodeArray();
153 code_proto.set_tier(V8JsCode::TIER_IGNITION);
154 code_proto.set_instruction_start(bytecode->GetFirstBytecodeAddress());
155 code_proto.set_instruction_size_bytes(bytecode->length());
156 if (ctx.log_instructions()) {
157 code_proto.set_bytecode(
158 reinterpret_cast<const uint8_t*>(bytecode->GetFirstBytecodeAddress()),
159 bytecode->length());
160 }
161 return;
162 }
163
164 DCHECK(IsCode(abstract_code));
165 Tagged<Code> code = abstract_code->GetCode();
166
167 V8JsCode::Tier tier = V8JsCode::TIER_UNKNOWN;
168 switch (code->kind()) {
169 case CodeKind::BUILTIN:
170 if (code->builtin_id() == Builtin::kInterpreterEntryTrampoline) {
171 DCHECK(isolate->interpreted_frames_native_stack());
172 DCHECK(code->has_instruction_stream());
173 tier = V8JsCode::TIER_IGNITION;
174 break;
175 }
176
177 // kEmptyFunction is used as a placeholder sometimes.
178 DCHECK_EQ(code->builtin_id(), Builtin::kEmptyFunction);
179 DCHECK(!code->has_instruction_stream());
180 return;
181
182 case CodeKind::INTERPRETED_FUNCTION:
183 // Handled above.
184 UNREACHABLE();
185
186 case CodeKind::BASELINE:
187 tier = V8JsCode::TIER_SPARKPLUG;
188 break;
189 case CodeKind::MAGLEV:
190 tier = V8JsCode::TIER_MAGLEV;
191 break;
192 case CodeKind::TURBOFAN_JS:
193 tier = V8JsCode::TIER_TURBOFAN;
194 break;
195
196 case CodeKind::BYTECODE_HANDLER:
197 case CodeKind::FOR_TESTING:
198 case CodeKind::REGEXP:
199 case CodeKind::WASM_FUNCTION:
200 case CodeKind::WASM_TO_CAPI_FUNCTION:
201 case CodeKind::WASM_TO_JS_FUNCTION:
202 case CodeKind::JS_TO_WASM_FUNCTION:
203 case CodeKind::C_WASM_ENTRY:
204 UNREACHABLE();
205 }
206
207 code_proto.set_tier(tier);
208 code_proto.set_instruction_start(code->instruction_start());
209 code_proto.set_instruction_size_bytes(code->instruction_size());
210 if (ctx.log_instructions()) {
211 code_proto.set_machine_code(
212 reinterpret_cast<const uint8_t*>(code->instruction_start()),
213 code->instruction_size());
214 }
215}
216
217} // namespace
218
219// static
221 IsolateRegistry::GetInstance().Register(isolate);
222 // TODO(carlscab): Actually if both perfetto and file logging are active the
223 // builtins will be logged twice to the file (EmitCodeCreateEvents is called
224 // somewhere in the isolate setup code). Probably not very likely to happen
225 // but we should find a better way.
226 CodeDataSource::CallIfEnabled(
227 [isolate](uint32_t) { Builtins::EmitCodeCreateEvents(isolate); });
228}
229
230// static
232 IsolateRegistry::GetInstance().Unregister(isolate);
233}
234
235// static
237 IsolateRegistry::GetInstance().OnCodeDataSourceStart();
238}
239
240// static
242 IsolateRegistry::GetInstance().OnCodeDataSourceStop();
243}
244
246 HandleScope scope(&isolate_);
247 ExistingCodeLogger logger(&isolate_, this);
248 logger.LogBuiltins();
249 logger.LogCodeObjects();
250 logger.LogCompiledFunctions();
251}
252
255
257 DirectHandle<AbstractCode> abstract_code,
258 const char* name) {
260 if (!IsCode(*abstract_code)) return;
261 Tagged<Code> code = abstract_code->GetCode();
262
263 V8InternalCode::Type type = V8InternalCode::TYPE_UNKNOWN;
264 switch (code->kind()) {
265 case CodeKind::REGEXP:
266 RegExpCodeCreateEvent(abstract_code, DirectHandle<String>(), {});
267 break;
268 case CodeKind::BYTECODE_HANDLER:
269 type = V8InternalCode::TYPE_BYTECODE_HANDLER;
270 break;
271 case CodeKind::FOR_TESTING:
272 type = V8InternalCode::TYPE_FOR_TESTING;
273 break;
274 case CodeKind::BUILTIN:
275 type = V8InternalCode::TYPE_BUILTIN;
276 break;
277 case CodeKind::WASM_FUNCTION:
278 type = V8InternalCode::TYPE_WASM_FUNCTION;
279 break;
280 case CodeKind::WASM_TO_CAPI_FUNCTION:
281 type = V8InternalCode::TYPE_WASM_TO_CAPI_FUNCTION;
282 break;
283 case CodeKind::WASM_TO_JS_FUNCTION:
284 type = V8InternalCode::TYPE_WASM_TO_JS_FUNCTION;
285 break;
286 case CodeKind::JS_TO_WASM_FUNCTION:
287 type = V8InternalCode::TYPE_JS_TO_WASM_FUNCTION;
288 break;
289 case CodeKind::C_WASM_ENTRY:
290 type = V8InternalCode::TYPE_C_WASM_ENTRY;
291 break;
292
293 case CodeKind::INTERPRETED_FUNCTION:
294 case CodeKind::BASELINE:
295 case CodeKind::MAGLEV:
296 case CodeKind::TURBOFAN_JS:
297 UNREACHABLE();
298 }
299
300 CodeDataSource::Trace(
301 [&](v8::internal::CodeDataSource::TraceContext trace_context) {
302 CodeTraceContext ctx = NewCodeTraceContext(trace_context);
303
304 auto* code_proto = ctx.set_v8_internal_code();
305 code_proto->set_v8_isolate_iid(ctx.InternIsolate(isolate_));
306 code_proto->set_name(name);
307 code_proto->set_type(type);
308 if (code->is_builtin()) {
309 code_proto->set_builtin_id(static_cast<int32_t>(code->builtin_id()));
310 }
311 code_proto->set_instruction_start(code->instruction_start());
312 code_proto->set_instruction_size_bytes(code->instruction_size());
313 if (ctx.log_instructions()) {
314 code_proto->set_machine_code(
315 reinterpret_cast<const uint8_t*>(code->instruction_start()),
316 code->instruction_size());
317 }
318 });
319}
320
322 DirectHandle<AbstractCode> abstract_code,
323 DirectHandle<Name> name) {
325 if (!IsString(*name)) return;
326 CodeCreateEvent(tag, abstract_code, Cast<String>(*name)->ToCString().get());
327}
328
330 DirectHandle<AbstractCode> abstract_code,
332 DirectHandle<Name> script_name) {
333 CodeCreateEvent(tag, abstract_code, info, script_name, 0, 0);
334}
335
337 DirectHandle<AbstractCode> abstract_code,
339 DirectHandle<Name> script_name, int line,
340 int column) {
342 DCHECK(IsScript(info->script()));
343
344 CodeDataSource::Trace(
345 [&](v8::internal::CodeDataSource::TraceContext trace_context) {
346 CodeTraceContext ctx = NewCodeTraceContext(trace_context);
347
348 auto* code_proto = ctx.set_v8_js_code();
349 code_proto->set_v8_isolate_iid(ctx.InternIsolate(isolate_));
350 code_proto->set_v8_js_function_iid(ctx.InternJsFunction(
351 isolate_, info,
352 ctx.InternJsScript(isolate_, Cast<Script>(info->script())), line,
353 column));
354 WriteJsCode(&isolate_, ctx, *abstract_code, *code_proto);
355 });
356}
357#if V8_ENABLE_WEBASSEMBLY
358void PerfettoLogger::CodeCreateEvent(CodeTag tag, const wasm::WasmCode* code,
359 wasm::WasmName name,
360 const char* source_url, int code_offset,
361 int script_id) {
363
364 CodeDataSource::Trace(
365 [&](v8::internal::CodeDataSource::TraceContext trace_context) {
366 CodeTraceContext ctx = NewCodeTraceContext(trace_context);
367 auto* code_proto = ctx.set_v8_wasm_code();
368 code_proto->set_v8_isolate_iid(ctx.InternIsolate(isolate_));
369 code_proto->set_v8_wasm_script_iid(ctx.InternWasmScript(
370 isolate_, script_id, source_url, code->native_module()));
371 code_proto->set_function_name(name.begin(), name.size());
372 // TODO(carlscab): Set tier
373 code_proto->set_instruction_start(code->instruction_start());
374 code_proto->set_instruction_size_bytes(code->instructions_size());
375 if (ctx.log_instructions()) {
376 code_proto->set_machine_code(
377 reinterpret_cast<const uint8_t*>(code->instruction_start()),
378 code->instructions_size());
379 }
380 });
381}
382#endif // V8_ENABLE_WEBASSEMBLY
383
392 RegExpFlags flags) {
394 DCHECK(IsCode(*abstract_code));
395 Tagged<Code> code = abstract_code->GetCode();
396 DCHECK(code->kind() == CodeKind::REGEXP);
397
398 CodeDataSource::Trace(
399 [&](v8::internal::CodeDataSource::TraceContext trace_context) {
400 CodeTraceContext ctx = NewCodeTraceContext(trace_context);
401
402 auto* code_proto = ctx.set_v8_reg_exp_code();
403 code_proto->set_v8_isolate_iid(ctx.InternIsolate(isolate_));
404
405 if (!pattern.is_null()) {
406 PerfettoV8String(*pattern).WriteToProto(*code_proto->set_pattern());
407 }
408 code_proto->set_instruction_start(code->instruction_start());
409 code_proto->set_instruction_size_bytes(code->instruction_size());
410 if (ctx.log_instructions()) {
411 code_proto->set_machine_code(
412 reinterpret_cast<const uint8_t*>(code->instruction_start()),
413 code->instruction_size());
414 }
415 });
416}
417
420 CodeDataSource::Trace(
421 [&](v8::internal::CodeDataSource::TraceContext trace_context) {
422 CodeTraceContext ctx = NewCodeTraceContext(trace_context);
423 auto* code_move = ctx.set_code_move();
424 code_move->set_isolate_iid(ctx.InternIsolate(isolate_));
425 code_move->set_from_instruction_start_address(
426 from->instruction_start());
427 code_move->set_to_instruction_start_address(to->instruction_start());
428 Tagged<Code> code = to->code(AcquireLoadTag());
429 code_move->set_instruction_size_bytes(code->instruction_size());
430 if (ctx.log_instructions()) {
431 code_move->set_to_machine_code(
432 reinterpret_cast<const uint8_t*>(code->instruction_start()),
433 code->instruction_size());
434 }
435 });
436}
439 CodeDataSource::Trace(
440 [&](v8::internal::CodeDataSource::TraceContext trace_context) {
441 CodeTraceContext ctx = NewCodeTraceContext(trace_context);
442 auto* code_move = ctx.set_code_move();
443 code_move->set_isolate_iid(ctx.InternIsolate(isolate_));
444 code_move->set_from_instruction_start_address(
445 from->GetFirstBytecodeAddress());
446 code_move->set_to_instruction_start_address(
447 to->GetFirstBytecodeAddress());
448 code_move->set_instruction_size_bytes(to->length());
449 if (ctx.log_instructions()) {
450 code_move->set_to_bytecode(
451 reinterpret_cast<const uint8_t*>(to->GetFirstBytecodeAddress()),
452 to->length());
453 }
454 });
455}
456
469
471
472} // namespace internal
473} // namespace v8
Isolate * isolate_
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
Builtins::Kind kind
Definition builtins.cc:40
static int GetCurrentThreadId()
Definition platform.cc:29
static TimeTicks Now()
Definition time.cc:736
static void EmitCodeCreateEvents(Isolate *isolate)
Definition builtins.cc:401
perfetto::protos::pbzero::V8JsCode * set_v8_js_code()
uint64_t InternJsScript(Isolate &isolate, Tagged< Script > script)
perfetto::protos::pbzero::V8CodeMove * set_code_move()
perfetto::protos::pbzero::V8WasmCode * set_v8_wasm_code()
perfetto::protos::pbzero::V8RegExpCode * set_v8_reg_exp_code()
uint64_t InternIsolate(Isolate &isolate)
perfetto::protos::pbzero::V8InternalCode * set_v8_internal_code()
uint64_t InternJsFunction(Isolate &isolate, DirectHandle< SharedFunctionInfo > info, uint64_t v8_js_script_iid, int line_num, int column_num)
void LogCompiledFunctions(bool ensure_source_positions_available=true)
Definition log.cc:2585
void CodeDeoptEvent(DirectHandle< Code > code, DeoptimizeKind kind, Address pc, int fp_to_sp_delta) override
void BytecodeMoveEvent(Tagged< BytecodeArray > from, Tagged< BytecodeArray > to) override
void SharedFunctionInfoMoveEvent(Address from, Address to) override
void GetterCallbackEvent(DirectHandle< Name > name, Address entry_point) override
void CodeMoveEvent(Tagged< InstructionStream > from, Tagged< InstructionStream > to) override
void RegExpCodeCreateEvent(DirectHandle< AbstractCode > code, DirectHandle< String > source, RegExpFlags flags) override
bool is_listening_to_code_events() override
void CodeDisableOptEvent(DirectHandle< AbstractCode > code, DirectHandle< SharedFunctionInfo > shared) override
static void UnregisterIsolate(Isolate *isolate)
static void RegisterIsolate(Isolate *isolate)
void CodeDependencyChangeEvent(DirectHandle< Code > code, DirectHandle< SharedFunctionInfo > shared, const char *reason) override
void SetterCallbackEvent(DirectHandle< Name > name, Address entry_point) override
void NativeContextMoveEvent(Address from, Address to) override
void CodeCreateEvent(CodeTag tag, DirectHandle< AbstractCode > code, const char *name) override
void CallbackEvent(DirectHandle< Name > name, Address entry_point) override
void WriteToProto(Proto &proto) const
base::Mutex & mutex_
std::string pattern
LockGuard< Mutex > MutexGuard
Definition mutex.h:219
Tagged(T object) -> Tagged< T >
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
absl::flat_hash_map< Isolate *, std::unique_ptr< PerfettoLogger > > isolates_
int num_active_data_sources_
#define CHECK(condition)
Definition logging.h:124
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485