/* * Copyright (C) 2018 Yusuke Suzuki . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "config.h" #include "PerfLog.h" #if ENABLE(ASSEMBLER) && OS(LINUX) #include #include #include #include #include #include #include #include #include #include #include #include #include namespace JSC { namespace PerfLogInternal { static constexpr bool verbose = false; } // namespace PerfLogInternal namespace JITDump { namespace Constants { // Perf jit-dump formats are specified here. // https://raw.githubusercontent.com/torvalds/linux/master/tools/perf/Documentation/jitdump-specification.txt // The latest version 2, but it is too new at that time. static constexpr uint32_t version = 1; #if CPU(LITTLE_ENDIAN) static constexpr uint32_t magic = 0x4a695444; #else static constexpr uint32_t magic = 0x4454694a; #endif #if CPU(X86) static constexpr uint32_t elfMachine = EM_386; #elif CPU(X86_64) static constexpr uint32_t elfMachine = EM_X86_64; #elif CPU(ARM64) static constexpr uint32_t elfMachine = EM_AARCH64; #elif CPU(ARM) static constexpr uint32_t elfMachine = EM_ARM; #elif CPU(MIPS) #if CPU(LITTLE_ENDIAN) static constexpr uint32_t elfMachine = EM_MIPS_RS3_LE; #else static constexpr uint32_t elfMachine = EM_MIPS; #endif #endif } // namespace Constants struct FileHeader { uint32_t magic { Constants::magic }; uint32_t version { Constants::version }; uint32_t totalSize { sizeof(FileHeader) }; uint32_t elfMachine { Constants::elfMachine }; uint32_t padding1 { 0 }; uint32_t pid { 0 }; uint64_t timestamp { 0 }; uint64_t flags { 0 }; }; enum class RecordType : uint32_t { JITCodeLoad = 0, JITCodeMove = 1, JITCodeDebugInfo = 2, JITCodeClose = 3, JITCodeUnwindingInfo = 4, }; struct RecordHeader { RecordType type { RecordType::JITCodeLoad }; uint32_t totalSize { 0 }; uint64_t timestamp { 0 }; }; struct CodeLoadRecord { RecordHeader header { RecordType::JITCodeLoad, 0, 0, }; uint32_t pid { 0 }; uint32_t tid { 0 }; uint64_t vma { 0 }; uint64_t codeAddress { 0 }; uint64_t codeSize { 0 }; uint64_t codeIndex { 0 }; }; } // namespace JITDump PerfLog& PerfLog::singleton() { static PerfLog* logger; static std::once_flag onceKey; std::call_once(onceKey, [] { logger = new PerfLog; }); return *logger; } static inline uint64_t generateTimestamp() { return MonotonicTime::now().secondsSinceEpoch().nanosecondsAs(); } static inline pid_t getCurrentThreadID() { return static_cast(syscall(__NR_gettid)); } PerfLog::PerfLog() { { std::array filename; snprintf(filename.data(), filename.size() - 1, "jit-%d.dump", getCurrentProcessID()); filename[filename.size() - 1] = '\0'; m_fd = open(filename.data(), O_CREAT | O_TRUNC | O_RDWR, 0666); RELEASE_ASSERT(m_fd != -1); // Linux perf command records this mmap operation in perf.data as a metadata to the JIT perf annotations. // We do not use this mmap-ed memory region actually. m_marker = mmap(nullptr, pageSize(), PROT_READ | PROT_EXEC, MAP_PRIVATE, m_fd, 0); RELEASE_ASSERT(m_marker != MAP_FAILED); m_file = fdopen(m_fd, "wb"); RELEASE_ASSERT(m_file); } JITDump::FileHeader header; header.timestamp = generateTimestamp(); header.pid = getCurrentProcessID(); auto locker = holdLock(m_lock); write(locker, &header, sizeof(JITDump::FileHeader)); flush(locker); } void PerfLog::write(const AbstractLocker&, const void* data, size_t size) { size_t result = fwrite(data, 1, size, m_file); RELEASE_ASSERT(result == size); } void PerfLog::flush(const AbstractLocker&) { fflush(m_file); } void PerfLog::log(CString&& name, const uint8_t* executableAddress, size_t size) { if (!size) { dataLogLnIf(PerfLogInternal::verbose, "0 size record ", name, " ", RawPointer(executableAddress)); return; } PerfLog& logger = singleton(); auto locker = holdLock(logger.m_lock); JITDump::CodeLoadRecord record; record.header.timestamp = generateTimestamp(); record.header.totalSize = sizeof(JITDump::CodeLoadRecord) + (name.length() + 1) + size; record.pid = getCurrentProcessID(); record.tid = getCurrentThreadID(); record.vma = bitwise_cast(executableAddress); record.codeAddress = bitwise_cast(executableAddress); record.codeSize = size; record.codeIndex = logger.m_codeIndex++; logger.write(locker, &record, sizeof(JITDump::CodeLoadRecord)); logger.write(locker, name.data(), name.length() + 1); logger.write(locker, executableAddress, size); logger.flush(locker); dataLogLnIf(PerfLogInternal::verbose, name, " [", record.codeIndex, "] ", RawPointer(executableAddress), "-", RawPointer(executableAddress + size), " ", size); } } // namespace JSC #endif // ENABLE(ASSEMBLER) && OS(LINUX)