duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

perf_scope.cpp (4981B)


      1 
      2 // SPDX-FileCopyrightText: 2023 Connor McLaughlin <stenzek@gmail.com>, PCSX2 Team
      3 // SPDX-License-Identifier: GPL-3.0
      4 
      5 #include "perf_scope.h"
      6 #include "assert.h"
      7 #include "string_util.h"
      8 
      9 #include <array>
     10 #include <cstring>
     11 
     12 #ifdef __linux__
     13 #include <atomic>
     14 #include <ctime>
     15 #include <elf.h>
     16 #include <mutex>
     17 #include <sys/mman.h>
     18 #include <sys/syscall.h>
     19 #include <unistd.h>
     20 #endif
     21 
     22 // #define ProfileWithPerf
     23 // #define ProfileWithPerfJitDump
     24 
     25 // Perf is only supported on linux
     26 #if defined(__linux__) && defined(ProfileWithPerf)
     27 
     28 static std::FILE* s_map_file = nullptr;
     29 static bool s_map_file_opened = false;
     30 static std::mutex s_mutex;
     31 static void RegisterMethod(const void* ptr, size_t size, const char* symbol)
     32 {
     33   std::unique_lock lock(s_mutex);
     34 
     35   if (!s_map_file)
     36   {
     37     if (s_map_file_opened)
     38       return;
     39 
     40     char file[256];
     41     snprintf(file, std::size(file), "/tmp/perf-%d.map", getpid());
     42     s_map_file = std::fopen(file, "wb");
     43     s_map_file_opened = true;
     44     if (!s_map_file)
     45       return;
     46   }
     47 
     48   std::fprintf(s_map_file, "%" PRIx64 " %zx %s\n", static_cast<u64>(reinterpret_cast<uintptr_t>(ptr)), size, symbol);
     49   std::fflush(s_map_file);
     50 }
     51 
     52 #elif defined(__linux__) && defined(ProfileWithPerfJitDump)
     53 enum : u32
     54 {
     55   JIT_CODE_LOAD = 0,
     56   JIT_CODE_MOVE = 1,
     57   JIT_CODE_DEBUG_INFO = 2,
     58   JIT_CODE_CLOSE = 3,
     59   JIT_CODE_UNWINDING_INFO = 4
     60 };
     61 
     62 #pragma pack(push, 1)
     63 struct JITDUMP_HEADER
     64 {
     65   u32 magic = 0x4A695444; // JiTD
     66   u32 version = 1;
     67   u32 header_size = sizeof(JITDUMP_HEADER);
     68   u32 elf_mach;
     69   u32 pad1 = 0;
     70   u32 pid;
     71   u64 timestamp;
     72   u64 flags = 0;
     73 };
     74 struct JITDUMP_RECORD_HEADER
     75 {
     76   u32 id;
     77   u32 total_size;
     78   u64 timestamp;
     79 };
     80 struct JITDUMP_CODE_LOAD
     81 {
     82   JITDUMP_RECORD_HEADER header;
     83   u32 pid;
     84   u32 tid;
     85   u64 vma;
     86   u64 code_addr;
     87   u64 code_size;
     88   u64 code_index;
     89   // name
     90 };
     91 #pragma pack(pop)
     92 
     93 static u64 JitDumpTimestamp()
     94 {
     95   struct timespec ts = {};
     96   clock_gettime(CLOCK_MONOTONIC, &ts);
     97   return (static_cast<u64>(ts.tv_sec) * 1000000000ULL) + static_cast<u64>(ts.tv_nsec);
     98 }
     99 
    100 static FILE* s_jitdump_file = nullptr;
    101 static bool s_jitdump_file_opened = false;
    102 static std::mutex s_jitdump_mutex;
    103 static u32 s_jitdump_record_id;
    104 
    105 static void RegisterMethod(const void* ptr, size_t size, const char* symbol)
    106 {
    107   const u32 namelen = std::strlen(symbol) + 1;
    108 
    109   std::unique_lock lock(s_jitdump_mutex);
    110   if (!s_jitdump_file)
    111   {
    112     if (!s_jitdump_file_opened)
    113     {
    114       char file[256];
    115       snprintf(file, std::size(file), "jit-%d.dump", getpid());
    116       s_jitdump_file = fopen(file, "w+b");
    117       s_jitdump_file_opened = true;
    118       if (!s_jitdump_file)
    119         return;
    120     }
    121 
    122     void* perf_marker = mmap(nullptr, 4096, PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(s_jitdump_file), 0);
    123     AssertMsg(perf_marker != MAP_FAILED, "Map perf marker");
    124 
    125     JITDUMP_HEADER jh = {};
    126 #if defined(__aarch64__)
    127     jh.elf_mach = EM_AARCH64;
    128 #else
    129     jh.elf_mach = EM_X86_64;
    130 #endif
    131     jh.pid = getpid();
    132     jh.timestamp = JitDumpTimestamp();
    133     std::fwrite(&jh, sizeof(jh), 1, s_jitdump_file);
    134   }
    135 
    136   JITDUMP_CODE_LOAD cl = {};
    137   cl.header.id = JIT_CODE_LOAD;
    138   cl.header.total_size = sizeof(cl) + namelen + static_cast<u32>(size);
    139   cl.header.timestamp = JitDumpTimestamp();
    140   cl.pid = getpid();
    141   cl.tid = syscall(SYS_gettid);
    142   cl.vma = 0;
    143   cl.code_addr = static_cast<u64>(reinterpret_cast<uintptr_t>(ptr));
    144   cl.code_size = static_cast<u64>(size);
    145   cl.code_index = s_jitdump_record_id++;
    146   std::fwrite(&cl, sizeof(cl), 1, s_jitdump_file);
    147   std::fwrite(symbol, namelen, 1, s_jitdump_file);
    148   std::fwrite(ptr, size, 1, s_jitdump_file);
    149   std::fflush(s_jitdump_file);
    150 }
    151 
    152 #endif
    153 
    154 #if defined(__linux__) && (defined(ProfileWithPerf) || defined(ProfileWithPerfJitDump))
    155 
    156 void PerfScope::Register(const void* ptr, size_t size, const char* symbol)
    157 {
    158   char full_symbol[128];
    159   if (HasPrefix())
    160     std::snprintf(full_symbol, std::size(full_symbol), "%s_%s", m_prefix, symbol);
    161   else
    162     StringUtil::Strlcpy(full_symbol, symbol, std::size(full_symbol));
    163   RegisterMethod(ptr, size, full_symbol);
    164 }
    165 
    166 void PerfScope::RegisterPC(const void* ptr, size_t size, u32 pc)
    167 {
    168   char full_symbol[128];
    169   if (HasPrefix())
    170     std::snprintf(full_symbol, std::size(full_symbol), "%s_%08X", m_prefix, pc);
    171   else
    172     std::snprintf(full_symbol, std::size(full_symbol), "%08X", pc);
    173   RegisterMethod(ptr, size, full_symbol);
    174 }
    175 
    176 void PerfScope::RegisterKey(const void* ptr, size_t size, const char* prefix, u64 key)
    177 {
    178   char full_symbol[128];
    179   if (HasPrefix())
    180     std::snprintf(full_symbol, std::size(full_symbol), "%s_%s%016" PRIX64, m_prefix, prefix, key);
    181   else
    182     std::snprintf(full_symbol, std::size(full_symbol), "%s%016" PRIX64, prefix, key);
    183   RegisterMethod(ptr, size, full_symbol);
    184 }
    185 
    186 #else
    187 
    188 void PerfScope::Register(const void* ptr, size_t size, const char* symbol)
    189 {
    190 }
    191 void PerfScope::RegisterPC(const void* ptr, size_t size, u32 pc)
    192 {
    193 }
    194 void PerfScope::RegisterKey(const void* ptr, size_t size, const char* prefix, u64 key)
    195 {
    196 }
    197 
    198 #endif