| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/profiling/perf/regs_parsing.h" |
| |
| #include <linux/perf_event.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| |
| #include <cinttypes> |
| #include <memory> |
| |
| #include <unwindstack/Elf.h> |
| #include <unwindstack/MachineArm.h> |
| #include <unwindstack/MachineArm64.h> |
| #include <unwindstack/MachineRiscv64.h> |
| #include <unwindstack/Regs.h> |
| #include <unwindstack/RegsArm.h> |
| #include <unwindstack/RegsArm64.h> |
| #include <unwindstack/RegsRiscv64.h> |
| #include <unwindstack/RegsX86.h> |
| #include <unwindstack/RegsX86_64.h> |
| #include <unwindstack/UserArm.h> |
| #include <unwindstack/UserArm64.h> |
| #include <unwindstack/UserRiscv64.h> |
| #include <unwindstack/UserX86.h> |
| #include <unwindstack/UserX86_64.h> |
| |
| // kernel uapi headers |
| #include <uapi/asm-arm/asm/perf_regs.h> |
| #undef PERF_REG_EXTENDED_MASK |
| #include <uapi/asm-x86/asm/perf_regs.h> |
| #undef PERF_REG_EXTENDED_MASK |
| #define perf_event_arm_regs perf_event_arm64_regs |
| #include <uapi/asm-arm64/asm/perf_regs.h> |
| #undef PERF_REG_EXTENDED_MASK |
| #undef perf_event_arm_regs |
| #include <uapi/asm-riscv/asm/perf_regs.h> |
| #undef PERF_REG_EXTENDED_MASK |
| |
| namespace perfetto { |
| namespace profiling { |
| |
| namespace { |
| |
| constexpr size_t constexpr_max(size_t x, size_t y) { |
| return x > y ? x : y; |
| } |
| |
| template <typename T> |
| const char* ReadValue(T* value_out, const char* ptr) { |
| memcpy(value_out, reinterpret_cast<const void*>(ptr), sizeof(T)); |
| return ptr + sizeof(T); |
| } |
| |
| // Supported configurations: |
| // * 32 bit daemon, 32 bit userspace |
| // * 64 bit daemon, mixed bitness userspace |
| // Therefore give the kernel the mask corresponding to our build architecture. |
| // Register parsing handles the mixed userspace ABI cases. |
| // For simplicity, we ask for as many registers as we can, even if not all of |
| // them will be used during unwinding. |
| // TODO(rsavitski): cleanly detect 32 bit traced_perf builds being side-loaded |
| // onto a system with 64 bit userspace processes. |
| uint64_t PerfUserRegsMask(unwindstack::ArchEnum arch) { |
| switch (static_cast<uint8_t>(arch)) { // cast to please -Wswitch-enum |
| case unwindstack::ARCH_ARM64: |
| return (1ULL << PERF_REG_ARM64_MAX) - 1; |
| case unwindstack::ARCH_ARM: |
| return (1ULL << PERF_REG_ARM_MAX) - 1; |
| // perf on x86_64 doesn't allow sampling ds/es/fs/gs registers. See |
| // arch/x86/kernel/perf_regs.c in the kernel. |
| case unwindstack::ARCH_X86_64: |
| return (((1ULL << PERF_REG_X86_64_MAX) - 1) & ~(1ULL << PERF_REG_X86_DS) & |
| ~(1ULL << PERF_REG_X86_ES) & ~(1ULL << PERF_REG_X86_FS) & |
| ~(1ULL << PERF_REG_X86_GS)); |
| // Note: excluding these segment registers might not be necessary on x86, |
| // but they won't be used anyway (so follow x64). |
| case unwindstack::ARCH_X86: |
| return ((1ULL << PERF_REG_X86_32_MAX) - 1) & ~(1ULL << PERF_REG_X86_DS) & |
| ~(1ULL << PERF_REG_X86_ES) & ~(1ULL << PERF_REG_X86_FS) & |
| ~(1ULL << PERF_REG_X86_GS); |
| case unwindstack::ARCH_RISCV64: |
| return (1ULL << PERF_REG_RISCV_MAX) - 1; |
| default: |
| PERFETTO_FATAL("Unsupported architecture"); |
| } |
| } |
| |
| // Adjusts the given architecture enum based on the ABI (as recorded in the perf |
| // sample). Note: we do not support 64 bit samples on a 32 bit daemon build, so |
| // this only converts from 64 bit to 32 bit architectures. |
| // TODO(rsavitski): on riscv64, are 32 bit userspace processes posible? |
| unwindstack::ArchEnum ArchForAbi(unwindstack::ArchEnum arch, uint64_t abi) { |
| if (arch == unwindstack::ARCH_ARM64 && abi == PERF_SAMPLE_REGS_ABI_32) { |
| return unwindstack::ARCH_ARM; |
| } |
| if (arch == unwindstack::ARCH_X86_64 && abi == PERF_SAMPLE_REGS_ABI_32) { |
| return unwindstack::ARCH_X86; |
| } |
| return arch; |
| } |
| |
| // Register values as an array, indexed using the kernel uapi perf_events.h enum |
| // values. Unsampled values will be left as zeroes. |
| struct RawRegisterData { |
| static constexpr uint64_t kMaxSize = |
| constexpr_max(constexpr_max(PERF_REG_ARM_MAX, PERF_REG_ARM64_MAX), |
| constexpr_max(PERF_REG_X86_64_MAX, PERF_REG_RISCV_MAX)); |
| uint64_t regs[kMaxSize] = {}; |
| }; |
| |
| // First converts the |RawRegisterData| array to libunwindstack's "user" |
| // register structs (which match the ptrace/coredump format, also available at |
| // <sys/user.h>), then constructs the relevant unwindstack::Regs subclass out |
| // of the latter. |
| std::unique_ptr<unwindstack::Regs> ToLibUnwindstackRegs( |
| const RawRegisterData& raw_regs, |
| unwindstack::ArchEnum arch) { |
| if (arch == unwindstack::ARCH_ARM64) { |
| static_assert(static_cast<int>(unwindstack::ARM64_REG_R0) == |
| static_cast<int>(PERF_REG_ARM64_X0) && |
| static_cast<int>(unwindstack::ARM64_REG_R0) == 0, |
| "register layout mismatch"); |
| static_assert(static_cast<int>(unwindstack::ARM64_REG_R30) == |
| static_cast<int>(PERF_REG_ARM64_LR), |
| "register layout mismatch"); |
| // Both the perf_event register order and the "user" format are derived from |
| // "struct pt_regs", so we can directly memcpy the first 31 regs (up to and |
| // including LR). |
| unwindstack::arm64_user_regs arm64_user_regs = {}; |
| memcpy(&arm64_user_regs.regs[0], &raw_regs.regs[0], |
| sizeof(uint64_t) * (PERF_REG_ARM64_LR + 1)); |
| arm64_user_regs.sp = raw_regs.regs[PERF_REG_ARM64_SP]; |
| arm64_user_regs.pc = raw_regs.regs[PERF_REG_ARM64_PC]; |
| return std::unique_ptr<unwindstack::Regs>( |
| unwindstack::RegsArm64::Read(&arm64_user_regs)); |
| } |
| |
| if (arch == unwindstack::ARCH_ARM) { |
| static_assert(static_cast<int>(unwindstack::ARM_REG_R0) == |
| static_cast<int>(PERF_REG_ARM_R0) && |
| static_cast<int>(unwindstack::ARM_REG_R0) == 0, |
| "register layout mismatch"); |
| static_assert(static_cast<int>(unwindstack::ARM_REG_LAST) == |
| static_cast<int>(PERF_REG_ARM_MAX), |
| "register layout mismatch"); |
| // As with arm64, the layouts match, but we need to downcast to u32. |
| unwindstack::arm_user_regs arm_user_regs = {}; |
| for (size_t i = 0; i < unwindstack::ARM_REG_LAST; i++) { |
| arm_user_regs.regs[i] = static_cast<uint32_t>(raw_regs.regs[i]); |
| } |
| return std::unique_ptr<unwindstack::Regs>( |
| unwindstack::RegsArm::Read(&arm_user_regs)); |
| } |
| |
| if (arch == unwindstack::ARCH_X86_64) { |
| // We've sampled more registers than what libunwindstack will use. Don't |
| // copy over cs/ss/flags. |
| unwindstack::x86_64_user_regs x86_64_user_regs = {}; |
| x86_64_user_regs.rax = raw_regs.regs[PERF_REG_X86_AX]; |
| x86_64_user_regs.rbx = raw_regs.regs[PERF_REG_X86_BX]; |
| x86_64_user_regs.rcx = raw_regs.regs[PERF_REG_X86_CX]; |
| x86_64_user_regs.rdx = raw_regs.regs[PERF_REG_X86_DX]; |
| x86_64_user_regs.r8 = raw_regs.regs[PERF_REG_X86_R8]; |
| x86_64_user_regs.r9 = raw_regs.regs[PERF_REG_X86_R9]; |
| x86_64_user_regs.r10 = raw_regs.regs[PERF_REG_X86_R10]; |
| x86_64_user_regs.r11 = raw_regs.regs[PERF_REG_X86_R11]; |
| x86_64_user_regs.r12 = raw_regs.regs[PERF_REG_X86_R12]; |
| x86_64_user_regs.r13 = raw_regs.regs[PERF_REG_X86_R13]; |
| x86_64_user_regs.r14 = raw_regs.regs[PERF_REG_X86_R14]; |
| x86_64_user_regs.r15 = raw_regs.regs[PERF_REG_X86_R15]; |
| x86_64_user_regs.rdi = raw_regs.regs[PERF_REG_X86_DI]; |
| x86_64_user_regs.rsi = raw_regs.regs[PERF_REG_X86_SI]; |
| x86_64_user_regs.rbp = raw_regs.regs[PERF_REG_X86_BP]; |
| x86_64_user_regs.rsp = raw_regs.regs[PERF_REG_X86_SP]; |
| x86_64_user_regs.rip = raw_regs.regs[PERF_REG_X86_IP]; |
| return std::unique_ptr<unwindstack::Regs>( |
| unwindstack::RegsX86_64::Read(&x86_64_user_regs)); |
| } |
| |
| if (arch == unwindstack::ARCH_X86) { |
| // We've sampled more registers than what libunwindstack will use. Don't |
| // copy over cs/ss/flags. |
| unwindstack::x86_user_regs x86_user_regs = {}; |
| x86_user_regs.eax = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_AX]); |
| x86_user_regs.ebx = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_BX]); |
| x86_user_regs.ecx = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_CX]); |
| x86_user_regs.edx = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_DX]); |
| x86_user_regs.ebp = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_BP]); |
| x86_user_regs.edi = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_DI]); |
| x86_user_regs.esi = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_SI]); |
| x86_user_regs.esp = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_SP]); |
| x86_user_regs.eip = static_cast<uint32_t>(raw_regs.regs[PERF_REG_X86_IP]); |
| return std::unique_ptr<unwindstack::Regs>( |
| unwindstack::RegsX86::Read(&x86_user_regs)); |
| } |
| |
| if (arch == unwindstack::ARCH_RISCV64) { |
| static_assert(static_cast<int>(unwindstack::RISCV64_REG_PC) == |
| static_cast<int>(PERF_REG_RISCV_PC) && |
| static_cast<int>(unwindstack::RISCV64_REG_PC) == 0, |
| "register layout mismatch"); |
| static_assert(static_cast<int>(unwindstack::RISCV64_REG_REAL_COUNT) == |
| static_cast<int>(PERF_REG_RISCV_MAX), |
| "register layout mismatch"); |
| // Register layout matches, pass the raw data to the Read call. |
| return std::unique_ptr<unwindstack::Regs>( |
| unwindstack::RegsRiscv64::Read(&raw_regs.regs[0])); |
| } |
| |
| PERFETTO_FATAL("Unsupported architecture"); |
| } |
| |
| } // namespace |
| |
| uint64_t PerfUserRegsMaskForArch(unwindstack::ArchEnum arch) { |
| return PerfUserRegsMask(arch); |
| } |
| |
| // Assumes that the sampling was configured with |
| // |PerfUserRegsMaskForArch(unwindstack::Regs::CurrentArch())|. |
| std::unique_ptr<unwindstack::Regs> ReadPerfUserRegsData(const char** data) { |
| unwindstack::ArchEnum requested_arch = unwindstack::Regs::CurrentArch(); |
| |
| // Layout, assuming a sparse bitmask requesting r1 and r15: |
| // userspace thread: [u64 abi] [u64 r1] [u64 r15] |
| // kernel thread: [u64 abi] |
| const char* parse_pos = *data; |
| uint64_t sampled_abi; |
| parse_pos = ReadValue(&sampled_abi, parse_pos); |
| |
| // ABI_NONE means there were no registers, as we've sampled a kernel thread, |
| // which doesn't have userspace registers. |
| if (sampled_abi == PERF_SAMPLE_REGS_ABI_NONE) { |
| *data = parse_pos; // adjust caller's parsing position |
| return nullptr; |
| } |
| |
| // Unpack the densely-packed register values into |RawRegisterData|, which has |
| // a value for every register (unsampled registers will be left at zero). |
| RawRegisterData raw_regs{}; |
| uint64_t regs_mask = PerfUserRegsMaskForArch(requested_arch); |
| for (size_t i = 0; regs_mask && (i < RawRegisterData::kMaxSize); i++) { |
| if (regs_mask & (1ULL << i)) { |
| parse_pos = ReadValue(&raw_regs.regs[i], parse_pos); |
| } |
| } |
| |
| // Special case: we've requested arm64 registers from a 64 bit kernel, but |
| // ended up sampling a 32 bit arm userspace process. The 32 bit execution |
| // state of the target process was saved by the exception entry in an |
| // ISA-specific way. The userspace R0-R14 end up saved as arm64 W0-W14, but |
| // the program counter (R15 on arm32) is still in PERF_REG_ARM64_PC (the 33rd |
| // register). So we can take the kernel-dumped 64 bit register state, reassign |
| // the PC into the R15 slot, and treat the resulting RawRegisterData as an |
| // arm32 register bank. See "Fundamentals of ARMv8-A" (ARM DOC |
| // 100878_0100_en), page 28. |
| // x86-64 doesn't need any such fixups. |
| if (requested_arch == unwindstack::ARCH_ARM64 && |
| sampled_abi == PERF_SAMPLE_REGS_ABI_32) { |
| raw_regs.regs[PERF_REG_ARM_PC] = raw_regs.regs[PERF_REG_ARM64_PC]; |
| } |
| |
| *data = parse_pos; // adjust caller's parsing position |
| |
| unwindstack::ArchEnum sampled_arch = ArchForAbi(requested_arch, sampled_abi); |
| return ToLibUnwindstackRegs(raw_regs, sampled_arch); |
| } |
| |
| } // namespace profiling |
| } // namespace perfetto |