• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // +---------------------+
OpenPerfMapLog()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 
WritePerfMapLog(const void * ptr,size_t code_size,ArtMethod * method)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 
ClosePerfMapLog()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 
GetElfMach()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 
OpenMarkerFile()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 
CloseMarkerFile()222 void JitLogger::CloseMarkerFile() {
223   if (marker_address_ != nullptr) {
224     munmap(marker_address_, kPageSize);
225   }
226 }
227 
WriteJitDumpDebugInfo()228 void JitLogger::WriteJitDumpDebugInfo() {
229   // In the future, we can add java source file line/column mapping here.
230 }
231 
WriteJitDumpHeader()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 
OpenJitDumpLog()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 
WriteJitDumpLog(const void * ptr,size_t code_size,ArtMethod * method)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 
CloseJitDumpLog()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