28 #include "src/perf-jit.h" 32 #include "src/assembler.h" 33 #include "src/eh-frame.h" 34 #include "src/objects-inl.h" 35 #include "src/snapshot/embedded-data.h" 36 #include "src/source-position-table.h" 37 #include "src/wasm/wasm-code-manager.h" 42 #undef MAP_TYPE // jumbo: conflicts with v8::internal::InstanceType::MAP_TYPE 51 struct PerfJitHeader {
61 static const uint32_t kMagic = 0x4A695444;
79 struct PerfJitCodeLoad : PerfJitBase {
83 uint64_t code_address_;
88 struct PerfJitDebugEntry {
95 struct PerfJitCodeDebugInfo : PerfJitBase {
97 uint64_t entry_count_;
101 struct PerfJitCodeUnwindingInfo : PerfJitBase {
102 uint64_t unwinding_size_;
103 uint64_t eh_frame_hdr_size_;
104 uint64_t mapped_size_;
108 const char PerfJitLogger::kFilenameFormatString[] =
"./jit-%d.dump";
111 const int PerfJitLogger::kFilenameBufferPadding = 16;
113 base::LazyRecursiveMutex PerfJitLogger::file_mutex_;
115 uint64_t PerfJitLogger::reference_count_ = 0;
116 void* PerfJitLogger::marker_address_ =
nullptr;
117 uint64_t PerfJitLogger::code_index_ = 0;
118 FILE* PerfJitLogger::perf_output_handle_ =
nullptr;
120 void PerfJitLogger::OpenJitDumpFile() {
122 perf_output_handle_ =
nullptr;
124 int bufferSize =
sizeof(kFilenameFormatString) + kFilenameBufferPadding;
125 ScopedVector<char> perf_dump_name(bufferSize);
126 int size = SNPrintF(perf_dump_name, kFilenameFormatString,
127 base::OS::GetCurrentProcessId());
130 int fd = open(perf_dump_name.start(), O_CREAT | O_TRUNC | O_RDWR, 0666);
131 if (fd == -1)
return;
133 marker_address_ = OpenMarkerFile(fd);
134 if (marker_address_ ==
nullptr)
return;
136 perf_output_handle_ = fdopen(fd,
"w+");
137 if (perf_output_handle_ ==
nullptr)
return;
139 setvbuf(perf_output_handle_,
nullptr, _IOFBF, kLogBufferSize);
142 void PerfJitLogger::CloseJitDumpFile() {
143 if (perf_output_handle_ ==
nullptr)
return;
144 fclose(perf_output_handle_);
145 perf_output_handle_ =
nullptr;
148 void* PerfJitLogger::OpenMarkerFile(
int fd) {
149 long page_size = sysconf(_SC_PAGESIZE);
150 if (page_size == -1)
return nullptr;
155 void* marker_address =
156 mmap(
nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
157 return (marker_address == MAP_FAILED) ? nullptr : marker_address;
160 void PerfJitLogger::CloseMarkerFile(
void* marker_address) {
161 if (marker_address ==
nullptr)
return;
162 long page_size = sysconf(_SC_PAGESIZE);
163 if (page_size == -1)
return;
164 munmap(marker_address, page_size);
167 PerfJitLogger::PerfJitLogger(Isolate* isolate) : CodeEventLogger(isolate) {
168 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
172 if (reference_count_ == 1) {
174 if (perf_output_handle_ ==
nullptr)
return;
179 PerfJitLogger::~PerfJitLogger() {
180 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
184 if (reference_count_ == 0) {
189 uint64_t PerfJitLogger::GetTimestamp() {
191 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
192 DCHECK_EQ(0, result);
194 static const uint64_t kNsecPerSec = 1000000000;
195 return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
198 void PerfJitLogger::LogRecordedBuffer(AbstractCode abstract_code,
199 SharedFunctionInfo* shared,
200 const char* name,
int length) {
201 if (FLAG_perf_basic_prof_only_functions &&
202 (abstract_code->kind() != AbstractCode::INTERPRETED_FUNCTION &&
203 abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) {
207 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
209 if (perf_output_handle_ ==
nullptr)
return;
212 if (!abstract_code->IsCode())
return;
213 Code code = abstract_code->GetCode();
214 DCHECK(code->raw_instruction_start() == code->address() + Code::kHeaderSize);
217 if (FLAG_perf_prof && shared !=
nullptr) {
219 if (code->kind() != Code::JS_TO_WASM_FUNCTION &&
220 code->kind() != Code::WASM_TO_JS_FUNCTION) {
221 LogWriteDebugInfo(code, shared);
225 const char* code_name = name;
226 uint8_t* code_pointer =
reinterpret_cast<uint8_t*
>(code->InstructionStart());
229 uint32_t code_size = code->is_turbofanned() ? code->safepoint_table_offset()
230 : code->InstructionSize();
233 if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(code);
235 WriteJitCodeLoadEntry(code_pointer, code_size, code_name, length);
238 void PerfJitLogger::LogRecordedBuffer(
const wasm::WasmCode* code,
239 const char* name,
int length) {
240 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
242 if (perf_output_handle_ ==
nullptr)
return;
244 WriteJitCodeLoadEntry(code->instructions().start(),
245 code->instructions().length(), name, length);
248 void PerfJitLogger::WriteJitCodeLoadEntry(
const uint8_t* code_pointer,
249 uint32_t code_size,
const char* name,
251 static const char string_terminator[] =
"\0";
253 PerfJitCodeLoad code_load;
254 code_load.event_ = PerfJitCodeLoad::kLoad;
255 code_load.size_ =
sizeof(code_load) + name_length + 1 + code_size;
256 code_load.time_stamp_ = GetTimestamp();
257 code_load.process_id_ =
258 static_cast<uint32_t>(base::OS::GetCurrentProcessId());
259 code_load.thread_id_ =
static_cast<uint32_t>(base::OS::GetCurrentThreadId());
260 code_load.vma_ =
reinterpret_cast<uint64_t
>(code_pointer);
261 code_load.code_address_ =
reinterpret_cast<uint64_t
>(code_pointer);
262 code_load.code_size_ = code_size;
263 code_load.code_id_ = code_index_;
267 LogWriteBytes(reinterpret_cast<const char*>(&code_load),
sizeof(code_load));
268 LogWriteBytes(name, name_length);
269 LogWriteBytes(string_terminator, 1);
270 LogWriteBytes(reinterpret_cast<const char*>(code_pointer), code_size);
275 constexpr
char kUnknownScriptNameString[] =
"<unknown>";
276 constexpr
size_t kUnknownScriptNameStringLen =
277 arraysize(kUnknownScriptNameString) - 1;
279 size_t GetScriptNameLength(
const SourcePositionInfo& info) {
280 if (!info.script.is_null()) {
281 Object* name_or_url = info.script->GetNameOrSourceURL();
282 if (name_or_url->IsString()) {
283 String str = String::cast(name_or_url);
284 if (str->IsOneByteRepresentation())
return str->length();
286 str->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &length);
287 return static_cast<size_t>(length);
290 return kUnknownScriptNameStringLen;
293 Vector<const char> GetScriptName(
const SourcePositionInfo& info,
294 std::unique_ptr<
char[]>* storage) {
295 if (!info.script.is_null()) {
296 Object* name_or_url = info.script->GetNameOrSourceURL();
297 if (name_or_url->IsSeqOneByteString()) {
298 SeqOneByteString str = SeqOneByteString::cast(name_or_url);
299 return {
reinterpret_cast<char*
>(str->GetChars()),
300 static_cast<size_t>(str->length())};
301 }
else if (name_or_url->IsString()) {
304 String::cast(name_or_url)
305 ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &length);
306 return {storage->get(),
static_cast<size_t>(length)};
309 return {kUnknownScriptNameString, kUnknownScriptNameStringLen};
312 SourcePositionInfo GetSourcePositionInfo(Handle<Code> code,
313 Handle<SharedFunctionInfo>
function,
314 SourcePosition pos) {
315 if (code->is_turbofanned()) {
316 DisallowHeapAllocation disallow;
317 return pos.InliningStack(code)[0];
319 return SourcePositionInfo(pos,
function);
325 void PerfJitLogger::LogWriteDebugInfo(Code code, SharedFunctionInfo* shared) {
328 for (SourcePositionTableIterator iterator(code->SourcePositionTable());
329 !iterator.done(); iterator.Advance()) {
332 if (entry_count == 0)
return;
334 if (!shared->HasSourceCode())
return;
335 Isolate* isolate = shared->GetIsolate();
336 Handle<Script> script(Script::cast(shared->script()), isolate);
338 PerfJitCodeDebugInfo debug_info;
340 debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
341 debug_info.time_stamp_ = GetTimestamp();
342 debug_info.address_ = code->InstructionStart();
343 debug_info.entry_count_ = entry_count;
347 size += entry_count *
sizeof(PerfJitDebugEntry);
350 Handle<Code> code_handle(code, isolate);
351 Handle<SharedFunctionInfo> function_handle(shared, isolate);
352 for (SourcePositionTableIterator iterator(code->SourcePositionTable());
353 !iterator.done(); iterator.Advance()) {
354 SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle,
355 iterator.source_position()));
356 size += GetScriptNameLength(info) + 1;
359 int padding = ((size + 7) & (~7)) - size;
360 debug_info.size_ = size + padding;
361 LogWriteBytes(reinterpret_cast<const char*>(&debug_info),
sizeof(debug_info));
363 Address code_start = code->InstructionStart();
365 for (SourcePositionTableIterator iterator(code->SourcePositionTable());
366 !iterator.done(); iterator.Advance()) {
367 SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle,
368 iterator.source_position()));
369 PerfJitDebugEntry entry;
373 entry.address_ = code_start + iterator.code_offset() + kElfHeaderSize;
374 entry.line_number_ = info.line + 1;
375 entry.column_ = info.column + 1;
376 LogWriteBytes(reinterpret_cast<const char*>(&entry),
sizeof(entry));
378 DisallowHeapAllocation no_gc;
379 std::unique_ptr<char[]> name_storage;
380 Vector<const char> name_string = GetScriptName(info, &name_storage);
381 LogWriteBytes(name_string.start(),
382 static_cast<uint32_t>(name_string.size()) + 1);
384 char padding_bytes[8] = {0};
385 LogWriteBytes(padding_bytes, padding);
388 void PerfJitLogger::LogWriteUnwindingInfo(Code code) {
389 PerfJitCodeUnwindingInfo unwinding_info_header;
390 unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
391 unwinding_info_header.time_stamp_ = GetTimestamp();
392 unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize;
394 if (code->has_unwinding_info()) {
395 unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
396 unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
398 unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize;
399 unwinding_info_header.mapped_size_ = 0;
402 int content_size =
static_cast<int>(
sizeof(unwinding_info_header) +
403 unwinding_info_header.unwinding_size_);
404 int padding_size = RoundUp(content_size, 8) - content_size;
405 unwinding_info_header.size_ = content_size + padding_size;
407 LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
408 sizeof(unwinding_info_header));
410 if (code->has_unwinding_info()) {
411 LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()),
412 code->unwinding_info_size());
414 OFStream perf_output_stream(perf_output_handle_);
415 EhFrameWriter::WriteEmptyEhFrame(perf_output_stream);
418 char padding_bytes[] =
"\0\0\0\0\0\0\0\0";
419 DCHECK_LT(padding_size, static_cast<int>(
sizeof(padding_bytes)));
420 LogWriteBytes(padding_bytes, static_cast<int>(padding_size));
423 void PerfJitLogger::CodeMoveEvent(AbstractCode from, AbstractCode to) {
426 CHECK(from->IsBytecodeArray());
429 void PerfJitLogger::LogWriteBytes(
const char* bytes,
int size) {
430 size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
431 DCHECK(static_cast<size_t>(size) == rv);
435 void PerfJitLogger::LogWriteHeader() {
436 DCHECK_NOT_NULL(perf_output_handle_);
437 PerfJitHeader header;
439 header.magic_ = PerfJitHeader::kMagic;
440 header.version_ = PerfJitHeader::kVersion;
441 header.size_ =
sizeof(header);
442 header.elf_mach_target_ = GetElfMach();
443 header.reserved_ = 0xDEADBEEF;
444 header.process_id_ = base::OS::GetCurrentProcessId();
446 static_cast<uint64_t
>(V8::GetCurrentPlatform()->CurrentClockTimeMillis() *
447 base::Time::kMicrosecondsPerMillisecond);
450 LogWriteBytes(reinterpret_cast<const char*>(&header),
sizeof(header));
453 #endif // V8_OS_LINUX