Linux perf integration with the new support for JIT.
Difference from --perf-basic-prof: - correctly attributes samples when code space gets reused (when unused code object dies and a new code objects is allocated at the same place). - outputs compiled machine code for instruction-level profile. Just like --perf-basic-prof, the file writer is not synchronized (even worse, there is a per-isolate file handle), so we will run into trouble with multiple isolates. However, this patch is still an improvement on --perf-basic-prof, and it should be fine to replace ll-prof. The patch also introduces experimental support for debug info, but it does not seem to be picked by the perf tool. Usage: You need the perf tool from Linux kernel >4.5. Then run: $ perf record -k mono d8 --perf-prof <your JS file> $ perf inject -j -i perf.data -o perf.data.jitted $ perf report -i perf.data.jitted Some explanations: The "-k mono" switch from "perf record" tells the perf tool to use the monotonic clock for perf sample timestamping. The "perf inject -j" command injects the collected code events into the perf data file, writing the output into perf.data.jitted. The perf report command then creates the report. Review URL: https://codereview.chromium.org/1809203007 Cr-Commit-Position: refs/heads/master@{#35091}
This commit is contained in:
parent
43216574ef
commit
82e95f597b
2
BUILD.gn
2
BUILD.gn
@ -1198,6 +1198,8 @@ source_set("v8_base") {
|
||||
"src/parsing/token.h",
|
||||
"src/pending-compilation-error-handler.cc",
|
||||
"src/pending-compilation-error-handler.h",
|
||||
"src/perf-jit.cc",
|
||||
"src/perf-jit.h",
|
||||
"src/profiler/allocation-tracker.cc",
|
||||
"src/profiler/allocation-tracker.h",
|
||||
"src/profiler/circular-queue-inl.h",
|
||||
|
@ -520,14 +520,6 @@ bool TimeTicks::IsHighResolutionClockWorking() {
|
||||
return high_res_tick_clock.Pointer()->IsHighResolution();
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
TimeTicks TimeTicks::KernelTimestampNow() { return TimeTicks(0); }
|
||||
|
||||
|
||||
// static
|
||||
bool TimeTicks::KernelTimestampAvailable() { return false; }
|
||||
|
||||
#else // V8_OS_WIN
|
||||
|
||||
TimeTicks TimeTicks::Now() {
|
||||
@ -566,82 +558,6 @@ bool TimeTicks::IsHighResolutionClockWorking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if V8_OS_LINUX
|
||||
|
||||
class KernelTimestampClock {
|
||||
public:
|
||||
KernelTimestampClock() : clock_fd_(-1), clock_id_(kClockInvalid) {
|
||||
clock_fd_ = open(kTraceClockDevice, O_RDONLY);
|
||||
if (clock_fd_ == -1) {
|
||||
return;
|
||||
}
|
||||
clock_id_ = get_clockid(clock_fd_);
|
||||
}
|
||||
|
||||
virtual ~KernelTimestampClock() {
|
||||
if (clock_fd_ != -1) {
|
||||
close(clock_fd_);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t Now() {
|
||||
if (clock_id_ == kClockInvalid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(clock_id_, &ts);
|
||||
return ((int64_t)ts.tv_sec * kNsecPerSec) + ts.tv_nsec;
|
||||
}
|
||||
|
||||
bool Available() { return clock_id_ != kClockInvalid; }
|
||||
|
||||
private:
|
||||
static const clockid_t kClockInvalid = -1;
|
||||
static const char kTraceClockDevice[];
|
||||
static const uint64_t kNsecPerSec = 1000000000;
|
||||
|
||||
int clock_fd_;
|
||||
clockid_t clock_id_;
|
||||
|
||||
static int get_clockid(int fd) { return ((~(clockid_t)(fd) << 3) | 3); }
|
||||
};
|
||||
|
||||
|
||||
// Timestamp module name
|
||||
const char KernelTimestampClock::kTraceClockDevice[] = "/dev/trace_clock";
|
||||
|
||||
#else
|
||||
|
||||
class KernelTimestampClock {
|
||||
public:
|
||||
KernelTimestampClock() {}
|
||||
|
||||
int64_t Now() { return 0; }
|
||||
bool Available() { return false; }
|
||||
};
|
||||
|
||||
#endif // V8_OS_LINUX
|
||||
|
||||
static LazyStaticInstance<KernelTimestampClock,
|
||||
DefaultConstructTrait<KernelTimestampClock>,
|
||||
ThreadSafeInitOnceTrait>::type kernel_tick_clock =
|
||||
LAZY_STATIC_INSTANCE_INITIALIZER;
|
||||
|
||||
|
||||
// static
|
||||
TimeTicks TimeTicks::KernelTimestampNow() {
|
||||
return TimeTicks(kernel_tick_clock.Pointer()->Now());
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
bool TimeTicks::KernelTimestampAvailable() {
|
||||
return kernel_tick_clock.Pointer()->Available();
|
||||
}
|
||||
|
||||
#endif // V8_OS_WIN
|
||||
|
||||
} // namespace base
|
||||
|
@ -318,13 +318,6 @@ class TimeTicks final {
|
||||
// Returns true if the high-resolution clock is working on this system.
|
||||
static bool IsHighResolutionClockWorking();
|
||||
|
||||
// Returns Linux kernel timestamp for generating profiler events. This method
|
||||
// returns null TimeTicks if the kernel cannot provide the timestamps (e.g.,
|
||||
// on non-Linux OS or if the kernel module for timestamps is not loaded).
|
||||
|
||||
static TimeTicks KernelTimestampNow();
|
||||
static bool KernelTimestampAvailable();
|
||||
|
||||
// Returns true if this object has not been initialized.
|
||||
bool IsNull() const { return ticks_ == 0; }
|
||||
|
||||
|
@ -1024,6 +1024,11 @@ DEFINE_NEG_IMPLICATION(perf_basic_prof, compact_code_space)
|
||||
DEFINE_BOOL(perf_basic_prof_only_functions, false,
|
||||
"Only report function code ranges to perf (i.e. no stubs).")
|
||||
DEFINE_IMPLICATION(perf_basic_prof_only_functions, perf_basic_prof)
|
||||
DEFINE_BOOL(perf_prof, false,
|
||||
"Enable perf linux profiler (experimental annotate support).")
|
||||
DEFINE_NEG_IMPLICATION(perf_prof, compact_code_space)
|
||||
DEFINE_BOOL(perf_prof_debug_info, false,
|
||||
"Enable debug info for perf linux profiler (experimental).")
|
||||
DEFINE_STRING(gc_fake_mmap, "/tmp/__v8_gc__",
|
||||
"Specify the name of the file for fake gc mmap used in ll_prof")
|
||||
DEFINE_BOOL(log_internal_timer_events, false, "Time internal events.")
|
||||
|
@ -30,7 +30,7 @@ class Log {
|
||||
static bool InitLogAtStart() {
|
||||
return FLAG_log || FLAG_log_api || FLAG_log_code || FLAG_log_gc ||
|
||||
FLAG_log_handles || FLAG_log_suspect || FLAG_log_regexp ||
|
||||
FLAG_ll_prof || FLAG_perf_basic_prof ||
|
||||
FLAG_ll_prof || FLAG_perf_basic_prof || FLAG_perf_prof ||
|
||||
FLAG_log_internal_timer_events || FLAG_prof_cpp;
|
||||
}
|
||||
|
||||
|
37
src/log.cc
37
src/log.cc
@ -18,6 +18,7 @@
|
||||
#include "src/log-inl.h"
|
||||
#include "src/log-utils.h"
|
||||
#include "src/macro-assembler.h"
|
||||
#include "src/perf-jit.h"
|
||||
#include "src/profiler/cpu-profiler.h"
|
||||
#include "src/runtime-profiler.h"
|
||||
#include "src/string-stream.h"
|
||||
@ -730,19 +731,18 @@ void Profiler::Run() {
|
||||
//
|
||||
|
||||
Logger::Logger(Isolate* isolate)
|
||||
: isolate_(isolate),
|
||||
ticker_(NULL),
|
||||
profiler_(NULL),
|
||||
log_events_(NULL),
|
||||
is_logging_(false),
|
||||
log_(new Log(this)),
|
||||
perf_basic_logger_(NULL),
|
||||
ll_logger_(NULL),
|
||||
jit_logger_(NULL),
|
||||
listeners_(5),
|
||||
is_initialized_(false) {
|
||||
}
|
||||
|
||||
: isolate_(isolate),
|
||||
ticker_(NULL),
|
||||
profiler_(NULL),
|
||||
log_events_(NULL),
|
||||
is_logging_(false),
|
||||
log_(new Log(this)),
|
||||
perf_basic_logger_(NULL),
|
||||
perf_jit_logger_(NULL),
|
||||
ll_logger_(NULL),
|
||||
jit_logger_(NULL),
|
||||
listeners_(5),
|
||||
is_initialized_(false) {}
|
||||
|
||||
Logger::~Logger() {
|
||||
delete log_;
|
||||
@ -1797,6 +1797,11 @@ bool Logger::SetUp(Isolate* isolate) {
|
||||
addCodeEventListener(perf_basic_logger_);
|
||||
}
|
||||
|
||||
if (FLAG_perf_prof) {
|
||||
perf_jit_logger_ = new PerfJitLogger();
|
||||
addCodeEventListener(perf_jit_logger_);
|
||||
}
|
||||
|
||||
if (FLAG_ll_prof) {
|
||||
ll_logger_ = new LowLevelLogger(log_file_name.str().c_str());
|
||||
addCodeEventListener(ll_logger_);
|
||||
@ -1865,6 +1870,12 @@ FILE* Logger::TearDown() {
|
||||
perf_basic_logger_ = NULL;
|
||||
}
|
||||
|
||||
if (perf_jit_logger_) {
|
||||
removeCodeEventListener(perf_jit_logger_);
|
||||
delete perf_jit_logger_;
|
||||
perf_jit_logger_ = NULL;
|
||||
}
|
||||
|
||||
if (ll_logger_) {
|
||||
removeCodeEventListener(ll_logger_);
|
||||
delete ll_logger_;
|
||||
|
@ -145,6 +145,7 @@ struct TickSample;
|
||||
class JitLogger;
|
||||
class PerfBasicLogger;
|
||||
class LowLevelLogger;
|
||||
class PerfJitLogger;
|
||||
class Sampler;
|
||||
|
||||
class Logger {
|
||||
@ -393,6 +394,7 @@ class Logger {
|
||||
bool is_logging_;
|
||||
Log* log_;
|
||||
PerfBasicLogger* perf_basic_logger_;
|
||||
PerfJitLogger* perf_jit_logger_;
|
||||
LowLevelLogger* ll_logger_;
|
||||
JitLogger* jit_logger_;
|
||||
List<CodeEventListener*> listeners_;
|
||||
|
321
src/perf-jit.cc
Normal file
321
src/perf-jit.cc
Normal file
@ -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
|
118
src/perf-jit.h
Normal file
118
src/perf-jit.h
Normal file
@ -0,0 +1,118 @@
|
||||
// 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.
|
||||
|
||||
#ifndef V8_PERF_JIT_H_
|
||||
#define V8_PERF_JIT_H_
|
||||
|
||||
#include "src/log.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
#if V8_OS_LINUX
|
||||
|
||||
// Linux perf tool logging support
|
||||
class PerfJitLogger : public CodeEventLogger {
|
||||
public:
|
||||
PerfJitLogger();
|
||||
virtual ~PerfJitLogger();
|
||||
|
||||
void CodeMoveEvent(AbstractCode* from, Address to) override;
|
||||
void CodeDisableOptEvent(AbstractCode* code,
|
||||
SharedFunctionInfo* shared) override {}
|
||||
|
||||
private:
|
||||
void OpenJitDumpFile();
|
||||
void CloseJitDumpFile();
|
||||
void* OpenMarkerFile(int fd);
|
||||
void CloseMarkerFile(void* marker_address);
|
||||
|
||||
uint64_t GetTimestamp();
|
||||
void LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo* shared,
|
||||
const char* name, int length) override;
|
||||
|
||||
// Extension added to V8 log file name to get the low-level log name.
|
||||
static const char kFilenameFormatString[];
|
||||
static const int kFilenameBufferPadding;
|
||||
|
||||
// File buffer size of the low-level log. We don't use the default to
|
||||
// minimize the associated overhead.
|
||||
static const int kLogBufferSize = 2 * MB;
|
||||
|
||||
void LogWriteBytes(const char* bytes, int size);
|
||||
void LogWriteHeader();
|
||||
void LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared);
|
||||
|
||||
static const uint32_t kElfMachIA32 = 3;
|
||||
static const uint32_t kElfMachX64 = 62;
|
||||
static const uint32_t kElfMachARM = 40;
|
||||
static const uint32_t kElfMachMIPS = 10;
|
||||
|
||||
uint32_t GetElfMach() {
|
||||
#if V8_TARGET_ARCH_IA32
|
||||
return kElfMachIA32;
|
||||
#elif V8_TARGET_ARCH_X64
|
||||
return kElfMachX64;
|
||||
#elif V8_TARGET_ARCH_ARM
|
||||
return kElfMachARM;
|
||||
#elif V8_TARGET_ARCH_MIPS
|
||||
return kElfMachMIPS;
|
||||
#else
|
||||
UNIMPLEMENTED();
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
FILE* perf_output_handle_;
|
||||
uint64_t code_index_;
|
||||
void* marker_address_;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
// PerfJitLogger is only implemented on Linux
|
||||
class PerfJitLogger : public CodeEventLogger {
|
||||
public:
|
||||
void CodeMoveEvent(AbstractCode* from, Address to) override {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void CodeDisableOptEvent(AbstractCode* code,
|
||||
SharedFunctionInfo* shared) override {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void LogRecordedBuffer(AbstractCode* code, SharedFunctionInfo* shared,
|
||||
const char* name, int length) override {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // V8_OS_LINUX
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
#endif
|
@ -1026,6 +1026,8 @@
|
||||
'../../src/parsing/token.h',
|
||||
'../../src/pending-compilation-error-handler.cc',
|
||||
'../../src/pending-compilation-error-handler.h',
|
||||
'../../src/perf-jit.cc',
|
||||
'../../src/perf-jit.h',
|
||||
'../../src/profiler/allocation-tracker.cc',
|
||||
'../../src/profiler/allocation-tracker.h',
|
||||
'../../src/profiler/circular-queue-inl.h',
|
||||
|
Loading…
Reference in New Issue
Block a user