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