| Index: src/log.cc
|
| diff --git a/src/log.cc b/src/log.cc
|
| index b353f548fb78b88d212b24b86391ccc08fb9c655..e123231292e288e33c94bd5b37980d94ca04b08b 100644
|
| --- a/src/log.cc
|
| +++ b/src/log.cc
|
| @@ -246,6 +246,231 @@ void CodeEventLogger::RegExpCodeCreateEvent(Code* code, String* source) {
|
| }
|
|
|
|
|
| +// Linux perf tool logging support
|
| +class PerfBasicLogger : public CodeEventLogger {
|
| + public:
|
| + PerfBasicLogger();
|
| + virtual ~PerfBasicLogger();
|
| +
|
| + virtual void CodeMoveEvent(Address from, Address to) { }
|
| + virtual void CodeDeleteEvent(Address from) { }
|
| +
|
| + private:
|
| + virtual void LogRecordedBuffer(Code* code,
|
| + SharedFunctionInfo* shared,
|
| + const char* name,
|
| + int length);
|
| +
|
| + // 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;
|
| +
|
| + FILE* perf_output_handle_;
|
| +};
|
| +
|
| +const char PerfBasicLogger::kFilenameFormatString[] = "/tmp/perf-%d.map";
|
| +// Extra space for the PID in the filename
|
| +const int PerfBasicLogger::kFilenameBufferPadding = 16;
|
| +
|
| +PerfBasicLogger::PerfBasicLogger()
|
| + : perf_output_handle_(NULL) {
|
| + // Open the perf JIT dump file.
|
| + int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding;
|
| + ScopedVector<char> perf_dump_name(bufferSize);
|
| + int size = OS::SNPrintF(
|
| + perf_dump_name,
|
| + kFilenameFormatString,
|
| + OS::GetCurrentProcessId());
|
| + CHECK_NE(size, -1);
|
| + perf_output_handle_ = OS::FOpen(perf_dump_name.start(), OS::LogFileOpenMode);
|
| + CHECK_NE(perf_output_handle_, NULL);
|
| + setvbuf(perf_output_handle_, NULL, _IOFBF, kLogBufferSize);
|
| +}
|
| +
|
| +
|
| +PerfBasicLogger::~PerfBasicLogger() {
|
| + fclose(perf_output_handle_);
|
| + perf_output_handle_ = NULL;
|
| +}
|
| +
|
| +
|
| +void PerfBasicLogger::LogRecordedBuffer(Code* code,
|
| + SharedFunctionInfo*,
|
| + const char* name,
|
| + int length) {
|
| + ASSERT(code->instruction_start() == code->address() + Code::kHeaderSize);
|
| +
|
| + OS::FPrint(perf_output_handle_, "%llx %x %.*s\n",
|
| + reinterpret_cast<uint64_t>(code->instruction_start()),
|
| + code->instruction_size(),
|
| + length, name);
|
| +}
|
| +
|
| +
|
| +// Linux perf tool logging support
|
| +class PerfJitLogger : public CodeEventLogger {
|
| + public:
|
| + PerfJitLogger();
|
| + virtual ~PerfJitLogger();
|
| +
|
| + virtual void CodeMoveEvent(Address from, Address to) { }
|
| + virtual void CodeDeleteEvent(Address from) { }
|
| +
|
| + private:
|
| + virtual void LogRecordedBuffer(Code* code,
|
| + SharedFunctionInfo* shared,
|
| + const char* name,
|
| + int length);
|
| +
|
| + // 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();
|
| +
|
| + static const uint32_t kJitHeaderMagic = 0x4F74496A;
|
| + static const uint32_t kJitHeaderVersion = 0x2;
|
| + static const uint32_t kElfMachIA32 = 3;
|
| + static const uint32_t kElfMachX64 = 62;
|
| + static const uint32_t kElfMachARM = 40;
|
| + static const uint32_t kElfMachMIPS = 10;
|
| +
|
| + struct jitheader {
|
| + uint32_t magic;
|
| + uint32_t version;
|
| + uint32_t total_size;
|
| + uint32_t elf_mach;
|
| + uint32_t pad1;
|
| + uint32_t pid;
|
| + uint64_t timestamp;
|
| + };
|
| +
|
| + enum jit_record_type {
|
| + JIT_CODE_LOAD = 0
|
| + // JIT_CODE_UNLOAD = 1,
|
| + // JIT_CODE_CLOSE = 2,
|
| + // JIT_CODE_DEBUG_INFO = 3,
|
| + // JIT_CODE_PAGE_MAP = 4,
|
| + // JIT_CODE_MAX = 5
|
| + };
|
| +
|
| + struct jr_code_load {
|
| + uint32_t id;
|
| + uint32_t total_size;
|
| + uint64_t timestamp;
|
| + uint64_t vma;
|
| + uint64_t code_addr;
|
| + uint32_t code_size;
|
| + uint32_t align;
|
| + };
|
| +
|
| + 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_;
|
| +};
|
| +
|
| +const char PerfJitLogger::kFilenameFormatString[] = "/tmp/jit-%d.dump";
|
| +
|
| +// Extra padding for the PID in the filename
|
| +const int PerfJitLogger::kFilenameBufferPadding = 16;
|
| +
|
| +PerfJitLogger::PerfJitLogger()
|
| + : perf_output_handle_(NULL) {
|
| + // Open the perf JIT dump file.
|
| + int bufferSize = sizeof(kFilenameFormatString) + kFilenameBufferPadding;
|
| + ScopedVector<char> perf_dump_name(bufferSize);
|
| + int size = OS::SNPrintF(
|
| + perf_dump_name,
|
| + kFilenameFormatString,
|
| + OS::GetCurrentProcessId());
|
| + CHECK_NE(size, -1);
|
| + perf_output_handle_ = OS::FOpen(perf_dump_name.start(), OS::LogFileOpenMode);
|
| + CHECK_NE(perf_output_handle_, NULL);
|
| + setvbuf(perf_output_handle_, NULL, _IOFBF, kLogBufferSize);
|
| +
|
| + LogWriteHeader();
|
| +}
|
| +
|
| +
|
| +PerfJitLogger::~PerfJitLogger() {
|
| + fclose(perf_output_handle_);
|
| + perf_output_handle_ = NULL;
|
| +}
|
| +
|
| +
|
| +void PerfJitLogger::LogRecordedBuffer(Code* code,
|
| + SharedFunctionInfo*,
|
| + const char* name,
|
| + int length) {
|
| + ASSERT(code->instruction_start() == code->address() + Code::kHeaderSize);
|
| + ASSERT(perf_output_handle_ != NULL);
|
| +
|
| + const char* code_name = name;
|
| + uint8_t* code_pointer = reinterpret_cast<uint8_t*>(code->instruction_start());
|
| + uint32_t code_size = code->instruction_size();
|
| +
|
| + static const char string_terminator[] = "\0";
|
| +
|
| + jr_code_load code_load;
|
| + code_load.id = JIT_CODE_LOAD;
|
| + code_load.total_size = sizeof(code_load) + length + 1 + code_size;
|
| + code_load.timestamp =
|
| + static_cast<uint64_t>(OS::TimeCurrentMillis() * 1000.0);
|
| + code_load.vma = 0x0; // Our addresses are absolute.
|
| + code_load.code_addr = reinterpret_cast<uint64_t>(code->instruction_start());
|
| + code_load.code_size = code_size;
|
| + code_load.align = 0;
|
| +
|
| + 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);
|
| +}
|
| +
|
| +
|
| +void PerfJitLogger::LogWriteBytes(const char* bytes, int size) {
|
| + size_t rv = fwrite(bytes, 1, size, perf_output_handle_);
|
| + ASSERT(static_cast<size_t>(size) == rv);
|
| + USE(rv);
|
| +}
|
| +
|
| +
|
| +void PerfJitLogger::LogWriteHeader() {
|
| + ASSERT(perf_output_handle_ != NULL);
|
| + jitheader header;
|
| + header.magic = kJitHeaderMagic;
|
| + header.version = kJitHeaderVersion;
|
| + header.total_size = sizeof(jitheader);
|
| + header.pad1 = 0xdeadbeef;
|
| + header.elf_mach = GetElfMach();
|
| + header.pid = OS::GetCurrentProcessId();
|
| + header.timestamp = static_cast<uint64_t>(OS::TimeCurrentMillis() * 1000.0);
|
| + LogWriteBytes(reinterpret_cast<const char*>(&header), sizeof(header));
|
| +}
|
| +
|
| +
|
| // Low-level logging support.
|
| #define LL_LOG(Call) if (ll_logger_) ll_logger_->Call;
|
|
|
| @@ -711,6 +936,8 @@ Logger::Logger(Isolate* isolate)
|
| 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),
|
| @@ -1844,6 +2071,17 @@ bool Logger::SetUp(Isolate* isolate) {
|
| PrepareLogFileName(isolate, FLAG_logfile);
|
| log_->Initialize(*log_file_name);
|
|
|
| +
|
| + if (FLAG_perf_basic_prof) {
|
| + perf_basic_logger_ = new PerfBasicLogger();
|
| + addCodeEventListener(perf_basic_logger_);
|
| + }
|
| +
|
| + if (FLAG_perf_jit_prof) {
|
| + perf_jit_logger_ = new PerfJitLogger();
|
| + addCodeEventListener(perf_jit_logger_);
|
| + }
|
| +
|
| if (FLAG_ll_prof) {
|
| ll_logger_ = new LowLevelLogger(*log_file_name);
|
| addCodeEventListener(ll_logger_);
|
| @@ -1906,6 +2144,18 @@ FILE* Logger::TearDown() {
|
| delete ticker_;
|
| ticker_ = NULL;
|
|
|
| + if (perf_basic_logger_) {
|
| + removeCodeEventListener(perf_basic_logger_);
|
| + delete perf_basic_logger_;
|
| + 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_;
|
|
|