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_debug_info && 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
LogWriteDebugInfo(Code * code,SharedFunctionInfo * shared)249 void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) {
250 // Compute the entry count and get the name of the script.
251 uint32_t entry_count = 0;
252 for (SourcePositionTableIterator iterator(code->source_position_table());
253 !iterator.done(); iterator.Advance()) {
254 entry_count++;
255 }
256 if (entry_count == 0) return;
257 Handle<Script> script(Script::cast(shared->script()));
258 Handle<Object> name_or_url(Script::GetNameOrSourceURL(script));
259
260 int name_length = 0;
261 std::unique_ptr<char[]> name_string;
262 if (name_or_url->IsString()) {
263 name_string =
264 Handle<String>::cast(name_or_url)
265 ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &name_length);
266 DCHECK_EQ(0, name_string.get()[name_length]);
267 } else {
268 const char unknown[] = "<unknown>";
269 name_length = static_cast<int>(strlen(unknown));
270 char* buffer = NewArray<char>(name_length);
271 base::OS::StrNCpy(buffer, name_length + 1, unknown,
272 static_cast<size_t>(name_length));
273 name_string = std::unique_ptr<char[]>(buffer);
274 }
275 DCHECK_EQ(name_length, static_cast<int>(strlen(name_string.get())));
276
277 PerfJitCodeDebugInfo debug_info;
278
279 debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
280 debug_info.time_stamp_ = GetTimestamp();
281 debug_info.address_ = reinterpret_cast<uint64_t>(code->instruction_start());
282 debug_info.entry_count_ = entry_count;
283
284 uint32_t size = sizeof(debug_info);
285 // Add the sizes of fixed parts of entries.
286 size += entry_count * sizeof(PerfJitDebugEntry);
287 // Add the size of the name after the first entry.
288 size += (static_cast<uint32_t>(name_length) + 1) * entry_count;
289
290 int padding = ((size + 7) & (~7)) - size;
291
292 debug_info.size_ = size + padding;
293
294 LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
295
296 int script_line_offset = script->line_offset();
297 Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
298 Address code_start = code->instruction_start();
299
300 for (SourcePositionTableIterator iterator(code->source_position_table());
301 !iterator.done(); iterator.Advance()) {
302 int position = iterator.source_position().ScriptOffset();
303 int line_number = Script::GetLineNumber(script, position);
304 // Compute column.
305 int relative_line_number = line_number - script_line_offset;
306 int start =
307 (relative_line_number == 0)
308 ? 0
309 : Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
310 int column_offset = position - start;
311 if (relative_line_number == 0) {
312 // For the case where the code is on the same line as the script tag.
313 column_offset += script->column_offset();
314 }
315
316 PerfJitDebugEntry entry;
317 entry.address_ =
318 reinterpret_cast<uint64_t>(code_start + iterator.code_offset());
319 entry.line_number_ = line_number;
320 entry.column_ = column_offset;
321 LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
322 LogWriteBytes(name_string.get(), name_length + 1);
323 }
324 char padding_bytes[] = "\0\0\0\0\0\0\0\0";
325 LogWriteBytes(padding_bytes, padding);
326 }
327
LogWriteUnwindingInfo(Code * code)328 void PerfJitLogger::LogWriteUnwindingInfo(Code* code) {
329 PerfJitCodeUnwindingInfo unwinding_info_header;
330 unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
331 unwinding_info_header.time_stamp_ = GetTimestamp();
332 unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize;
333
334 if (code->has_unwinding_info()) {
335 unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
336 unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
337 } else {
338 unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize;
339 unwinding_info_header.mapped_size_ = 0;
340 }
341
342 int content_size = static_cast<int>(sizeof(unwinding_info_header) +
343 unwinding_info_header.unwinding_size_);
344 int padding_size = RoundUp(content_size, 8) - content_size;
345 unwinding_info_header.size_ = content_size + padding_size;
346
347 LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
348 sizeof(unwinding_info_header));
349
350 if (code->has_unwinding_info()) {
351 LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()),
352 code->unwinding_info_size());
353 } else {
354 OFStream perf_output_stream(perf_output_handle_);
355 EhFrameWriter::WriteEmptyEhFrame(perf_output_stream);
356 }
357
358 char padding_bytes[] = "\0\0\0\0\0\0\0\0";
359 DCHECK_LT(padding_size, static_cast<int>(sizeof(padding_bytes)));
360 LogWriteBytes(padding_bytes, static_cast<int>(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