• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
OpenJitDumpFile()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 
CloseJitDumpFile()139 void PerfJitLogger::CloseJitDumpFile() {
140   if (perf_output_handle_ == nullptr) return;
141   fclose(perf_output_handle_);
142   perf_output_handle_ = nullptr;
143 }
144 
OpenMarkerFile(int fd)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 
CloseMarkerFile(void * marker_address)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 
PerfJitLogger()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 
~PerfJitLogger()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 
GetTimestamp()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 
LogRecordedBuffer(AbstractCode * abstract_code,SharedFunctionInfo * shared,const char * name,int length)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 
GetScriptName(Handle<Script> script)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 
GetSourcePositionInfo(Handle<Code> code,Handle<SharedFunctionInfo> function,SourcePosition pos)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 
LogWriteDebugInfo(Code * code,SharedFunctionInfo * shared)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 
LogWriteUnwindingInfo(Code * code)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 
CodeMoveEvent(AbstractCode * from,Address to)379 void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
380   // Code relocation not supported.
381   UNREACHABLE();
382 }
383 
LogWriteBytes(const char * bytes,int size)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 
LogWriteHeader()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