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 && 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