Home | History | Annotate | Download | only in jit
      1 /*
      2  * Copyright 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "jit_logger.h"
     18 
     19 #include "arch/instruction_set.h"
     20 #include "art_method-inl.h"
     21 #include "base/time_utils.h"
     22 #include "base/unix_file/fd_file.h"
     23 #include "driver/compiler_driver.h"
     24 #include "jit/jit.h"
     25 #include "jit/jit_code_cache.h"
     26 #include "oat_file-inl.h"
     27 
     28 namespace art {
     29 namespace jit {
     30 
     31 #ifdef ART_TARGET_ANDROID
     32 static const char* kLogPrefix = "/data/misc/trace";
     33 #else
     34 static const char* kLogPrefix = "/tmp";
     35 #endif
     36 
     37 // File format of perf-PID.map:
     38 // +---------------------+
     39 // |ADDR SIZE symbolname1|
     40 // |ADDR SIZE symbolname2|
     41 // |...                  |
     42 // +---------------------+
     43 void JitLogger::OpenPerfMapLog() {
     44   std::string pid_str = std::to_string(getpid());
     45   std::string perf_filename = std::string(kLogPrefix) + "/perf-" + pid_str + ".map";
     46   perf_file_.reset(OS::CreateEmptyFileWriteOnly(perf_filename.c_str()));
     47   if (perf_file_ == nullptr) {
     48     LOG(ERROR) << "Could not create perf file at " << perf_filename <<
     49       " Are you on a user build? Perf only works on userdebug/eng builds";
     50   }
     51 }
     52 
     53 void JitLogger::WritePerfMapLog(const void* ptr, size_t code_size, ArtMethod* method) {
     54   if (perf_file_ != nullptr) {
     55     std::string method_name = method->PrettyMethod();
     56 
     57     std::ostringstream stream;
     58     stream << std::hex
     59            << reinterpret_cast<uintptr_t>(ptr)
     60            << " "
     61            << code_size
     62            << " "
     63            << method_name
     64            << std::endl;
     65     std::string str = stream.str();
     66     bool res = perf_file_->WriteFully(str.c_str(), str.size());
     67     if (!res) {
     68       LOG(WARNING) << "Failed to write jitted method info in log: write failure.";
     69     }
     70   } else {
     71     LOG(WARNING) << "Failed to write jitted method info in log: log file doesn't exist.";
     72   }
     73 }
     74 
     75 void JitLogger::ClosePerfMapLog() {
     76   if (perf_file_ != nullptr) {
     77     UNUSED(perf_file_->Flush());
     78     UNUSED(perf_file_->Close());
     79   }
     80 }
     81 
     82 //  File format of jit-PID.jump:
     83 //
     84 //  +--------------------------------+
     85 //  |  PerfJitHeader                 |
     86 //  +--------------------------------+
     87 //  |  PerfJitCodeLoad {             | .
     88 //  |    struct PerfJitBase;         |  .
     89 //  |    uint32_t process_id_;       |   .
     90 //  |    uint32_t thread_id_;        |   .
     91 //  |    uint64_t vma_;              |   .
     92 //  |    uint64_t code_address_;     |   .
     93 //  |    uint64_t code_size_;        |   .
     94 //  |    uint64_t code_id_;          |   .
     95 //  |  }                             |   .
     96 //  +-                              -+   .
     97 //  |  method_name'\0'               |   +--> one jitted method
     98 //  +-                              -+   .
     99 //  |  jitted code binary            |   .
    100 //  |  ...                           |   .
    101 //  +--------------------------------+   .
    102 //  |  PerfJitCodeDebugInfo     {    |   .
    103 //  |    struct PerfJitBase;         |   .
    104 //  |    uint64_t address_;          |   .
    105 //  |    uint64_t entry_count_;      |   .
    106 //  |    struct PerfJitDebugEntry;   |  .
    107 //  |  }                             | .
    108 //  +--------------------------------+
    109 //  |  PerfJitCodeLoad               |
    110 //     ...
    111 //
    112 struct PerfJitHeader {
    113   uint32_t magic_;            // Characters "JiTD"
    114   uint32_t version_;          // Header version
    115   uint32_t size_;             // Total size of header
    116   uint32_t elf_mach_target_;  // Elf mach target
    117   uint32_t reserved_;         // Reserved, currently not used
    118   uint32_t process_id_;       // Process ID of the JIT compiler
    119   uint64_t time_stamp_;       // Timestamp when the header is generated
    120   uint64_t flags_;            // Currently the flags are only used for choosing clock for timestamp,
    121                               // we set it to 0 to tell perf that we use CLOCK_MONOTONIC clock.
    122   static const uint32_t kMagic = 0x4A695444;  // "JiTD"
    123   static const uint32_t kVersion = 1;
    124 };
    125 
    126 // Each record starts with such basic information: event type, total size, and timestamp.
    127 struct PerfJitBase {
    128   enum PerfJitEvent {
    129     // A jitted code load event.
    130     // In ART JIT, it is used to log a new method is jit compiled and committed to jit-code-cache.
    131     // Note that such kLoad event supports code cache GC in ART JIT.
    132     // For every kLoad event recorded in jit-PID.dump and every perf sample recorded in perf.data,
    133     // each event/sample has time stamp. In case code cache GC happens in ART JIT, and a new
    134     // jitted method is committed to the same address of a previously deleted method,
    135     // the time stamp information can help profiler to tell whether this sample belongs to the
    136     // era of the first jitted method, or does it belong to the period of the second jitted method.
    137     // JitCodeCache doesn't have to record any event on 'code delete'.
    138     kLoad = 0,
    139 
    140     // A jitted code move event, i,e. a jitted code moved from one address to another address.
    141     // It helps profiler to map samples to the right symbol even when the code is moved.
    142     // In ART JIT, this event can help log such behavior:
    143     // A jitted method is recorded in previous kLoad event, but due to some reason,
    144     // it is moved to another address in jit-code-cache.
    145     kMove = 1,
    146 
    147     // Logs debug line/column information.
    148     kDebugInfo = 2,
    149 
    150     // Logs JIT VM end of life event.
    151     kClose = 3
    152   };
    153   uint32_t event_;       // Must be one of the events defined in PerfJitEvent.
    154   uint32_t size_;        // Total size of this event record.
    155                          // For example, for kLoad event, size of the event record is:
    156                          // sizeof(PerfJitCodeLoad) + method_name.size() + compiled code size.
    157   uint64_t time_stamp_;  // Timestamp for the event.
    158 };
    159 
    160 // Logs a jitted code load event (kLoad).
    161 // In ART JIT, it is used to log a new method is jit compiled and commited to jit-code-cache.
    162 struct PerfJitCodeLoad : PerfJitBase {
    163   uint32_t process_id_;    // Process ID who performs the jit code load.
    164                            // In ART JIT, it is the pid of the JIT compiler.
    165   uint32_t thread_id_;     // Thread ID who performs the jit code load.
    166                            // In ART JIT, it is the tid of the JIT compiler.
    167   uint64_t vma_;           // Address of the code section. In ART JIT, because code_address_
    168                            // uses absolute address, this field is 0.
    169   uint64_t code_address_;  // Address where is jitted code is loaded.
    170   uint64_t code_size_;     // Size of the jitted code.
    171   uint64_t code_id_;       // Unique ID for each jitted code.
    172 };
    173 
    174 // This structure is for source line/column mapping.
    175 // Currently this feature is not implemented in ART JIT yet.
    176 struct PerfJitDebugEntry {
    177   uint64_t address_;      // Code address which maps to the line/column in source.
    178   uint32_t line_number_;  // Source line number starting at 1.
    179   uint32_t column_;       // Column discriminator, default 0.
    180   const char name_[0];    // Followed by null-terminated name or \0xff\0 if same as previous.
    181 };
    182 
    183 // Logs debug line information (kDebugInfo).
    184 // This structure is for source line/column mapping.
    185 // Currently this feature is not implemented in ART JIT yet.
    186 struct PerfJitCodeDebugInfo : PerfJitBase {
    187   uint64_t address_;              // Starting code address which the debug info describes.
    188   uint64_t entry_count_;          // How many instances of PerfJitDebugEntry.
    189   PerfJitDebugEntry entries_[0];  // Followed by entry_count_ instances of PerfJitDebugEntry.
    190 };
    191 
    192 static uint32_t GetElfMach() {
    193 #if defined(__arm__)
    194   static const uint32_t kElfMachARM = 0x28;
    195   return kElfMachARM;
    196 #elif defined(__aarch64__)
    197   static const uint32_t kElfMachARM64 = 0xB7;
    198   return kElfMachARM64;
    199 #elif defined(__i386__)
    200   static const uint32_t kElfMachIA32 = 0x3;
    201   return kElfMachIA32;
    202 #elif defined(__x86_64__)
    203   static const uint32_t kElfMachX64 = 0x3E;
    204   return kElfMachX64;
    205 #else
    206   UNIMPLEMENTED(WARNING) << "Unsupported architecture in JitLogger";
    207   return 0;
    208 #endif
    209 }
    210 
    211 void JitLogger::OpenMarkerFile() {
    212   int fd = jit_dump_file_->Fd();
    213   // The 'perf inject' tool requires that the jit-PID.dump file
    214   // must have a mmap(PROT_READ|PROT_EXEC) record in perf.data.
    215   marker_address_ = mmap(nullptr, kPageSize, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
    216   if (marker_address_ == MAP_FAILED) {
    217     LOG(WARNING) << "Failed to create record in perf.data. JITed code profiling will not work.";
    218     return;
    219   }
    220 }
    221 
    222 void JitLogger::CloseMarkerFile() {
    223   if (marker_address_ != nullptr) {
    224     munmap(marker_address_, kPageSize);
    225   }
    226 }
    227 
    228 void JitLogger::WriteJitDumpDebugInfo() {
    229   // In the future, we can add java source file line/column mapping here.
    230 }
    231 
    232 void JitLogger::WriteJitDumpHeader() {
    233   PerfJitHeader header;
    234 
    235   std::memset(&header, 0, sizeof(header));
    236   header.magic_ = PerfJitHeader::kMagic;
    237   header.version_ = PerfJitHeader::kVersion;
    238   header.size_ = sizeof(header);
    239   header.elf_mach_target_ = GetElfMach();
    240   header.process_id_ = static_cast<uint32_t>(getpid());
    241   header.time_stamp_ = art::NanoTime();  // CLOCK_MONOTONIC clock is required.
    242   header.flags_ = 0;
    243 
    244   bool res = jit_dump_file_->WriteFully(reinterpret_cast<const char*>(&header), sizeof(header));
    245   if (!res) {
    246     LOG(WARNING) << "Failed to write profiling log. The 'perf inject' tool will not work.";
    247   }
    248 }
    249 
    250 void JitLogger::OpenJitDumpLog() {
    251   std::string pid_str = std::to_string(getpid());
    252   std::string jitdump_filename = std::string(kLogPrefix) + "/jit-" + pid_str + ".dump";
    253 
    254   jit_dump_file_.reset(OS::CreateEmptyFile(jitdump_filename.c_str()));
    255   if (jit_dump_file_ == nullptr) {
    256     LOG(ERROR) << "Could not create jit dump file at " << jitdump_filename <<
    257       " Are you on a user build? Perf only works on userdebug/eng builds";
    258     return;
    259   }
    260 
    261   OpenMarkerFile();
    262 
    263   // Continue to write jit-PID.dump file even above OpenMarkerFile() fails.
    264   // Even if that means 'perf inject' tool cannot work, developers can still use other tools
    265   // to map the samples in perf.data to the information (symbol,address,code) recorded
    266   // in the jit-PID.dump file, and still proceed the jitted code analysis.
    267   WriteJitDumpHeader();
    268 }
    269 
    270 void JitLogger::WriteJitDumpLog(const void* ptr, size_t code_size, ArtMethod* method) {
    271   if (jit_dump_file_ != nullptr) {
    272     std::string method_name = method->PrettyMethod();
    273 
    274     PerfJitCodeLoad jit_code;
    275     std::memset(&jit_code, 0, sizeof(jit_code));
    276     jit_code.event_ = PerfJitCodeLoad::kLoad;
    277     jit_code.size_ = sizeof(jit_code) + method_name.size() + 1 + code_size;
    278     jit_code.time_stamp_ = art::NanoTime();    // CLOCK_MONOTONIC clock is required.
    279     jit_code.process_id_ = static_cast<uint32_t>(getpid());
    280     jit_code.thread_id_ = static_cast<uint32_t>(art::GetTid());
    281     jit_code.vma_ = 0x0;
    282     jit_code.code_address_ = reinterpret_cast<uint64_t>(ptr);
    283     jit_code.code_size_ = code_size;
    284     jit_code.code_id_ = code_index_++;
    285 
    286     // Write one complete jitted method info, including:
    287     // - PerfJitCodeLoad structure
    288     // - Method name
    289     // - Complete generated code of this method
    290     //
    291     // Use UNUSED() here to avoid compiler warnings.
    292     UNUSED(jit_dump_file_->WriteFully(reinterpret_cast<const char*>(&jit_code), sizeof(jit_code)));
    293     UNUSED(jit_dump_file_->WriteFully(method_name.c_str(), method_name.size() + 1));
    294     UNUSED(jit_dump_file_->WriteFully(ptr, code_size));
    295 
    296     WriteJitDumpDebugInfo();
    297   }
    298 }
    299 
    300 void JitLogger::CloseJitDumpLog() {
    301   if (jit_dump_file_ != nullptr) {
    302     CloseMarkerFile();
    303     UNUSED(jit_dump_file_->Flush());
    304     UNUSED(jit_dump_file_->Close());
    305   }
    306 }
    307 
    308 }  // namespace jit
    309 }  // namespace art
    310