blob: 09d792225bd3e0e9729b43d87f5d446eb0f4f8a2 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/unwinder/benchmark/libunwindstack.h"
#include "lib/syslog/cpp/macros.h"
#include "snapshot/thread_snapshot.h"
#include "src/developer/debug/third_party/libunwindstack/include/unwindstack/UcontextArm64.h"
#include "src/developer/debug/third_party/libunwindstack/include/unwindstack/UcontextX86_64.h"
#include "src/developer/debug/third_party/libunwindstack/include/unwindstack/Unwinder.h"
namespace benchmark {
namespace {
std::unique_ptr<unwindstack::Regs> GetUnwindRegsARM64(const crashpad::CPUContextARM64& ctx,
size_t stack_size) {
unwindstack::arm64_ucontext_t ucontext;
ucontext.uc_stack.ss_sp = ctx.sp;
ucontext.uc_stack.ss_size = stack_size;
ucontext.uc_mcontext.pstate = ctx.spsr;
for (size_t i = 0; i <= 31; i++) {
ucontext.uc_mcontext.regs[i] = ctx.regs[i];
}
ucontext.uc_mcontext.regs[unwindstack::Arm64Reg::ARM64_REG_PC] = ctx.pc;
return std::unique_ptr<unwindstack::Regs>(
unwindstack::Regs::CreateFromUcontext(unwindstack::ArchEnum::ARCH_ARM64, &ucontext));
}
std::unique_ptr<unwindstack::Regs> GetUnwindRegsX86_64(const crashpad::CPUContextX86_64& ctx,
size_t stack_size) {
unwindstack::x86_64_ucontext_t ucontext;
ucontext.uc_stack.ss_sp = ctx.rsp;
ucontext.uc_stack.ss_size = stack_size;
ucontext.uc_mcontext.rax = ctx.rax;
ucontext.uc_mcontext.rbx = ctx.rbx;
ucontext.uc_mcontext.rcx = ctx.rcx;
ucontext.uc_mcontext.rdx = ctx.rdx;
ucontext.uc_mcontext.rsi = ctx.rsi;
ucontext.uc_mcontext.rdi = ctx.rdi;
ucontext.uc_mcontext.rbp = ctx.rbp;
ucontext.uc_mcontext.rsp = ctx.rsp;
ucontext.uc_mcontext.r8 = ctx.r8;
ucontext.uc_mcontext.r9 = ctx.r9;
ucontext.uc_mcontext.r10 = ctx.r10;
ucontext.uc_mcontext.r11 = ctx.r11;
ucontext.uc_mcontext.r12 = ctx.r12;
ucontext.uc_mcontext.r13 = ctx.r13;
ucontext.uc_mcontext.r14 = ctx.r14;
ucontext.uc_mcontext.r15 = ctx.r15;
ucontext.uc_mcontext.rip = ctx.rip;
return std::unique_ptr<unwindstack::Regs>(
unwindstack::Regs::CreateFromUcontext(unwindstack::ArchEnum::ARCH_X86_64, &ucontext));
}
unwinder::RegisterID RegisterNameToId(const char* name) {
if (!strcmp(name, "rax"))
return unwinder::RegisterID::kX64_rax;
if (!strcmp(name, "rbx"))
return unwinder::RegisterID::kX64_rbx;
if (!strcmp(name, "rcx"))
return unwinder::RegisterID::kX64_rcx;
if (!strcmp(name, "rdx"))
return unwinder::RegisterID::kX64_rdx;
if (!strcmp(name, "r8"))
return unwinder::RegisterID::kX64_r8;
if (!strcmp(name, "r9"))
return unwinder::RegisterID::kX64_r9;
if (!strcmp(name, "r10"))
return unwinder::RegisterID::kX64_r10;
if (!strcmp(name, "r11"))
return unwinder::RegisterID::kX64_r11;
if (!strcmp(name, "r12"))
return unwinder::RegisterID::kX64_r12;
if (!strcmp(name, "r13"))
return unwinder::RegisterID::kX64_r13;
if (!strcmp(name, "r14"))
return unwinder::RegisterID::kX64_r14;
if (!strcmp(name, "r15"))
return unwinder::RegisterID::kX64_r15;
if (!strcmp(name, "rdi"))
return unwinder::RegisterID::kX64_rdi;
if (!strcmp(name, "rsi"))
return unwinder::RegisterID::kX64_rsi;
if (!strcmp(name, "rbp"))
return unwinder::RegisterID::kX64_rbp;
if (!strcmp(name, "rsp"))
return unwinder::RegisterID::kX64_rsp;
if (!strcmp(name, "rip"))
return unwinder::RegisterID::kX64_rip;
if (!strcmp(name, "x0"))
return unwinder::RegisterID::kArm64_x0;
if (!strcmp(name, "x1"))
return unwinder::RegisterID::kArm64_x1;
if (!strcmp(name, "x2"))
return unwinder::RegisterID::kArm64_x2;
if (!strcmp(name, "x3"))
return unwinder::RegisterID::kArm64_x3;
if (!strcmp(name, "x4"))
return unwinder::RegisterID::kArm64_x4;
if (!strcmp(name, "x5"))
return unwinder::RegisterID::kArm64_x5;
if (!strcmp(name, "x6"))
return unwinder::RegisterID::kArm64_x6;
if (!strcmp(name, "x7"))
return unwinder::RegisterID::kArm64_x7;
if (!strcmp(name, "x8"))
return unwinder::RegisterID::kArm64_x8;
if (!strcmp(name, "x9"))
return unwinder::RegisterID::kArm64_x9;
if (!strcmp(name, "x10"))
return unwinder::RegisterID::kArm64_x10;
if (!strcmp(name, "x11"))
return unwinder::RegisterID::kArm64_x11;
if (!strcmp(name, "x12"))
return unwinder::RegisterID::kArm64_x12;
if (!strcmp(name, "x13"))
return unwinder::RegisterID::kArm64_x13;
if (!strcmp(name, "x14"))
return unwinder::RegisterID::kArm64_x14;
if (!strcmp(name, "x15"))
return unwinder::RegisterID::kArm64_x15;
if (!strcmp(name, "x16"))
return unwinder::RegisterID::kArm64_x16;
if (!strcmp(name, "x17"))
return unwinder::RegisterID::kArm64_x17;
if (!strcmp(name, "x18"))
return unwinder::RegisterID::kArm64_x18;
if (!strcmp(name, "x19"))
return unwinder::RegisterID::kArm64_x19;
if (!strcmp(name, "x20"))
return unwinder::RegisterID::kArm64_x20;
if (!strcmp(name, "x21"))
return unwinder::RegisterID::kArm64_x21;
if (!strcmp(name, "x22"))
return unwinder::RegisterID::kArm64_x22;
if (!strcmp(name, "x23"))
return unwinder::RegisterID::kArm64_x23;
if (!strcmp(name, "x24"))
return unwinder::RegisterID::kArm64_x24;
if (!strcmp(name, "x25"))
return unwinder::RegisterID::kArm64_x25;
if (!strcmp(name, "x26"))
return unwinder::RegisterID::kArm64_x26;
if (!strcmp(name, "x27"))
return unwinder::RegisterID::kArm64_x27;
if (!strcmp(name, "x28"))
return unwinder::RegisterID::kArm64_x28;
if (!strcmp(name, "x29"))
return unwinder::RegisterID::kArm64_x29;
if (!strcmp(name, "sp"))
return unwinder::RegisterID::kArm64_sp;
if (!strcmp(name, "lr"))
return unwinder::RegisterID::kArm64_lr;
if (!strcmp(name, "pc"))
return unwinder::RegisterID::kArm64_pc;
return unwinder::RegisterID::kInvalid;
}
} // namespace
std::vector<unwinder::Frame> UnwindFromLibunwindstack(
const std::shared_ptr<MinidumpMemory>& memory,
std::vector<const crashpad::ModuleSnapshot*> modules, const crashpad::ThreadSnapshot* thread) {
size_t stack_size = 0;
if (auto stack = thread->Stack()) {
stack_size = stack->Size();
}
const auto& context = *thread->Context();
std::unique_ptr<unwindstack::Regs> regs;
unwinder::Registers::Arch arch;
switch (context.architecture) {
case crashpad::CPUArchitecture::kCPUArchitectureARM64:
arch = unwinder::Registers::Arch::kArm64;
regs = GetUnwindRegsARM64(*context.arm64, stack_size);
break;
case crashpad::CPUArchitecture::kCPUArchitectureX86_64:
arch = unwinder::Registers::Arch::kX64;
regs = GetUnwindRegsX86_64(*context.x86_64, stack_size);
break;
default:
FX_NOTREACHED();
}
std::sort(modules.begin(), modules.end(),
[](auto a, auto b) { return a->Address() < b->Address(); });
unwindstack::Maps maps;
for (const auto& mod : modules) {
maps.Add(mod->Address(), mod->Address() + mod->Size(), 0, 0, mod->Name(), 0);
}
unwindstack::Unwinder unwinder(40, &maps, regs.get(), memory, true);
unwinder.SetResolveNames(false);
unwinder.Unwind();
std::vector<unwinder::Frame> res;
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
const auto& src = unwinder.frames()[i];
unwinder::Registers dest(arch);
if (src.regs) {
src.regs->IterateRegisters([&dest](const char* name, uint64_t val) {
if (auto id = RegisterNameToId(name); id != unwinder::RegisterID::kInvalid) {
dest.Set(id, val);
}
});
}
// unwindstack will adjust the pc for all frames except the bottom-most one. The logic lives in
// RegsFuchsia::GetPcAdjustment and is required in order to get the correct cfa_offset. However,
// it's not ideal for us because we want return addresses rather than call sites for previous
// frames. So we restore the pc here.
if (i == 0) {
dest.SetPC(src.pc);
} else {
dest.SetPC(src.pc + regs->GetPcAdjustment(src.pc, nullptr));
}
dest.SetSP(src.sp);
res.emplace_back(std::move(dest), unwinder::Frame::Trust::kCFI, unwinder::Success());
}
return res;
}
} // namespace benchmark