Home | History | Annotate | Download | only in src
      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_debug_info && 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 void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) {
    250   // Compute the entry count and get the name of the script.
    251   uint32_t entry_count = 0;
    252   for (SourcePositionTableIterator iterator(code->source_position_table());
    253        !iterator.done(); iterator.Advance()) {
    254     entry_count++;
    255   }
    256   if (entry_count == 0) return;
    257   Handle<Script> script(Script::cast(shared->script()));
    258   Handle<Object> name_or_url(Script::GetNameOrSourceURL(script));
    259 
    260   int name_length = 0;
    261   std::unique_ptr<char[]> name_string;
    262   if (name_or_url->IsString()) {
    263     name_string =
    264         Handle<String>::cast(name_or_url)
    265             ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &name_length);
    266     DCHECK_EQ(0, name_string.get()[name_length]);
    267   } else {
    268     const char unknown[] = "<unknown>";
    269     name_length = static_cast<int>(strlen(unknown));
    270     char* buffer = NewArray<char>(name_length);
    271     base::OS::StrNCpy(buffer, name_length + 1, unknown,
    272                       static_cast<size_t>(name_length));
    273     name_string = std::unique_ptr<char[]>(buffer);
    274   }
    275   DCHECK_EQ(name_length, static_cast<int>(strlen(name_string.get())));
    276 
    277   PerfJitCodeDebugInfo debug_info;
    278 
    279   debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
    280   debug_info.time_stamp_ = GetTimestamp();
    281   debug_info.address_ = reinterpret_cast<uint64_t>(code->instruction_start());
    282   debug_info.entry_count_ = entry_count;
    283 
    284   uint32_t size = sizeof(debug_info);
    285   // Add the sizes of fixed parts of entries.
    286   size += entry_count * sizeof(PerfJitDebugEntry);
    287   // Add the size of the name after the first entry.
    288   size += (static_cast<uint32_t>(name_length) + 1) * entry_count;
    289 
    290   int padding = ((size + 7) & (~7)) - size;
    291 
    292   debug_info.size_ = size + padding;
    293 
    294   LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
    295 
    296   int script_line_offset = script->line_offset();
    297   Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
    298   Address code_start = code->instruction_start();
    299 
    300   for (SourcePositionTableIterator iterator(code->source_position_table());
    301        !iterator.done(); iterator.Advance()) {
    302     int position = iterator.source_position().ScriptOffset();
    303     int line_number = Script::GetLineNumber(script, position);
    304     // Compute column.
    305     int relative_line_number = line_number - script_line_offset;
    306     int start =
    307         (relative_line_number == 0)
    308             ? 0
    309             : Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
    310     int column_offset = position - start;
    311     if (relative_line_number == 0) {
    312       // For the case where the code is on the same line as the script tag.
    313       column_offset += script->column_offset();
    314     }
    315 
    316     PerfJitDebugEntry entry;
    317     entry.address_ =
    318         reinterpret_cast<uint64_t>(code_start + iterator.code_offset());
    319     entry.line_number_ = line_number;
    320     entry.column_ = column_offset;
    321     LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
    322     LogWriteBytes(name_string.get(), name_length + 1);
    323   }
    324   char padding_bytes[] = "\0\0\0\0\0\0\0\0";
    325   LogWriteBytes(padding_bytes, padding);
    326 }
    327 
    328 void PerfJitLogger::LogWriteUnwindingInfo(Code* code) {
    329   PerfJitCodeUnwindingInfo unwinding_info_header;
    330   unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
    331   unwinding_info_header.time_stamp_ = GetTimestamp();
    332   unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize;
    333 
    334   if (code->has_unwinding_info()) {
    335     unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
    336     unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
    337   } else {
    338     unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize;
    339     unwinding_info_header.mapped_size_ = 0;
    340   }
    341 
    342   int content_size = static_cast<int>(sizeof(unwinding_info_header) +
    343                                       unwinding_info_header.unwinding_size_);
    344   int padding_size = RoundUp(content_size, 8) - content_size;
    345   unwinding_info_header.size_ = content_size + padding_size;
    346 
    347   LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
    348                 sizeof(unwinding_info_header));
    349 
    350   if (code->has_unwinding_info()) {
    351     LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()),
    352                   code->unwinding_info_size());
    353   } else {
    354     OFStream perf_output_stream(perf_output_handle_);
    355     EhFrameWriter::WriteEmptyEhFrame(perf_output_stream);
    356   }
    357 
    358   char padding_bytes[] = "\0\0\0\0\0\0\0\0";
    359   DCHECK_LT(padding_size, static_cast<int>(sizeof(padding_bytes)));
    360   LogWriteBytes(padding_bytes, static_cast<int>(padding_size));
    361 }
    362 
    363 void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
    364   // Code relocation not supported.
    365   UNREACHABLE();
    366 }
    367 
    368 void PerfJitLogger::LogWriteBytes(const char* bytes, int size) {
    369   size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
    370   DCHECK(static_cast<size_t>(size) == rv);
    371   USE(rv);
    372 }
    373 
    374 void PerfJitLogger::LogWriteHeader() {
    375   DCHECK(perf_output_handle_ != NULL);
    376   PerfJitHeader header;
    377 
    378   header.magic_ = PerfJitHeader::kMagic;
    379   header.version_ = PerfJitHeader::kVersion;
    380   header.size_ = sizeof(header);
    381   header.elf_mach_target_ = GetElfMach();
    382   header.reserved_ = 0xdeadbeef;
    383   header.process_id_ = base::OS::GetCurrentProcessId();
    384   header.time_stamp_ =
    385       static_cast<uint64_t>(base::OS::TimeCurrentMillis() * 1000.0);
    386   header.flags_ = 0;
    387 
    388   LogWriteBytes(reinterpret_cast<const char*>(&header), sizeof(header));
    389 }
    390 
    391 #endif  // V8_OS_LINUX
    392 }  // namespace internal
    393 }  // namespace v8
    394