1 // Copyright 2016 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "src/perf-jit.h" 29 30 #include <memory> 31 32 #include "src/assembler.h" 33 #include "src/eh-frame.h" 34 #include "src/objects-inl.h" 35 #include "src/source-position-table.h" 36 37 #if V8_OS_LINUX 38 #include <fcntl.h> 39 #include <sys/mman.h> 40 #include <unistd.h> 41 #endif // V8_OS_LINUX 42 43 namespace v8 { 44 namespace internal { 45 46 #if V8_OS_LINUX 47 48 struct PerfJitHeader { 49 uint32_t magic_; 50 uint32_t version_; 51 uint32_t size_; 52 uint32_t elf_mach_target_; 53 uint32_t reserved_; 54 uint32_t process_id_; 55 uint64_t time_stamp_; 56 uint64_t flags_; 57 58 static const uint32_t kMagic = 0x4A695444; 59 static const uint32_t kVersion = 1; 60 }; 61 62 struct PerfJitBase { 63 enum PerfJitEvent { 64 kLoad = 0, 65 kMove = 1, 66 kDebugInfo = 2, 67 kClose = 3, 68 kUnwindingInfo = 4 69 }; 70 71 uint32_t event_; 72 uint32_t size_; 73 uint64_t time_stamp_; 74 }; 75 76 struct PerfJitCodeLoad : PerfJitBase { 77 uint32_t process_id_; 78 uint32_t thread_id_; 79 uint64_t vma_; 80 uint64_t code_address_; 81 uint64_t code_size_; 82 uint64_t code_id_; 83 }; 84 85 struct PerfJitDebugEntry { 86 uint64_t address_; 87 int line_number_; 88 int column_; 89 // Followed by null-terminated name or \0xff\0 if same as previous. 90 }; 91 92 struct PerfJitCodeDebugInfo : PerfJitBase { 93 uint64_t address_; 94 uint64_t entry_count_; 95 // Followed by entry_count_ instances of PerfJitDebugEntry. 96 }; 97 98 struct PerfJitCodeUnwindingInfo : PerfJitBase { 99 uint64_t unwinding_size_; 100 uint64_t eh_frame_hdr_size_; 101 uint64_t mapped_size_; 102 // Followed by size_ - sizeof(PerfJitCodeUnwindingInfo) bytes of data. 103 }; 104 105 const char PerfJitLogger::kFilenameFormatString[] = "./jit-%d.dump"; 106 107 // Extra padding for the PID in the filename 108 const int PerfJitLogger::kFilenameBufferPadding = 16; 109 110 base::LazyRecursiveMutex PerfJitLogger::file_mutex_; 111 // The following static variables are protected by PerfJitLogger::file_mutex_. 112 uint64_t PerfJitLogger::reference_count_ = 0; 113 void* PerfJitLogger::marker_address_ = nullptr; 114 uint64_t PerfJitLogger::code_index_ = 0; 115 FILE* PerfJitLogger::perf_output_handle_ = nullptr; 116 117 void PerfJitLogger::OpenJitDumpFile() { 118 // Open the perf JIT dump file. 119 perf_output_handle_ = nullptr; 120 121 int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding; 122 ScopedVector<char> perf_dump_name(bufferSize); 123 int size = SNPrintF(perf_dump_name, kFilenameFormatString, 124 base::OS::GetCurrentProcessId()); 125 CHECK_NE(size, -1); 126 127 int fd = open(perf_dump_name.start(), O_CREAT | O_TRUNC | O_RDWR, 0666); 128 if (fd == -1) return; 129 130 marker_address_ = OpenMarkerFile(fd); 131 if (marker_address_ == nullptr) return; 132 133 perf_output_handle_ = fdopen(fd, "w+"); 134 if (perf_output_handle_ == nullptr) return; 135 136 setvbuf(perf_output_handle_, NULL, _IOFBF, kLogBufferSize); 137 } 138 139 void PerfJitLogger::CloseJitDumpFile() { 140 if (perf_output_handle_ == nullptr) return; 141 fclose(perf_output_handle_); 142 perf_output_handle_ = nullptr; 143 } 144 145 void* PerfJitLogger::OpenMarkerFile(int fd) { 146 long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) 147 if (page_size == -1) return nullptr; 148 149 // Mmap the file so that there is a mmap record in the perf_data file. 150 // 151 // The map must be PROT_EXEC to ensure it is not ignored by perf record. 152 void* marker_address = 153 mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0); 154 return (marker_address == MAP_FAILED) ? nullptr : marker_address; 155 } 156 157 void PerfJitLogger::CloseMarkerFile(void* marker_address) { 158 if (marker_address == nullptr) return; 159 long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int) 160 if (page_size == -1) return; 161 munmap(marker_address, page_size); 162 } 163 164 PerfJitLogger::PerfJitLogger() { 165 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer()); 166 167 reference_count_++; 168 // If this is the first logger, open the file and write the header. 169 if (reference_count_ == 1) { 170 OpenJitDumpFile(); 171 if (perf_output_handle_ == nullptr) return; 172 LogWriteHeader(); 173 } 174 } 175 176 PerfJitLogger::~PerfJitLogger() { 177 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer()); 178 179 reference_count_--; 180 // If this was the last logger, close the file. 181 if (reference_count_ == 0) { 182 CloseJitDumpFile(); 183 } 184 } 185 186 uint64_t PerfJitLogger::GetTimestamp() { 187 struct timespec ts; 188 int result = clock_gettime(CLOCK_MONOTONIC, &ts); 189 DCHECK_EQ(0, result); 190 USE(result); 191 static const uint64_t kNsecPerSec = 1000000000; 192 return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec; 193 } 194 195 void PerfJitLogger::LogRecordedBuffer(AbstractCode* abstract_code, 196 SharedFunctionInfo* shared, 197 const char* name, int length) { 198 if (FLAG_perf_basic_prof_only_functions && 199 (abstract_code->kind() != AbstractCode::FUNCTION && 200 abstract_code->kind() != AbstractCode::INTERPRETED_FUNCTION && 201 abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) { 202 return; 203 } 204 205 base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer()); 206 207 if (perf_output_handle_ == nullptr) return; 208 209 // We only support non-interpreted functions. 210 if (!abstract_code->IsCode()) return; 211 Code* code = abstract_code->GetCode(); 212 DCHECK(code->instruction_start() == code->address() + Code::kHeaderSize); 213 214 // Debug info has to be emitted first. 215 if (FLAG_perf_prof && shared != nullptr) { 216 LogWriteDebugInfo(code, shared); 217 } 218 219 const char* code_name = name; 220 uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code->instruction_start()); 221 uint32_t code_size = code->is_crankshafted() ? code->safepoint_table_offset() 222 : code->instruction_size(); 223 224 // Unwinding info comes right after debug info. 225 if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(code); 226 227 static const char string_terminator[] = "\0"; 228 229 PerfJitCodeLoad code_load; 230 code_load.event_ = PerfJitCodeLoad::kLoad; 231 code_load.size_ = sizeof(code_load) + length + 1 + code_size; 232 code_load.time_stamp_ = GetTimestamp(); 233 code_load.process_id_ = 234 static_cast<uint32_t>(base::OS::GetCurrentProcessId()); 235 code_load.thread_id_ = static_cast<uint32_t>(base::OS::GetCurrentThreadId()); 236 code_load.vma_ = 0x0; // Our addresses are absolute. 237 code_load.code_address_ = reinterpret_cast<uint64_t>(code_pointer); 238 code_load.code_size_ = code_size; 239 code_load.code_id_ = code_index_; 240 241 code_index_++; 242 243 LogWriteBytes(reinterpret_cast<const char*>(&code_load), sizeof(code_load)); 244 LogWriteBytes(code_name, length); 245 LogWriteBytes(string_terminator, 1); 246 LogWriteBytes(reinterpret_cast<const char*>(code_pointer), code_size); 247 } 248 249 namespace { 250 251 std::unique_ptr<char[]> GetScriptName(Handle<Script> script) { 252 Object* name_or_url = script->GetNameOrSourceURL(); 253 int name_length = 0; 254 std::unique_ptr<char[]> name_string; 255 if (name_or_url->IsString()) { 256 return String::cast(name_or_url) 257 ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &name_length); 258 } else { 259 const char unknown[] = "<unknown>"; 260 name_length = static_cast<int>(strlen(unknown)); 261 char* buffer = NewArray<char>(name_length); 262 base::OS::StrNCpy(buffer, name_length + 1, unknown, 263 static_cast<size_t>(name_length)); 264 return std::unique_ptr<char[]>(buffer); 265 } 266 } 267 268 SourcePositionInfo GetSourcePositionInfo(Handle<Code> code, 269 Handle<SharedFunctionInfo> function, 270 SourcePosition pos) { 271 if (code->is_turbofanned() || code->is_crankshafted()) { 272 DisallowHeapAllocation disallow; 273 return pos.InliningStack(code)[0]; 274 } else { 275 return SourcePositionInfo(pos, function); 276 } 277 } 278 279 } // namespace 280 281 void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) { 282 // Compute the entry count and get the name of the script. 283 uint32_t entry_count = 0; 284 for (SourcePositionTableIterator iterator(code->source_position_table()); 285 !iterator.done(); iterator.Advance()) { 286 entry_count++; 287 } 288 if (entry_count == 0) return; 289 Handle<Script> script(Script::cast(shared->script())); 290 291 PerfJitCodeDebugInfo debug_info; 292 293 debug_info.event_ = PerfJitCodeLoad::kDebugInfo; 294 debug_info.time_stamp_ = GetTimestamp(); 295 debug_info.address_ = reinterpret_cast<uint64_t>(code->instruction_start()); 296 debug_info.entry_count_ = entry_count; 297 298 uint32_t size = sizeof(debug_info); 299 // Add the sizes of fixed parts of entries. 300 size += entry_count * sizeof(PerfJitDebugEntry); 301 // Add the size of the name after each entry. 302 303 Handle<Code> code_handle(code); 304 Handle<SharedFunctionInfo> function_handle(shared); 305 for (SourcePositionTableIterator iterator(code->source_position_table()); 306 !iterator.done(); iterator.Advance()) { 307 SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle, 308 iterator.source_position())); 309 Handle<Script> script(Script::cast(info.function->script())); 310 std::unique_ptr<char[]> name_string = GetScriptName(script); 311 size += (static_cast<uint32_t>(strlen(name_string.get())) + 1); 312 } 313 314 int padding = ((size + 7) & (~7)) - size; 315 debug_info.size_ = size + padding; 316 LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info)); 317 318 Address code_start = code->instruction_start(); 319 320 for (SourcePositionTableIterator iterator(code->source_position_table()); 321 !iterator.done(); iterator.Advance()) { 322 SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle, 323 iterator.source_position())); 324 PerfJitDebugEntry entry; 325 // TODO(danno): There seems to be a bug in the dwarf handling of JIT code in 326 // the perf tool. It seems to erroneously believe that the first instruction 327 // of functions is at offset 0x40 when displayed in "perf report". To 328 // compensate for this, add a magic constant to the position addresses when 329 // writing them out. 330 entry.address_ = 331 reinterpret_cast<intptr_t>(code_start + iterator.code_offset() + 0x40); 332 entry.line_number_ = info.line + 1; 333 entry.column_ = info.column + 1; 334 LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry)); 335 Handle<Script> script(Script::cast(info.function->script())); 336 std::unique_ptr<char[]> name_string = GetScriptName(script); 337 LogWriteBytes(name_string.get(), 338 static_cast<uint32_t>(strlen(name_string.get())) + 1); 339 } 340 char padding_bytes[] = "\0\0\0\0\0\0\0\0"; 341 LogWriteBytes(padding_bytes, padding); 342 } 343 344 void PerfJitLogger::LogWriteUnwindingInfo(Code* code) { 345 PerfJitCodeUnwindingInfo unwinding_info_header; 346 unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo; 347 unwinding_info_header.time_stamp_ = GetTimestamp(); 348 unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize; 349 350 if (code->has_unwinding_info()) { 351 unwinding_info_header.unwinding_size_ = code->unwinding_info_size(); 352 unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_; 353 } else { 354 unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize; 355 unwinding_info_header.mapped_size_ = 0; 356 } 357 358 int content_size = static_cast<int>(sizeof(unwinding_info_header) + 359 unwinding_info_header.unwinding_size_); 360 int padding_size = RoundUp(content_size, 8) - content_size; 361 unwinding_info_header.size_ = content_size + padding_size; 362 363 LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header), 364 sizeof(unwinding_info_header)); 365 366 if (code->has_unwinding_info()) { 367 LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()), 368 code->unwinding_info_size()); 369 } else { 370 OFStream perf_output_stream(perf_output_handle_); 371 EhFrameWriter::WriteEmptyEhFrame(perf_output_stream); 372 } 373 374 char padding_bytes[] = "\0\0\0\0\0\0\0\0"; 375 DCHECK_LT(padding_size, static_cast<int>(sizeof(padding_bytes))); 376 LogWriteBytes(padding_bytes, static_cast<int>(padding_size)); 377 } 378 379 void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) { 380 // Code relocation not supported. 381 UNREACHABLE(); 382 } 383 384 void PerfJitLogger::LogWriteBytes(const char* bytes, int size) { 385 size_t rv = fwrite(bytes, 1, size, perf_output_handle_); 386 DCHECK(static_cast<size_t>(size) == rv); 387 USE(rv); 388 } 389 390 void PerfJitLogger::LogWriteHeader() { 391 DCHECK(perf_output_handle_ != NULL); 392 PerfJitHeader header; 393 394 header.magic_ = PerfJitHeader::kMagic; 395 header.version_ = PerfJitHeader::kVersion; 396 header.size_ = sizeof(header); 397 header.elf_mach_target_ = GetElfMach(); 398 header.reserved_ = 0xdeadbeef; 399 header.process_id_ = base::OS::GetCurrentProcessId(); 400 header.time_stamp_ = 401 static_cast<uint64_t>(base::OS::TimeCurrentMillis() * 1000.0); 402 header.flags_ = 0; 403 404 LogWriteBytes(reinterpret_cast<const char*>(&header), sizeof(header)); 405 } 406 407 #endif // V8_OS_LINUX 408 } // namespace internal 409 } // namespace v8 410