• 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 "src/assembler.h"
31 #include "src/eh-frame.h"
32 #include "src/objects-inl.h"
33 
34 #if V8_OS_LINUX
35 #include <fcntl.h>
36 #include <sys/mman.h>
37 #include <unistd.h>
38 #endif  // V8_OS_LINUX
39 
40 namespace v8 {
41 namespace internal {
42 
43 #if V8_OS_LINUX
44 
45 struct PerfJitHeader {
46   uint32_t magic_;
47   uint32_t version_;
48   uint32_t size_;
49   uint32_t elf_mach_target_;
50   uint32_t reserved_;
51   uint32_t process_id_;
52   uint64_t time_stamp_;
53   uint64_t flags_;
54 
55   static const uint32_t kMagic = 0x4A695444;
56   static const uint32_t kVersion = 1;
57 };
58 
59 struct PerfJitBase {
60   enum PerfJitEvent {
61     kLoad = 0,
62     kMove = 1,
63     kDebugInfo = 2,
64     kClose = 3,
65     kUnwindingInfo = 4
66   };
67 
68   uint32_t event_;
69   uint32_t size_;
70   uint64_t time_stamp_;
71 };
72 
73 struct PerfJitCodeLoad : PerfJitBase {
74   uint32_t process_id_;
75   uint32_t thread_id_;
76   uint64_t vma_;
77   uint64_t code_address_;
78   uint64_t code_size_;
79   uint64_t code_id_;
80 };
81 
82 struct PerfJitDebugEntry {
83   uint64_t address_;
84   int line_number_;
85   int column_;
86   // Followed by null-terminated name or \0xff\0 if same as previous.
87 };
88 
89 struct PerfJitCodeDebugInfo : PerfJitBase {
90   uint64_t address_;
91   uint64_t entry_count_;
92   // Followed by entry_count_ instances of PerfJitDebugEntry.
93 };
94 
95 struct PerfJitCodeUnwindingInfo : PerfJitBase {
96   uint64_t unwinding_size_;
97   uint64_t eh_frame_hdr_size_;
98   uint64_t mapped_size_;
99   // Followed by size_ - sizeof(PerfJitCodeUnwindingInfo) bytes of data.
100 };
101 
102 const char PerfJitLogger::kFilenameFormatString[] = "./jit-%d.dump";
103 
104 // Extra padding for the PID in the filename
105 const int PerfJitLogger::kFilenameBufferPadding = 16;
106 
107 base::LazyRecursiveMutex PerfJitLogger::file_mutex_;
108 // The following static variables are protected by PerfJitLogger::file_mutex_.
109 uint64_t PerfJitLogger::reference_count_ = 0;
110 void* PerfJitLogger::marker_address_ = nullptr;
111 uint64_t PerfJitLogger::code_index_ = 0;
112 FILE* PerfJitLogger::perf_output_handle_ = nullptr;
113 
OpenJitDumpFile()114 void PerfJitLogger::OpenJitDumpFile() {
115   // Open the perf JIT dump file.
116   perf_output_handle_ = nullptr;
117 
118   int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding;
119   ScopedVector<char> perf_dump_name(bufferSize);
120   int size = SNPrintF(perf_dump_name, kFilenameFormatString,
121                       base::OS::GetCurrentProcessId());
122   CHECK_NE(size, -1);
123 
124   int fd = open(perf_dump_name.start(), O_CREAT | O_TRUNC | O_RDWR, 0666);
125   if (fd == -1) return;
126 
127   marker_address_ = OpenMarkerFile(fd);
128   if (marker_address_ == nullptr) return;
129 
130   perf_output_handle_ = fdopen(fd, "w+");
131   if (perf_output_handle_ == nullptr) return;
132 
133   setvbuf(perf_output_handle_, NULL, _IOFBF, kLogBufferSize);
134 }
135 
CloseJitDumpFile()136 void PerfJitLogger::CloseJitDumpFile() {
137   if (perf_output_handle_ == nullptr) return;
138   fclose(perf_output_handle_);
139   perf_output_handle_ = nullptr;
140 }
141 
OpenMarkerFile(int fd)142 void* PerfJitLogger::OpenMarkerFile(int fd) {
143   long page_size = sysconf(_SC_PAGESIZE);  // NOLINT(runtime/int)
144   if (page_size == -1) return nullptr;
145 
146   // Mmap the file so that there is a mmap record in the perf_data file.
147   //
148   // The map must be PROT_EXEC to ensure it is not ignored by perf record.
149   void* marker_address =
150       mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
151   return (marker_address == MAP_FAILED) ? nullptr : marker_address;
152 }
153 
CloseMarkerFile(void * marker_address)154 void PerfJitLogger::CloseMarkerFile(void* marker_address) {
155   if (marker_address == nullptr) return;
156   long page_size = sysconf(_SC_PAGESIZE);  // NOLINT(runtime/int)
157   if (page_size == -1) return;
158   munmap(marker_address, page_size);
159 }
160 
PerfJitLogger()161 PerfJitLogger::PerfJitLogger() {
162   base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
163 
164   reference_count_++;
165   // If this is the first logger, open the file and write the header.
166   if (reference_count_ == 1) {
167     OpenJitDumpFile();
168     if (perf_output_handle_ == nullptr) return;
169     LogWriteHeader();
170   }
171 }
172 
~PerfJitLogger()173 PerfJitLogger::~PerfJitLogger() {
174   base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
175 
176   reference_count_--;
177   // If this was the last logger, close the file.
178   if (reference_count_ == 0) {
179     CloseJitDumpFile();
180   }
181 }
182 
GetTimestamp()183 uint64_t PerfJitLogger::GetTimestamp() {
184   struct timespec ts;
185   int result = clock_gettime(CLOCK_MONOTONIC, &ts);
186   DCHECK_EQ(0, result);
187   USE(result);
188   static const uint64_t kNsecPerSec = 1000000000;
189   return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
190 }
191 
LogRecordedBuffer(AbstractCode * abstract_code,SharedFunctionInfo * shared,const char * name,int length)192 void PerfJitLogger::LogRecordedBuffer(AbstractCode* abstract_code,
193                                       SharedFunctionInfo* shared,
194                                       const char* name, int length) {
195   if (FLAG_perf_basic_prof_only_functions &&
196       (abstract_code->kind() != AbstractCode::FUNCTION &&
197        abstract_code->kind() != AbstractCode::INTERPRETED_FUNCTION &&
198        abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) {
199     return;
200   }
201 
202   base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
203 
204   if (perf_output_handle_ == nullptr) return;
205 
206   // We only support non-interpreted functions.
207   if (!abstract_code->IsCode()) return;
208   Code* code = abstract_code->GetCode();
209   DCHECK(code->instruction_start() == code->address() + Code::kHeaderSize);
210 
211   // Debug info has to be emitted first.
212   if (FLAG_perf_prof_debug_info && shared != nullptr) {
213     LogWriteDebugInfo(code, shared);
214   }
215 
216   const char* code_name = name;
217   uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code->instruction_start());
218   uint32_t code_size = code->is_crankshafted() ? code->safepoint_table_offset()
219                                                : code->instruction_size();
220 
221   // Unwinding info comes right after debug info.
222   if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(code);
223 
224   static const char string_terminator[] = "\0";
225 
226   PerfJitCodeLoad code_load;
227   code_load.event_ = PerfJitCodeLoad::kLoad;
228   code_load.size_ = sizeof(code_load) + length + 1 + code_size;
229   code_load.time_stamp_ = GetTimestamp();
230   code_load.process_id_ =
231       static_cast<uint32_t>(base::OS::GetCurrentProcessId());
232   code_load.thread_id_ = static_cast<uint32_t>(base::OS::GetCurrentThreadId());
233   code_load.vma_ = 0x0;  //  Our addresses are absolute.
234   code_load.code_address_ = reinterpret_cast<uint64_t>(code_pointer);
235   code_load.code_size_ = code_size;
236   code_load.code_id_ = code_index_;
237 
238   code_index_++;
239 
240   LogWriteBytes(reinterpret_cast<const char*>(&code_load), sizeof(code_load));
241   LogWriteBytes(code_name, length);
242   LogWriteBytes(string_terminator, 1);
243   LogWriteBytes(reinterpret_cast<const char*>(code_pointer), code_size);
244 }
245 
LogWriteDebugInfo(Code * code,SharedFunctionInfo * shared)246 void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) {
247   // Compute the entry count and get the name of the script.
248   uint32_t entry_count = 0;
249   for (RelocIterator it(code, RelocInfo::kPositionMask); !it.done();
250        it.next()) {
251     entry_count++;
252   }
253   if (entry_count == 0) return;
254   Handle<Script> script(Script::cast(shared->script()));
255   Handle<Object> name_or_url(Script::GetNameOrSourceURL(script));
256 
257   int name_length = 0;
258   base::SmartArrayPointer<char> name_string;
259   if (name_or_url->IsString()) {
260     name_string =
261         Handle<String>::cast(name_or_url)
262             ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &name_length);
263     DCHECK_EQ(0, name_string.get()[name_length]);
264   } else {
265     const char unknown[] = "<unknown>";
266     name_length = static_cast<int>(strlen(unknown));
267     char* buffer = NewArray<char>(name_length);
268     base::OS::StrNCpy(buffer, name_length + 1, unknown,
269                       static_cast<size_t>(name_length));
270     name_string = base::SmartArrayPointer<char>(buffer);
271   }
272   DCHECK_EQ(name_length, strlen(name_string.get()));
273 
274   PerfJitCodeDebugInfo debug_info;
275 
276   debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
277   debug_info.time_stamp_ = GetTimestamp();
278   debug_info.address_ = reinterpret_cast<uint64_t>(code->instruction_start());
279   debug_info.entry_count_ = entry_count;
280 
281   uint32_t size = sizeof(debug_info);
282   // Add the sizes of fixed parts of entries.
283   size += entry_count * sizeof(PerfJitDebugEntry);
284   // Add the size of the name after the first entry.
285   size += (static_cast<uint32_t>(name_length) + 1) * entry_count;
286 
287   int padding = ((size + 7) & (~7)) - size;
288 
289   debug_info.size_ = size + padding;
290 
291   LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
292 
293   int script_line_offset = script->line_offset();
294   Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
295 
296   for (RelocIterator it(code, RelocInfo::kPositionMask); !it.done();
297        it.next()) {
298     int position = static_cast<int>(it.rinfo()->data());
299     int line_number = Script::GetLineNumber(script, position);
300     // Compute column.
301     int relative_line_number = line_number - script_line_offset;
302     int start =
303         (relative_line_number == 0)
304             ? 0
305             : Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
306     int column_offset = position - start;
307     if (relative_line_number == 0) {
308       // For the case where the code is on the same line as the script tag.
309       column_offset += script->column_offset();
310     }
311 
312     PerfJitDebugEntry entry;
313     entry.address_ = reinterpret_cast<uint64_t>(it.rinfo()->pc());
314     entry.line_number_ = line_number;
315     entry.column_ = column_offset;
316     LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
317     LogWriteBytes(name_string.get(), name_length + 1);
318   }
319   char padding_bytes[] = "\0\0\0\0\0\0\0\0";
320   LogWriteBytes(padding_bytes, padding);
321 }
322 
LogWriteUnwindingInfo(Code * code)323 void PerfJitLogger::LogWriteUnwindingInfo(Code* code) {
324   EhFrameHdr eh_frame_hdr(code);
325 
326   PerfJitCodeUnwindingInfo unwinding_info_header;
327   unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
328   unwinding_info_header.time_stamp_ = GetTimestamp();
329   unwinding_info_header.eh_frame_hdr_size_ = EhFrameHdr::kRecordSize;
330 
331   if (code->has_unwinding_info()) {
332     unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
333     unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
334   } else {
335     unwinding_info_header.unwinding_size_ = EhFrameHdr::kRecordSize;
336     unwinding_info_header.mapped_size_ = 0;
337   }
338 
339   int content_size = static_cast<int>(sizeof(unwinding_info_header) +
340                                       unwinding_info_header.unwinding_size_);
341   int padding_size = RoundUp(content_size, 8) - content_size;
342   unwinding_info_header.size_ = content_size + padding_size;
343 
344   LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
345                 sizeof(unwinding_info_header));
346 
347   if (code->has_unwinding_info()) {
348     // The last EhFrameHdr::kRecordSize bytes were a placeholder for the header.
349     // Discard them and write the actual eh_frame_hdr (below).
350     DCHECK_GE(code->unwinding_info_size(), EhFrameHdr::kRecordSize);
351     LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()),
352                   code->unwinding_info_size() - EhFrameHdr::kRecordSize);
353   }
354 
355   LogWriteBytes(reinterpret_cast<const char*>(&eh_frame_hdr),
356                 EhFrameHdr::kRecordSize);
357 
358   char padding_bytes[] = "\0\0\0\0\0\0\0\0";
359   DCHECK_LT(padding_size, sizeof(padding_bytes));
360   LogWriteBytes(padding_bytes, padding_size);
361 }
362 
CodeMoveEvent(AbstractCode * from,Address to)363 void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
364   // Code relocation not supported.
365   UNREACHABLE();
366 }
367 
LogWriteBytes(const char * bytes,int size)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 
LogWriteHeader()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