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