52#if V8_ENABLE_WEBASSEMBLY
68 uint32_t elf_mach_target_;
74 static const uint32_t kMagic = 0x4A695444;
92struct PerfJitCodeLoad : PerfJitBase {
96 uint64_t code_address_;
101struct PerfJitDebugEntry {
108struct PerfJitCodeDebugInfo : PerfJitBase {
110 uint64_t entry_count_;
114struct PerfJitCodeUnwindingInfo : PerfJitBase {
115 uint64_t unwinding_size_;
116 uint64_t eh_frame_hdr_size_;
117 uint64_t mapped_size_;
121const char LinuxPerfJitLogger::kFilenameFormatString[] =
"%s/jit-%d.dump";
124const int LinuxPerfJitLogger::kFilenameBufferPadding = 16;
130int LinuxPerfJitLogger::process_id_ = 0;
131uint64_t LinuxPerfJitLogger::reference_count_ = 0;
132void* LinuxPerfJitLogger::marker_address_ =
nullptr;
133uint64_t LinuxPerfJitLogger::code_index_ = 0;
134FILE* LinuxPerfJitLogger::perf_output_handle_ =
nullptr;
136void LinuxPerfJitLogger::OpenJitDumpFile() {
138 perf_output_handle_ =
nullptr;
140 size_t bufferSize = strlen(
v8_flags.perf_prof_path) +
141 sizeof(kFilenameFormatString) + kFilenameBufferPadding;
142 base::ScopedVector<char> perf_dump_name(bufferSize);
143 int size =
SNPrintF(perf_dump_name, kFilenameFormatString,
144 v8_flags.perf_prof_path.value(), process_id_);
147 int fd = open(perf_dump_name.begin(), O_CREAT | O_TRUNC | O_RDWR, 0666);
148 if (fd == -1)
return;
154 CHECK_EQ(0, unlink(perf_dump_name.begin()));
156 marker_address_ = OpenMarkerFile(fd);
157 if (marker_address_ ==
nullptr)
return;
159 perf_output_handle_ = fdopen(fd,
"w+");
160 if (perf_output_handle_ ==
nullptr)
return;
162 setvbuf(perf_output_handle_,
nullptr, _IOFBF, kLogBufferSize);
165void LinuxPerfJitLogger::CloseJitDumpFile() {
166 if (perf_output_handle_ ==
nullptr)
return;
168 perf_output_handle_ =
nullptr;
171void* LinuxPerfJitLogger::OpenMarkerFile(
int fd) {
172 long page_size = sysconf(_SC_PAGESIZE);
173 if (page_size == -1)
return nullptr;
178 void* marker_address =
179 mmap(
nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
180 return (marker_address == MAP_FAILED) ?
nullptr : marker_address;
183void LinuxPerfJitLogger::CloseMarkerFile(
void* marker_address) {
184 if (marker_address ==
nullptr)
return;
185 long page_size = sysconf(_SC_PAGESIZE);
186 if (page_size == -1)
return;
187 munmap(marker_address, page_size);
190LinuxPerfJitLogger::LinuxPerfJitLogger(Isolate* isolate)
191 : CodeEventLogger(isolate) {
192 base::LockGuard<base::RecursiveMutex> guard_file(GetFileMutex().Pointer());
193 process_id_ = base::OS::GetCurrentProcessId();
197 if (reference_count_ == 1) {
199 if (perf_output_handle_ ==
nullptr)
return;
204LinuxPerfJitLogger::~LinuxPerfJitLogger() {
205 base::LockGuard<base::RecursiveMutex> guard_file(GetFileMutex().Pointer());
209 if (reference_count_ == 0) {
214uint64_t LinuxPerfJitLogger::GetTimestamp() {
216 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
219 static const uint64_t kNsecPerSec = 1000000000;
220 return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
223void LinuxPerfJitLogger::LogRecordedBuffer(
224 Tagged<AbstractCode> abstract_code,
225 MaybeDirectHandle<SharedFunctionInfo> maybe_sfi,
const char* name,
228 if (
v8_flags.perf_basic_prof_only_functions) {
235 base::LockGuard<base::RecursiveMutex> guard_file(GetFileMutex().Pointer());
237 if (perf_output_handle_ ==
nullptr)
return;
240 if (!IsCode(abstract_code,
isolate_))
return;
241 Tagged<Code> code = Cast<Code>(abstract_code);
244 DirectHandle<SharedFunctionInfo> sfi;
245 if (
v8_flags.perf_prof && maybe_sfi.ToHandle(&sfi)) {
248 if (
kind != CodeKind::JS_TO_WASM_FUNCTION &&
249 kind != CodeKind::WASM_TO_JS_FUNCTION) {
251 Cast<Script>(sfi->script())->has_line_ends());
252 LogWriteDebugInfo(code, sfi);
256 const char* code_name =
name;
257 uint8_t* code_pointer =
reinterpret_cast<uint8_t*
>(code->instruction_start());
260 if (
v8_flags.perf_prof_unwinding_info) LogWriteUnwindingInfo(code);
262 WriteJitCodeLoadEntry(code_pointer, code->instruction_size(), code_name,
266#if V8_ENABLE_WEBASSEMBLY
267void LinuxPerfJitLogger::LogRecordedBuffer(
const wasm::WasmCode* code,
268 const char* name,
size_t length) {
269 base::LockGuard<base::RecursiveMutex> guard_file(GetFileMutex().Pointer());
271 if (perf_output_handle_ ==
nullptr)
return;
273 if (
v8_flags.perf_prof_annotate_wasm) LogWriteDebugInfo(code);
275 WriteJitCodeLoadEntry(code->instructions().begin(),
276 code->instructions().length(), name, length);
280void LinuxPerfJitLogger::WriteJitCodeLoadEntry(
const uint8_t* code_pointer,
283 size_t name_length) {
284 PerfJitCodeLoad code_load;
285 code_load.event_ = PerfJitCodeLoad::kLoad;
287 static_cast<uint32_t
>(
sizeof(code_load) + name_length + 1 + code_size);
288 code_load.time_stamp_ = GetTimestamp();
289 code_load.process_id_ =
static_cast<uint32_t
>(process_id_);
290 code_load.thread_id_ =
static_cast<uint32_t
>(base::OS::GetCurrentThreadId());
291 code_load.vma_ =
reinterpret_cast<uint64_t
>(code_pointer);
292 code_load.code_address_ =
reinterpret_cast<uint64_t
>(code_pointer);
293 code_load.code_size_ = code_size;
294 code_load.code_id_ = code_index_;
298 LogWriteBytes(
reinterpret_cast<const char*
>(&code_load),
sizeof(code_load));
299 LogWriteBytes(name, name_length);
300 LogWriteBytes(kStringTerminator,
sizeof(kStringTerminator));
301 LogWriteBytes(
reinterpret_cast<const char*
>(code_pointer), code_size);
306constexpr char kUnknownScriptNameString[] =
"<unknown>";
307constexpr size_t kUnknownScriptNameStringLen =
311base::Vector<const char> GetScriptName(Tagged<Object> maybeScript,
312 std::unique_ptr<
char[]>* storage,
313 const DisallowGarbageCollection& no_gc) {
314 if (IsScript(maybeScript)) {
315 Tagged<Object> name_or_url =
316 Cast<Script>(maybeScript)->GetNameOrSourceURL();
317 if (IsSeqOneByteString(name_or_url)) {
318 Tagged<SeqOneByteString> str = Cast<SeqOneByteString>(name_or_url);
319 return {
reinterpret_cast<char*
>(str->GetChars(no_gc)),
320 static_cast<size_t>(str->length())};
321 }
else if (IsString(name_or_url)) {
323 *storage = Cast<String>(name_or_url)->ToCString(&length);
324 return {storage->get(), length};
327 return {kUnknownScriptNameString, kUnknownScriptNameStringLen};
332SourcePositionInfo GetSourcePositionInfo(
333 Isolate* isolate, Tagged<Code> code,
334 DirectHandle<SharedFunctionInfo> function, SourcePosition
pos) {
336 if (code->is_turbofanned()) {
337 return pos.FirstInfo(isolate, code);
339 return SourcePositionInfo(isolate,
pos, function);
345void LinuxPerfJitLogger::LogWriteDebugInfo(
346 Tagged<Code> code, DirectHandle<SharedFunctionInfo> shared) {
350 Tagged<SharedFunctionInfo> raw_shared = *
shared;
351 if (!raw_shared->HasSourceCode())
return;
353 PerfJitCodeDebugInfo debug_info;
354 uint32_t size =
sizeof(debug_info);
356 Tagged<TrustedByteArray> source_position_table =
357 code->SourcePositionTable(
isolate_, raw_shared);
361 uint32_t entry_count = 0;
362 Tagged<Object> last_script = Smi::zero();
363 size_t last_script_name_size = 0;
364 std::vector<base::Vector<const char>> script_names;
365 for (SourcePositionTableIterator iterator(source_position_table);
366 !iterator.done(); iterator.Advance()) {
367 SourcePositionInfo
info(GetSourcePositionInfo(
isolate_, code, shared,
368 iterator.source_position()));
369 Tagged<Object> current_script = *info.script;
370 if (current_script != last_script) {
371 std::unique_ptr<char[]> name_storage;
372 auto name = GetScriptName(raw_shared->script(), &name_storage, no_gc);
373 script_names.push_back(name);
376 size += last_script_name_size;
377 last_script = current_script;
380 size += last_script_name_size;
384 if (entry_count == 0)
return;
386 debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
387 debug_info.time_stamp_ = GetTimestamp();
388 debug_info.address_ = code->instruction_start();
389 debug_info.entry_count_ = entry_count;
392 size += entry_count *
sizeof(PerfJitDebugEntry);
394 int padding = ((size + 7) & (~7)) - size;
395 debug_info.size_ = size + padding;
396 LogWriteBytes(
reinterpret_cast<const char*
>(&debug_info),
sizeof(debug_info));
398 Address code_start = code->instruction_start();
400 last_script = Smi::zero();
401 int script_names_index = 0;
402 for (SourcePositionTableIterator iterator(source_position_table);
403 !iterator.done(); iterator.Advance()) {
404 SourcePositionInfo
info(GetSourcePositionInfo(
isolate_, code, shared,
405 iterator.source_position()));
406 PerfJitDebugEntry entry;
410 entry.address_ = code_start + iterator.code_offset() + kElfHeaderSize;
411 entry.line_number_ = info.line + 1;
412 entry.column_ = info.column + 1;
413 LogWriteBytes(
reinterpret_cast<const char*
>(&entry),
sizeof(entry));
414 Tagged<Object> current_script = *info.script;
415 auto name_string = script_names[script_names_index];
416 LogWriteBytes(name_string.begin(), name_string.size());
417 LogWriteBytes(kStringTerminator,
sizeof(kStringTerminator));
418 if (current_script != last_script) {
419 if (last_script != Smi::zero()) script_names_index++;
420 last_script = current_script;
423 char padding_bytes[8] = {0};
424 LogWriteBytes(padding_bytes, padding);
427#if V8_ENABLE_WEBASSEMBLY
428void LinuxPerfJitLogger::LogWriteDebugInfo(
const wasm::WasmCode* code) {
429 if (code->IsAnonymous()) {
433 wasm::WasmModuleSourceMap* source_map =
434 code->native_module()->GetWasmSourceMap();
435 wasm::WireBytesRef code_ref =
436 code->native_module()->module()->functions[code->index()].code;
437 uint32_t code_offset = code_ref.offset();
438 uint32_t code_end_offset = code_ref.end_offset();
440 uint32_t entry_count = 0;
443 if (!source_map || !source_map->IsValid() ||
444 !source_map->HasSource(code_offset, code_end_offset)) {
448 for (SourcePositionTableIterator iterator(code->source_positions());
449 !iterator.done(); iterator.Advance()) {
450 uint32_t
offset = iterator.source_position().ScriptOffset() + code_offset;
451 if (!source_map->HasValidEntry(code_offset,
offset))
continue;
453 size += source_map->GetFilename(
offset).size() + 1;
456 if (entry_count == 0)
return;
458 PerfJitCodeDebugInfo debug_info;
460 debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
461 debug_info.time_stamp_ = GetTimestamp();
462 debug_info.address_ =
463 reinterpret_cast<uintptr_t
>(code->instructions().
begin());
464 debug_info.entry_count_ = entry_count;
466 size +=
sizeof(debug_info);
468 size += entry_count *
sizeof(PerfJitDebugEntry);
470 int padding = ((size + 7) & (~7)) - size;
471 debug_info.size_ = size + padding;
472 LogWriteBytes(
reinterpret_cast<const char*
>(&debug_info),
sizeof(debug_info));
474 uintptr_t code_begin =
475 reinterpret_cast<uintptr_t
>(code->instructions().
begin());
477 for (SourcePositionTableIterator iterator(code->source_positions());
478 !iterator.done(); iterator.Advance()) {
479 uint32_t
offset = iterator.source_position().ScriptOffset() + code_offset;
480 if (!source_map->HasValidEntry(code_offset,
offset))
continue;
481 PerfJitDebugEntry entry;
485 entry.address_ = code_begin + iterator.code_offset() + kElfHeaderSize;
487 static_cast<int>(source_map->GetSourceLine(
offset)) + 1;
489 LogWriteBytes(
reinterpret_cast<const char*
>(&entry),
sizeof(entry));
490 std::string name_string = source_map->GetFilename(
offset);
491 LogWriteBytes(name_string.c_str(), name_string.size());
492 LogWriteBytes(kStringTerminator,
sizeof(kStringTerminator));
495 char padding_bytes[8] = {0};
496 LogWriteBytes(padding_bytes, padding);
500void LinuxPerfJitLogger::LogWriteUnwindingInfo(Tagged<Code> code) {
501 PerfJitCodeUnwindingInfo unwinding_info_header;
502 unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
503 unwinding_info_header.time_stamp_ = GetTimestamp();
504 unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize;
506 if (code->has_unwinding_info()) {
507 unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
508 unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
510 unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize;
511 unwinding_info_header.mapped_size_ = 0;
514 int content_size =
static_cast<int>(
sizeof(unwinding_info_header) +
515 unwinding_info_header.unwinding_size_);
516 int padding_size =
RoundUp(content_size, 8) - content_size;
517 unwinding_info_header.size_ = content_size + padding_size;
519 LogWriteBytes(
reinterpret_cast<const char*
>(&unwinding_info_header),
520 sizeof(unwinding_info_header));
522 if (code->has_unwinding_info()) {
523 LogWriteBytes(
reinterpret_cast<const char*
>(code->unwinding_info_start()),
524 code->unwinding_info_size());
526 OFStream perf_output_stream(perf_output_handle_);
527 EhFrameWriter::WriteEmptyEhFrame(perf_output_stream);
530 char padding_bytes[] =
"\0\0\0\0\0\0\0\0";
531 DCHECK_LT(padding_size,
static_cast<int>(
sizeof(padding_bytes)));
532 LogWriteBytes(padding_bytes, padding_size);
535void LinuxPerfJitLogger::LogWriteBytes(
const char* bytes,
size_t size) {
536 size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
541void LinuxPerfJitLogger::LogWriteHeader() {
543 PerfJitHeader header;
545 header.magic_ = PerfJitHeader::kMagic;
546 header.version_ = PerfJitHeader::kVersion;
547 header.size_ =
sizeof(header);
548 header.elf_mach_target_ = GetElfMach();
549 header.reserved_ = 0xDEADBEEF;
550 header.process_id_ = process_id_;
551 header.time_stamp_ =
static_cast<uint64_t
>(
552 V8::GetCurrentPlatform()->CurrentClockTimeMillisecondsHighResolution() *
553 base::Time::kMicrosecondsPerMillisecond);
556 LogWriteBytes(
reinterpret_cast<const char*
>(&header),
sizeof(header));
Handle< SharedFunctionInfo > info
SharedFunctionInfoRef shared
ZoneVector< RpoNumber > & result
LazyStaticInstance< RecursiveMutex, DefaultConstructTrait< RecursiveMutex >, ThreadSafeInitOnceTrait >::type LazyRecursiveMutex
int SNPrintF(Vector< char > str, const char *format,...)
Node::Uses::const_iterator begin(const Node::Uses &uses)
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
V8_EXPORT_PRIVATE FlagValues v8_flags
constexpr bool CodeKindIsJSFunction(CodeKind kind)
refactor address components for immediate indexing make OptimizeMaglevOnNextCall optimize to turbofan instead of maglev filter for tracing turbofan compilation nullptr
#define DCHECK_NOT_NULL(val)
#define DCHECK_IMPLIES(v1, v2)
#define CHECK_NE(lhs, rhs)
#define CHECK_EQ(lhs, rhs)
#define DCHECK_LT(v1, v2)
#define DCHECK_EQ(v1, v2)
constexpr T RoundUp(T x, intptr_t m)