Line data Source code
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 :
117 0 : void PerfJitLogger::OpenJitDumpFile() {
118 : // Open the perf JIT dump file.
119 0 : 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 0 : base::OS::GetCurrentProcessId());
125 0 : CHECK_NE(size, -1);
126 :
127 : int fd = open(perf_dump_name.start(), O_CREAT | O_TRUNC | O_RDWR, 0666);
128 0 : if (fd == -1) return;
129 :
130 0 : marker_address_ = OpenMarkerFile(fd);
131 0 : if (marker_address_ == nullptr) return;
132 :
133 0 : perf_output_handle_ = fdopen(fd, "w+");
134 0 : if (perf_output_handle_ == nullptr) return;
135 :
136 0 : setvbuf(perf_output_handle_, NULL, _IOFBF, kLogBufferSize);
137 : }
138 :
139 0 : void PerfJitLogger::CloseJitDumpFile() {
140 0 : if (perf_output_handle_ == nullptr) return;
141 0 : fclose(perf_output_handle_);
142 0 : perf_output_handle_ = nullptr;
143 : }
144 :
145 0 : void* PerfJitLogger::OpenMarkerFile(int fd) {
146 0 : long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
147 0 : 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 0 : mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
154 0 : return (marker_address == MAP_FAILED) ? nullptr : marker_address;
155 : }
156 :
157 0 : void PerfJitLogger::CloseMarkerFile(void* marker_address) {
158 0 : if (marker_address == nullptr) return;
159 0 : long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
160 0 : if (page_size == -1) return;
161 0 : munmap(marker_address, page_size);
162 : }
163 :
164 0 : PerfJitLogger::PerfJitLogger() {
165 : base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
166 :
167 0 : reference_count_++;
168 : // If this is the first logger, open the file and write the header.
169 0 : if (reference_count_ == 1) {
170 0 : OpenJitDumpFile();
171 0 : if (perf_output_handle_ == nullptr) return;
172 0 : LogWriteHeader();
173 : }
174 : }
175 :
176 0 : PerfJitLogger::~PerfJitLogger() {
177 : base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
178 :
179 0 : reference_count_--;
180 : // If this was the last logger, close the file.
181 0 : if (reference_count_ == 0) {
182 : CloseJitDumpFile();
183 : }
184 0 : }
185 :
186 0 : uint64_t PerfJitLogger::GetTimestamp() {
187 : struct timespec ts;
188 0 : int result = clock_gettime(CLOCK_MONOTONIC, &ts);
189 : DCHECK_EQ(0, result);
190 : USE(result);
191 : static const uint64_t kNsecPerSec = 1000000000;
192 0 : return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
193 : }
194 :
195 0 : void PerfJitLogger::LogRecordedBuffer(AbstractCode* abstract_code,
196 : SharedFunctionInfo* shared,
197 : const char* name, int length) {
198 0 : if (FLAG_perf_basic_prof_only_functions &&
199 0 : (abstract_code->kind() != AbstractCode::FUNCTION &&
200 0 : abstract_code->kind() != AbstractCode::INTERPRETED_FUNCTION &&
201 0 : abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) {
202 0 : return;
203 : }
204 :
205 : base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
206 :
207 0 : if (perf_output_handle_ == nullptr) return;
208 :
209 : // We only support non-interpreted functions.
210 0 : 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 0 : if (FLAG_perf_prof && shared != nullptr) {
216 0 : LogWriteDebugInfo(code, shared);
217 : }
218 :
219 : const char* code_name = name;
220 0 : uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code->instruction_start());
221 : // Code generated by Crankshaft or Turbofan will have the safepoint table
222 : // directly after instructions. There is no need to record the safepoint table
223 : // itself.
224 : uint32_t code_size = code->is_crankshafted() ? code->safepoint_table_offset()
225 0 : : code->instruction_size();
226 :
227 : // Unwinding info comes right after debug info.
228 0 : if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(code);
229 :
230 : static const char string_terminator[] = "\0";
231 :
232 : PerfJitCodeLoad code_load;
233 0 : code_load.event_ = PerfJitCodeLoad::kLoad;
234 0 : code_load.size_ = sizeof(code_load) + length + 1 + code_size;
235 0 : code_load.time_stamp_ = GetTimestamp();
236 : code_load.process_id_ =
237 0 : static_cast<uint32_t>(base::OS::GetCurrentProcessId());
238 0 : code_load.thread_id_ = static_cast<uint32_t>(base::OS::GetCurrentThreadId());
239 0 : code_load.vma_ = 0x0; // Our addresses are absolute.
240 0 : code_load.code_address_ = reinterpret_cast<uint64_t>(code_pointer);
241 0 : code_load.code_size_ = code_size;
242 0 : code_load.code_id_ = code_index_;
243 :
244 0 : code_index_++;
245 :
246 : LogWriteBytes(reinterpret_cast<const char*>(&code_load), sizeof(code_load));
247 : LogWriteBytes(code_name, length);
248 : LogWriteBytes(string_terminator, 1);
249 0 : LogWriteBytes(reinterpret_cast<const char*>(code_pointer), code_size);
250 : }
251 :
252 : namespace {
253 :
254 0 : std::unique_ptr<char[]> GetScriptName(Handle<Script> script) {
255 0 : Object* name_or_url = script->GetNameOrSourceURL();
256 0 : int name_length = 0;
257 : std::unique_ptr<char[]> name_string;
258 0 : if (name_or_url->IsString()) {
259 : return String::cast(name_or_url)
260 0 : ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &name_length);
261 : } else {
262 0 : const char unknown[] = "<unknown>";
263 0 : name_length = static_cast<int>(strlen(unknown));
264 0 : char* buffer = NewArray<char>(name_length);
265 : base::OS::StrNCpy(buffer, name_length + 1, unknown,
266 0 : static_cast<size_t>(name_length));
267 : return std::unique_ptr<char[]>(buffer);
268 : }
269 : }
270 :
271 0 : SourcePositionInfo GetSourcePositionInfo(Handle<Code> code,
272 : Handle<SharedFunctionInfo> function,
273 : SourcePosition pos) {
274 0 : if (code->is_turbofanned() || code->is_crankshafted()) {
275 : DisallowHeapAllocation disallow;
276 0 : return pos.InliningStack(code)[0];
277 : } else {
278 0 : return SourcePositionInfo(pos, function);
279 : }
280 : }
281 :
282 : } // namespace
283 :
284 0 : void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) {
285 : // Compute the entry count and get the name of the script.
286 : uint32_t entry_count = 0;
287 0 : for (SourcePositionTableIterator iterator(code->SourcePositionTable());
288 0 : !iterator.done(); iterator.Advance()) {
289 0 : entry_count++;
290 : }
291 0 : if (entry_count == 0) return;
292 : Handle<Script> script(Script::cast(shared->script()));
293 :
294 : PerfJitCodeDebugInfo debug_info;
295 :
296 0 : debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
297 0 : debug_info.time_stamp_ = GetTimestamp();
298 0 : debug_info.address_ = reinterpret_cast<uint64_t>(code->instruction_start());
299 0 : debug_info.entry_count_ = entry_count;
300 :
301 : uint32_t size = sizeof(debug_info);
302 : // Add the sizes of fixed parts of entries.
303 0 : size += entry_count * sizeof(PerfJitDebugEntry);
304 : // Add the size of the name after each entry.
305 :
306 : Handle<Code> code_handle(code);
307 : Handle<SharedFunctionInfo> function_handle(shared);
308 0 : for (SourcePositionTableIterator iterator(code->SourcePositionTable());
309 0 : !iterator.done(); iterator.Advance()) {
310 : SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle,
311 0 : iterator.source_position()));
312 : Handle<Script> script(Script::cast(info.function->script()));
313 0 : std::unique_ptr<char[]> name_string = GetScriptName(script);
314 0 : size += (static_cast<uint32_t>(strlen(name_string.get())) + 1);
315 : }
316 :
317 0 : int padding = ((size + 7) & (~7)) - size;
318 0 : debug_info.size_ = size + padding;
319 : LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
320 :
321 : Address code_start = code->instruction_start();
322 :
323 0 : for (SourcePositionTableIterator iterator(code->SourcePositionTable());
324 0 : !iterator.done(); iterator.Advance()) {
325 : SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle,
326 0 : iterator.source_position()));
327 : PerfJitDebugEntry entry;
328 : // The entry point of the function will be placed straight after the ELF
329 : // header when processed by "perf inject". Adjust the position addresses
330 : // accordingly.
331 : entry.address_ = reinterpret_cast<intptr_t>(
332 0 : code_start + iterator.code_offset() + kElfHeaderSize);
333 0 : entry.line_number_ = info.line + 1;
334 0 : entry.column_ = info.column + 1;
335 : LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
336 : Handle<Script> script(Script::cast(info.function->script()));
337 0 : std::unique_ptr<char[]> name_string = GetScriptName(script);
338 : LogWriteBytes(name_string.get(),
339 0 : static_cast<uint32_t>(strlen(name_string.get())) + 1);
340 : }
341 0 : char padding_bytes[] = "\0\0\0\0\0\0\0\0";
342 : LogWriteBytes(padding_bytes, padding);
343 : }
344 :
345 0 : void PerfJitLogger::LogWriteUnwindingInfo(Code* code) {
346 : PerfJitCodeUnwindingInfo unwinding_info_header;
347 0 : unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
348 0 : unwinding_info_header.time_stamp_ = GetTimestamp();
349 0 : unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize;
350 :
351 0 : if (code->has_unwinding_info()) {
352 0 : unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
353 0 : unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
354 : } else {
355 0 : unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize;
356 0 : unwinding_info_header.mapped_size_ = 0;
357 : }
358 :
359 : int content_size = static_cast<int>(sizeof(unwinding_info_header) +
360 0 : unwinding_info_header.unwinding_size_);
361 0 : int padding_size = RoundUp(content_size, 8) - content_size;
362 0 : unwinding_info_header.size_ = content_size + padding_size;
363 :
364 : LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
365 : sizeof(unwinding_info_header));
366 :
367 0 : if (code->has_unwinding_info()) {
368 : LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()),
369 : code->unwinding_info_size());
370 : } else {
371 0 : OFStream perf_output_stream(perf_output_handle_);
372 0 : EhFrameWriter::WriteEmptyEhFrame(perf_output_stream);
373 : }
374 :
375 0 : char padding_bytes[] = "\0\0\0\0\0\0\0\0";
376 : DCHECK_LT(padding_size, static_cast<int>(sizeof(padding_bytes)));
377 : LogWriteBytes(padding_bytes, static_cast<int>(padding_size));
378 0 : }
379 :
380 0 : void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
381 : // Code relocation not supported.
382 0 : UNREACHABLE();
383 : }
384 :
385 0 : void PerfJitLogger::LogWriteBytes(const char* bytes, int size) {
386 0 : size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
387 : DCHECK(static_cast<size_t>(size) == rv);
388 : USE(rv);
389 0 : }
390 :
391 0 : void PerfJitLogger::LogWriteHeader() {
392 : DCHECK(perf_output_handle_ != NULL);
393 : PerfJitHeader header;
394 :
395 0 : header.magic_ = PerfJitHeader::kMagic;
396 0 : header.version_ = PerfJitHeader::kVersion;
397 0 : header.size_ = sizeof(header);
398 0 : header.elf_mach_target_ = GetElfMach();
399 0 : header.reserved_ = 0xdeadbeef;
400 0 : header.process_id_ = base::OS::GetCurrentProcessId();
401 : header.time_stamp_ =
402 0 : static_cast<uint64_t>(base::OS::TimeCurrentMillis() * 1000.0);
403 0 : header.flags_ = 0;
404 :
405 : LogWriteBytes(reinterpret_cast<const char*>(&header), sizeof(header));
406 0 : }
407 :
408 : #endif // V8_OS_LINUX
409 : } // namespace internal
410 : } // namespace v8
|