Commit 82e95f59 authored by jarin's avatar jarin Committed by Commit bot

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}
parent 43216574
......@@ -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;
}
......
......@@ -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_;
......
This diff is collapsed.
// 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',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment