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 : #undef MAP_TYPE // jumbo: conflicts with v8::internal::InstanceType::MAP_TYPE
41 : #include <unistd.h>
42 : #endif // V8_OS_LINUX
43 :
44 : namespace v8 {
45 : namespace internal {
46 :
47 : #if V8_OS_LINUX
48 :
49 : struct PerfJitHeader {
50 : uint32_t magic_;
51 : uint32_t version_;
52 : uint32_t size_;
53 : uint32_t elf_mach_target_;
54 : uint32_t reserved_;
55 : uint32_t process_id_;
56 : uint64_t time_stamp_;
57 : uint64_t flags_;
58 :
59 : static const uint32_t kMagic = 0x4A695444;
60 : static const uint32_t kVersion = 1;
61 : };
62 :
63 : struct PerfJitBase {
64 : enum PerfJitEvent {
65 : kLoad = 0,
66 : kMove = 1,
67 : kDebugInfo = 2,
68 : kClose = 3,
69 : kUnwindingInfo = 4
70 : };
71 :
72 : uint32_t event_;
73 : uint32_t size_;
74 : uint64_t time_stamp_;
75 : };
76 :
77 : struct PerfJitCodeLoad : PerfJitBase {
78 : uint32_t process_id_;
79 : uint32_t thread_id_;
80 : uint64_t vma_;
81 : uint64_t code_address_;
82 : uint64_t code_size_;
83 : uint64_t code_id_;
84 : };
85 :
86 : struct PerfJitDebugEntry {
87 : uint64_t address_;
88 : int line_number_;
89 : int column_;
90 : // Followed by null-terminated name or \0xff\0 if same as previous.
91 : };
92 :
93 : struct PerfJitCodeDebugInfo : PerfJitBase {
94 : uint64_t address_;
95 : uint64_t entry_count_;
96 : // Followed by entry_count_ instances of PerfJitDebugEntry.
97 : };
98 :
99 : struct PerfJitCodeUnwindingInfo : PerfJitBase {
100 : uint64_t unwinding_size_;
101 : uint64_t eh_frame_hdr_size_;
102 : uint64_t mapped_size_;
103 : // Followed by size_ - sizeof(PerfJitCodeUnwindingInfo) bytes of data.
104 : };
105 :
106 : const char PerfJitLogger::kFilenameFormatString[] = "./jit-%d.dump";
107 :
108 : // Extra padding for the PID in the filename
109 : const int PerfJitLogger::kFilenameBufferPadding = 16;
110 :
111 : base::LazyRecursiveMutex PerfJitLogger::file_mutex_;
112 : // The following static variables are protected by PerfJitLogger::file_mutex_.
113 : uint64_t PerfJitLogger::reference_count_ = 0;
114 : void* PerfJitLogger::marker_address_ = nullptr;
115 : uint64_t PerfJitLogger::code_index_ = 0;
116 : FILE* PerfJitLogger::perf_output_handle_ = nullptr;
117 :
118 0 : void PerfJitLogger::OpenJitDumpFile() {
119 : // Open the perf JIT dump file.
120 0 : perf_output_handle_ = nullptr;
121 :
122 : int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding;
123 : ScopedVector<char> perf_dump_name(bufferSize);
124 : int size = SNPrintF(perf_dump_name, kFilenameFormatString,
125 0 : base::OS::GetCurrentProcessId());
126 0 : CHECK_NE(size, -1);
127 :
128 : int fd = open(perf_dump_name.start(), O_CREAT | O_TRUNC | O_RDWR, 0666);
129 0 : if (fd == -1) return;
130 :
131 0 : marker_address_ = OpenMarkerFile(fd);
132 0 : if (marker_address_ == nullptr) return;
133 :
134 0 : perf_output_handle_ = fdopen(fd, "w+");
135 0 : if (perf_output_handle_ == nullptr) return;
136 :
137 0 : setvbuf(perf_output_handle_, nullptr, _IOFBF, kLogBufferSize);
138 : }
139 :
140 0 : void PerfJitLogger::CloseJitDumpFile() {
141 0 : if (perf_output_handle_ == nullptr) return;
142 0 : fclose(perf_output_handle_);
143 0 : perf_output_handle_ = nullptr;
144 : }
145 :
146 0 : void* PerfJitLogger::OpenMarkerFile(int fd) {
147 0 : long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
148 0 : if (page_size == -1) return nullptr;
149 :
150 : // Mmap the file so that there is a mmap record in the perf_data file.
151 : //
152 : // The map must be PROT_EXEC to ensure it is not ignored by perf record.
153 : void* marker_address =
154 0 : mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
155 0 : return (marker_address == MAP_FAILED) ? nullptr : marker_address;
156 : }
157 :
158 0 : void PerfJitLogger::CloseMarkerFile(void* marker_address) {
159 0 : if (marker_address == nullptr) return;
160 0 : long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
161 0 : if (page_size == -1) return;
162 0 : munmap(marker_address, page_size);
163 : }
164 :
165 0 : PerfJitLogger::PerfJitLogger() {
166 : base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
167 :
168 0 : reference_count_++;
169 : // If this is the first logger, open the file and write the header.
170 0 : if (reference_count_ == 1) {
171 0 : OpenJitDumpFile();
172 0 : if (perf_output_handle_ == nullptr) return;
173 0 : LogWriteHeader();
174 : }
175 : }
176 :
177 0 : PerfJitLogger::~PerfJitLogger() {
178 : base::LockGuard<base::RecursiveMutex> guard_file(file_mutex_.Pointer());
179 :
180 0 : reference_count_--;
181 : // If this was the last logger, close the file.
182 0 : if (reference_count_ == 0) {
183 : CloseJitDumpFile();
184 : }
185 0 : }
186 :
187 0 : uint64_t PerfJitLogger::GetTimestamp() {
188 : struct timespec ts;
189 0 : int result = clock_gettime(CLOCK_MONOTONIC, &ts);
190 : DCHECK_EQ(0, result);
191 : USE(result);
192 : static const uint64_t kNsecPerSec = 1000000000;
193 0 : return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
194 : }
195 :
196 0 : void PerfJitLogger::LogRecordedBuffer(AbstractCode* abstract_code,
197 : SharedFunctionInfo* shared,
198 : const char* name, int length) {
199 0 : if (FLAG_perf_basic_prof_only_functions &&
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 Turbofan will have the safepoint table directly after
222 : // instructions. There is no need to record the safepoint table itself.
223 : uint32_t code_size = code->is_turbofanned() ? code->safepoint_table_offset()
224 0 : : code->instruction_size();
225 :
226 : // Unwinding info comes right after debug info.
227 0 : if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(code);
228 :
229 : static const char string_terminator[] = "\0";
230 :
231 : PerfJitCodeLoad code_load;
232 0 : code_load.event_ = PerfJitCodeLoad::kLoad;
233 0 : code_load.size_ = sizeof(code_load) + length + 1 + code_size;
234 0 : code_load.time_stamp_ = GetTimestamp();
235 : code_load.process_id_ =
236 0 : static_cast<uint32_t>(base::OS::GetCurrentProcessId());
237 0 : code_load.thread_id_ = static_cast<uint32_t>(base::OS::GetCurrentThreadId());
238 0 : code_load.vma_ = 0x0; // Our addresses are absolute.
239 0 : code_load.code_address_ = reinterpret_cast<uint64_t>(code_pointer);
240 0 : code_load.code_size_ = code_size;
241 0 : code_load.code_id_ = code_index_;
242 :
243 0 : code_index_++;
244 :
245 : LogWriteBytes(reinterpret_cast<const char*>(&code_load), sizeof(code_load));
246 : LogWriteBytes(code_name, length);
247 : LogWriteBytes(string_terminator, 1);
248 0 : LogWriteBytes(reinterpret_cast<const char*>(code_pointer), code_size);
249 : }
250 :
251 : namespace {
252 :
253 0 : std::unique_ptr<char[]> GetScriptName(Handle<Script> script) {
254 0 : Object* name_or_url = script->GetNameOrSourceURL();
255 0 : int name_length = 0;
256 : std::unique_ptr<char[]> name_string;
257 0 : if (name_or_url->IsString()) {
258 : return String::cast(name_or_url)
259 0 : ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &name_length);
260 : } else {
261 0 : const char unknown[] = "<unknown>";
262 0 : name_length = static_cast<int>(strlen(unknown));
263 0 : char* buffer = NewArray<char>(name_length);
264 : base::OS::StrNCpy(buffer, name_length + 1, unknown,
265 0 : static_cast<size_t>(name_length));
266 : return std::unique_ptr<char[]>(buffer);
267 : }
268 : }
269 :
270 0 : SourcePositionInfo GetSourcePositionInfo(Handle<Code> code,
271 : Handle<SharedFunctionInfo> function,
272 : SourcePosition pos) {
273 0 : if (code->is_turbofanned()) {
274 : DisallowHeapAllocation disallow;
275 0 : return pos.InliningStack(code)[0];
276 : } else {
277 0 : return SourcePositionInfo(pos, function);
278 : }
279 : }
280 :
281 : } // namespace
282 :
283 0 : void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) {
284 : // Compute the entry count and get the name of the script.
285 : uint32_t entry_count = 0;
286 0 : for (SourcePositionTableIterator iterator(code->SourcePositionTable());
287 0 : !iterator.done(); iterator.Advance()) {
288 0 : entry_count++;
289 : }
290 0 : if (entry_count == 0) return;
291 : Handle<Script> script(Script::cast(shared->script()));
292 :
293 : PerfJitCodeDebugInfo debug_info;
294 :
295 0 : debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
296 0 : debug_info.time_stamp_ = GetTimestamp();
297 0 : debug_info.address_ = reinterpret_cast<uint64_t>(code->instruction_start());
298 0 : debug_info.entry_count_ = entry_count;
299 :
300 : uint32_t size = sizeof(debug_info);
301 : // Add the sizes of fixed parts of entries.
302 0 : size += entry_count * sizeof(PerfJitDebugEntry);
303 : // Add the size of the name after each entry.
304 :
305 : Handle<Code> code_handle(code);
306 : Handle<SharedFunctionInfo> function_handle(shared);
307 0 : for (SourcePositionTableIterator iterator(code->SourcePositionTable());
308 0 : !iterator.done(); iterator.Advance()) {
309 : SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle,
310 0 : iterator.source_position()));
311 : Handle<Script> script(Script::cast(info.function->script()));
312 0 : std::unique_ptr<char[]> name_string = GetScriptName(script);
313 0 : size += (static_cast<uint32_t>(strlen(name_string.get())) + 1);
314 : }
315 :
316 0 : int padding = ((size + 7) & (~7)) - size;
317 0 : debug_info.size_ = size + padding;
318 : LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
319 :
320 : Address code_start = code->instruction_start();
321 :
322 0 : for (SourcePositionTableIterator iterator(code->SourcePositionTable());
323 0 : !iterator.done(); iterator.Advance()) {
324 : SourcePositionInfo info(GetSourcePositionInfo(code_handle, function_handle,
325 0 : iterator.source_position()));
326 : PerfJitDebugEntry entry;
327 : // The entry point of the function will be placed straight after the ELF
328 : // header when processed by "perf inject". Adjust the position addresses
329 : // accordingly.
330 : entry.address_ = reinterpret_cast<intptr_t>(
331 0 : code_start + iterator.code_offset() + kElfHeaderSize);
332 0 : entry.line_number_ = info.line + 1;
333 0 : entry.column_ = info.column + 1;
334 : LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
335 : Handle<Script> script(Script::cast(info.function->script()));
336 0 : std::unique_ptr<char[]> name_string = GetScriptName(script);
337 : LogWriteBytes(name_string.get(),
338 0 : static_cast<uint32_t>(strlen(name_string.get())) + 1);
339 : }
340 0 : char padding_bytes[] = "\0\0\0\0\0\0\0\0";
341 : LogWriteBytes(padding_bytes, padding);
342 : }
343 :
344 0 : void PerfJitLogger::LogWriteUnwindingInfo(Code* code) {
345 : PerfJitCodeUnwindingInfo unwinding_info_header;
346 0 : unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
347 0 : unwinding_info_header.time_stamp_ = GetTimestamp();
348 0 : unwinding_info_header.eh_frame_hdr_size_ = EhFrameConstants::kEhFrameHdrSize;
349 :
350 0 : if (code->has_unwinding_info()) {
351 0 : unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
352 0 : unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
353 : } else {
354 0 : unwinding_info_header.unwinding_size_ = EhFrameConstants::kEhFrameHdrSize;
355 0 : unwinding_info_header.mapped_size_ = 0;
356 : }
357 :
358 : int content_size = static_cast<int>(sizeof(unwinding_info_header) +
359 0 : unwinding_info_header.unwinding_size_);
360 0 : int padding_size = RoundUp(content_size, 8) - content_size;
361 0 : unwinding_info_header.size_ = content_size + padding_size;
362 :
363 : LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
364 : sizeof(unwinding_info_header));
365 :
366 0 : if (code->has_unwinding_info()) {
367 : LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()),
368 : code->unwinding_info_size());
369 : } else {
370 0 : OFStream perf_output_stream(perf_output_handle_);
371 0 : EhFrameWriter::WriteEmptyEhFrame(perf_output_stream);
372 : }
373 :
374 0 : char padding_bytes[] = "\0\0\0\0\0\0\0\0";
375 : DCHECK_LT(padding_size, static_cast<int>(sizeof(padding_bytes)));
376 : LogWriteBytes(padding_bytes, static_cast<int>(padding_size));
377 0 : }
378 :
379 0 : void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
380 : // Code relocation not supported.
381 0 : UNREACHABLE();
382 : }
383 :
384 0 : void PerfJitLogger::LogWriteBytes(const char* bytes, int size) {
385 0 : size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
386 : DCHECK(static_cast<size_t>(size) == rv);
387 : USE(rv);
388 0 : }
389 :
390 0 : void PerfJitLogger::LogWriteHeader() {
391 : DCHECK_NOT_NULL(perf_output_handle_);
392 : PerfJitHeader header;
393 :
394 0 : header.magic_ = PerfJitHeader::kMagic;
395 0 : header.version_ = PerfJitHeader::kVersion;
396 0 : header.size_ = sizeof(header);
397 0 : header.elf_mach_target_ = GetElfMach();
398 0 : header.reserved_ = 0xdeadbeef;
399 0 : header.process_id_ = base::OS::GetCurrentProcessId();
400 : header.time_stamp_ =
401 0 : static_cast<uint64_t>(V8::GetCurrentPlatform()->CurrentClockTimeMillis() *
402 0 : base::Time::kMicrosecondsPerMillisecond);
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
|