Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1007)

Unified Diff: src/perf-jit.cc

Issue 1809203007: Linux perf integration with the new support for JIT. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Fixes Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/perf-jit.h ('k') | tools/gyp/v8.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/perf-jit.cc
diff --git a/src/perf-jit.cc b/src/perf-jit.cc
new file mode 100644
index 0000000000000000000000000000000000000000..4ba9909e16dd1e09e839a86ed9b968041d100d9d
--- /dev/null
+++ b/src/perf-jit.cc
@@ -0,0 +1,321 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/perf-jit.h"
+
+#include "src/assembler.h"
+#include "src/objects-inl.h"
+
+#if V8_OS_LINUX
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#endif // V8_OS_LINUX
+
+namespace v8 {
+namespace internal {
+
+#if V8_OS_LINUX
+
+struct PerfJitHeader {
+ uint32_t magic_;
+ uint32_t version_;
+ uint32_t size_;
+ uint32_t elf_mach_target_;
+ uint32_t reserved_;
+ uint32_t process_id_;
+ uint64_t time_stamp_;
+ uint64_t flags_;
+
+ static const uint32_t kMagic = 0x4A695444;
+ static const uint32_t kVersion = 1;
+};
+
+struct PerfJitBase {
+ enum PerfJitEvent { kLoad = 0, kMove = 1, kDebugInfo = 2, kClose = 3 };
+
+ uint32_t event_;
+ uint32_t size_;
+ uint64_t time_stamp_;
+};
+
+struct PerfJitCodeLoad : PerfJitBase {
+ uint32_t process_id_;
+ uint32_t thread_id_;
+ uint64_t vma_;
+ uint64_t code_address_;
+ uint64_t code_size_;
+ uint64_t code_id_;
+};
+
+struct PerfJitDebugEntry {
+ uint64_t address_;
+ int line_number_;
+ int column_;
+ // Followed by null-terminated name or \0xff\0 if same as previous.
+};
+
+struct PerfJitCodeDebugInfo : PerfJitBase {
+ uint64_t address_;
+ uint64_t entry_count_;
+ // Followed by entry_count_ instances of PerfJitDebugEntry.
+};
+
+const char PerfJitLogger::kFilenameFormatString[] = "./jit-%d.dump";
+
+// Extra padding for the PID in the filename
+const int PerfJitLogger::kFilenameBufferPadding = 16;
+
+void PerfJitLogger::OpenJitDumpFile() {
+ // Open the perf JIT dump file.
+ perf_output_handle_ = nullptr;
+
+ int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding;
+ ScopedVector<char> perf_dump_name(bufferSize);
+ int size = SNPrintF(perf_dump_name, kFilenameFormatString,
+ base::OS::GetCurrentProcessId());
+ CHECK_NE(size, -1);
+
+ int fd = open(perf_dump_name.start(), O_CREAT | O_TRUNC | O_RDWR, 0666);
+ if (fd == -1) return;
+
+ marker_address_ = OpenMarkerFile(fd);
+ if (marker_address_ == nullptr) return;
+
+ perf_output_handle_ = fdopen(fd, "w+");
+ if (perf_output_handle_ == nullptr) return;
+
+ setvbuf(perf_output_handle_, NULL, _IOFBF, kLogBufferSize);
+}
+
+void PerfJitLogger::CloseJitDumpFile() {
+ if (perf_output_handle_ == nullptr) return;
+ fclose(perf_output_handle_);
+ perf_output_handle_ = nullptr;
+}
+
+void* PerfJitLogger::OpenMarkerFile(int fd) {
+ long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
+ if (page_size == -1) return nullptr;
+
+ // Mmap the file so that there is a mmap record in the perf_data file.
+ //
+ // The map must be PROT_EXEC to ensure it is not ignored by perf record.
+ void* marker_address =
+ mmap(nullptr, page_size, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
+ return (marker_address == MAP_FAILED) ? nullptr : marker_address;
+}
+
+void PerfJitLogger::CloseMarkerFile(void* marker_address) {
+ if (marker_address == nullptr) return;
+ long page_size = sysconf(_SC_PAGESIZE); // NOLINT(runtime/int)
+ if (page_size == -1) return;
+ munmap(marker_address, page_size);
+}
+
+PerfJitLogger::PerfJitLogger()
+ : perf_output_handle_(nullptr), code_index_(0), marker_address_(nullptr) {
+ OpenJitDumpFile();
+ if (perf_output_handle_ == nullptr) return;
+ LogWriteHeader();
+}
+
+PerfJitLogger::~PerfJitLogger() { CloseJitDumpFile(); }
+
+uint64_t PerfJitLogger::GetTimestamp() {
+ struct timespec ts;
+ int result = clock_gettime(CLOCK_MONOTONIC, &ts);
+ DCHECK_EQ(0, result);
+ USE(result);
+ static const uint64_t kNsecPerSec = 1000000000;
+ return (ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
+}
+
+void PerfJitLogger::LogRecordedBuffer(AbstractCode* abstract_code,
+ SharedFunctionInfo* shared,
+ const char* name, int length) {
+ if (perf_output_handle_ == nullptr) return;
+ if (FLAG_perf_basic_prof_only_functions &&
+ (abstract_code->kind() != AbstractCode::FUNCTION &&
+ abstract_code->kind() != AbstractCode::INTERPRETED_FUNCTION &&
+ abstract_code->kind() != AbstractCode::OPTIMIZED_FUNCTION)) {
+ return;
+ }
+
+ // We only support non-interpreted functions.
+ if (!abstract_code->IsCode()) return;
+ Code* code = abstract_code->GetCode();
+ DCHECK(code->instruction_start() == code->address() + Code::kHeaderSize);
+
+ const char* code_name = name;
+ uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code->instruction_start());
+ uint32_t code_size = code->is_crankshafted() ? code->safepoint_table_offset()
+ : code->instruction_size();
+
+ static const char string_terminator[] = "\0";
+
+ PerfJitCodeLoad code_load;
+ code_load.event_ = PerfJitCodeLoad::kLoad;
+ code_load.size_ = sizeof(code_load) + length + 1 + code_size;
+ code_load.time_stamp_ = GetTimestamp();
+ code_load.process_id_ =
+ static_cast<uint32_t>(base::OS::GetCurrentProcessId());
+ code_load.thread_id_ = static_cast<uint32_t>(base::OS::GetCurrentThreadId());
+ code_load.vma_ = 0x0; // Our addresses are absolute.
+ code_load.code_address_ = reinterpret_cast<uint64_t>(code_pointer);
+ code_load.code_size_ = code_size;
+ code_load.code_id_ = code_index_;
+
+ code_index_++;
+
+ LogWriteBytes(reinterpret_cast<const char*>(&code_load), sizeof(code_load));
+ LogWriteBytes(code_name, length);
+ LogWriteBytes(string_terminator, 1);
+ LogWriteBytes(reinterpret_cast<const char*>(code_pointer), code_size);
+
+ if (FLAG_perf_prof_debug_info) {
+ LogWriteDebugInfo(code, shared);
+ }
+}
+
+void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) {
+ // Compute the entry count and get the name of the script.
+ uint32_t entry_count = 0;
+ for (RelocIterator it(code, RelocInfo::kPositionMask); !it.done();
+ it.next()) {
+ entry_count++;
+ }
+ if (entry_count == 0) return;
+ Handle<Script> script(Script::cast(shared->script()));
+ Handle<Object> name_or_url(Script::GetNameOrSourceURL(script));
+
+ int name_length = 0;
+ base::SmartArrayPointer<char> name_string;
+ if (name_or_url->IsString()) {
+ name_string =
+ Handle<String>::cast(name_or_url)
+ ->ToCString(DISALLOW_NULLS, FAST_STRING_TRAVERSAL, &name_length);
+ DCHECK_EQ(0, name_string.get()[name_length]);
+ } else {
+ const char unknown[] = "<unknown>";
+ name_length = static_cast<int>(strlen(unknown));
+ char* buffer = NewArray<char>(name_length);
+ base::OS::StrNCpy(buffer, name_length + 1, unknown,
+ static_cast<size_t>(name_length));
+ name_string = base::SmartArrayPointer<char>(buffer);
+ }
+ DCHECK_EQ(name_length, strlen(name_string.get()));
+
+ PerfJitCodeDebugInfo debug_info;
+
+ debug_info.event_ = PerfJitCodeLoad::kDebugInfo;
+ debug_info.time_stamp_ = GetTimestamp();
+ debug_info.address_ = reinterpret_cast<uint64_t>(code->instruction_start());
+ debug_info.entry_count_ = entry_count;
+
+ uint32_t size = sizeof(debug_info);
+ // Add the sizes of fixed parts of entries.
+ size += entry_count * sizeof(PerfJitDebugEntry);
+ // Add the size of the name after the first entry.
+ size += static_cast<uint32_t>(name_length) + 1;
+ // Add the sizes of the links to previous name (\0xff\0).
+ size += (entry_count - 1) * 2;
+
+ int padding = ((size + 7) & (~7)) - size;
+
+ debug_info.size_ = size + padding;
+
+ LogWriteBytes(reinterpret_cast<const char*>(&debug_info), sizeof(debug_info));
+
+ int script_line_offset = script->line_offset();
+ Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends()));
+
+ bool is_first = true;
+ for (RelocIterator it(code, RelocInfo::kPositionMask); !it.done();
+ it.next()) {
+ int position = static_cast<int>(it.rinfo()->data());
+ int line_number = Script::GetLineNumber(script, position);
+ // Compute column.
+ int relative_line_number = line_number - script_line_offset;
+ int start =
+ (relative_line_number == 0)
+ ? 0
+ : Smi::cast(line_ends->get(relative_line_number - 1))->value() + 1;
+ int column_offset = position - start;
+ if (relative_line_number == 0) {
+ // For the case where the code is on the same line as the script tag.
+ column_offset += script->column_offset();
+ }
+
+ PerfJitDebugEntry entry;
+ entry.address_ = reinterpret_cast<uint64_t>(it.rinfo()->pc());
+ entry.line_number_ = line_number;
+ entry.column_ = column_offset;
+ LogWriteBytes(reinterpret_cast<const char*>(&entry), sizeof(entry));
+
+ if (is_first) {
+ is_first = false;
+ LogWriteBytes(name_string.get(), name_length + 1);
+ } else {
+ LogWriteBytes("\xff", 2);
+ }
+ }
+ char padding_bytes[] = "\0\0\0\0\0\0\0\0";
+ LogWriteBytes(padding_bytes, padding);
+}
+
+void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
+ // Code relocation not supported.
+ UNREACHABLE();
+}
+
+void PerfJitLogger::LogWriteBytes(const char* bytes, int size) {
+ size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
+ DCHECK(static_cast<size_t>(size) == rv);
+ USE(rv);
+}
+
+void PerfJitLogger::LogWriteHeader() {
+ DCHECK(perf_output_handle_ != NULL);
+ PerfJitHeader header;
+
+ header.magic_ = PerfJitHeader::kMagic;
+ header.version_ = PerfJitHeader::kVersion;
+ header.size_ = sizeof(header);
+ header.elf_mach_target_ = GetElfMach();
+ header.reserved_ = 0xdeadbeef;
+ header.process_id_ = base::OS::GetCurrentProcessId();
+ header.time_stamp_ =
+ static_cast<uint64_t>(base::OS::TimeCurrentMillis() * 1000.0);
+ header.flags_ = 0;
+
+ LogWriteBytes(reinterpret_cast<const char*>(&header), sizeof(header));
+}
+
+#endif // V8_OS_LINUX
+} // namespace internal
+} // namespace v8
« no previous file with comments | « src/perf-jit.h ('k') | tools/gyp/v8.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698