blob: df251fde6e10685944c3a45e5113c56fc23cb40b [file] [log] [blame]
Ben Murdochda12d292016-06-02 14:46:10 +01001// 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"
Ben Murdoch61f157c2016-09-16 13:49:30 +010031#include "src/eh-frame.h"
Ben Murdochda12d292016-06-02 14:46:10 +010032#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
40namespace v8 {
41namespace internal {
42
43#if V8_OS_LINUX
44
45struct 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
59struct PerfJitBase {
Ben Murdoch61f157c2016-09-16 13:49:30 +010060 enum PerfJitEvent {
61 kLoad = 0,
62 kMove = 1,
63 kDebugInfo = 2,
64 kClose = 3,
65 kUnwindingInfo = 4
66 };
Ben Murdochda12d292016-06-02 14:46:10 +010067
68 uint32_t event_;
69 uint32_t size_;
70 uint64_t time_stamp_;
71};
72
73struct 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
82struct 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
89struct PerfJitCodeDebugInfo : PerfJitBase {
90 uint64_t address_;
91 uint64_t entry_count_;
92 // Followed by entry_count_ instances of PerfJitDebugEntry.
93};
94
Ben Murdoch61f157c2016-09-16 13:49:30 +010095struct 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
Ben Murdochda12d292016-06-02 14:46:10 +0100102const char PerfJitLogger::kFilenameFormatString[] = "./jit-%d.dump";
103
104// Extra padding for the PID in the filename
105const int PerfJitLogger::kFilenameBufferPadding = 16;
106
107base::LazyRecursiveMutex PerfJitLogger::file_mutex_;
108// The following static variables are protected by PerfJitLogger::file_mutex_.
109uint64_t PerfJitLogger::reference_count_ = 0;
110void* PerfJitLogger::marker_address_ = nullptr;
111uint64_t PerfJitLogger::code_index_ = 0;
112FILE* PerfJitLogger::perf_output_handle_ = nullptr;
113
114void 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
136void PerfJitLogger::CloseJitDumpFile() {
137 if (perf_output_handle_ == nullptr) return;
138 fclose(perf_output_handle_);
139 perf_output_handle_ = nullptr;
140}
141
142void* 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
154void 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
161PerfJitLogger::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
173PerfJitLogger::~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
183uint64_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
192void 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
Ben Murdoch61f157c2016-09-16 13:49:30 +0100221 // Unwinding info comes right after debug info.
222 if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(code);
223
Ben Murdochda12d292016-06-02 14:46:10 +0100224 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
246void 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
Ben Murdoch61f157c2016-09-16 13:49:30 +0100323void 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
Ben Murdochda12d292016-06-02 14:46:10 +0100363void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
364 // Code relocation not supported.
365 UNREACHABLE();
366}
367
368void 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
374void 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