|  | // Copyright 2018 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/debug_agent/unwind.h" | 
|  |  | 
|  | #include <inttypes.h> | 
|  |  | 
|  | #include <algorithm> | 
|  |  | 
|  | #include <ngunwind/fuchsia.h> | 
|  | #include <ngunwind/libunwind.h> | 
|  |  | 
|  | #include "src/developer/debug/debug_agent/arch.h" | 
|  | #include "src/developer/debug/debug_agent/general_registers.h" | 
|  | #include "src/developer/debug/debug_agent/module_list.h" | 
|  | #include "src/developer/debug/debug_agent/process_handle.h" | 
|  | #include "src/developer/debug/debug_agent/thread_handle.h" | 
|  | #include "src/developer/debug/third_party/libunwindstack/fuchsia/MemoryFuchsia.h" | 
|  | #include "src/developer/debug/third_party/libunwindstack/fuchsia/RegsFuchsia.h" | 
|  | #include "src/developer/debug/third_party/libunwindstack/include/unwindstack/Unwinder.h" | 
|  | #include "src/lib/containers/cpp/array_view.h" | 
|  |  | 
|  | namespace debug_agent { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using debug_ipc::RegisterID; | 
|  |  | 
|  | using ModuleVector = std::vector<debug_ipc::Module>; | 
|  |  | 
|  | // Default unwinder type to use. | 
|  | UnwinderType unwinder_type = UnwinderType::kNgUnwind; | 
|  |  | 
|  | // These are the registers we attempt to extract from stack frams from NGUnwind This does not | 
|  | // include the IP/SP which are specially handled separately. | 
|  | struct NGUnwindRegisterMap { | 
|  | int ngunwind;   // NGUnwind's register #define. | 
|  | RegisterID id;  // Our RegisterID for the same thing. | 
|  | }; | 
|  | containers::array_view<NGUnwindRegisterMap> GetNGUnwindGeneralRegisters() { | 
|  | // clang-format off | 
|  | #if defined(__x86_64__) | 
|  | static NGUnwindRegisterMap kGeneral[] = { | 
|  | {UNW_X86_64_RAX, RegisterID::kX64_rax}, | 
|  | {UNW_X86_64_RBX, RegisterID::kX64_rbx}, | 
|  | {UNW_X86_64_RCX, RegisterID::kX64_rcx}, | 
|  | {UNW_X86_64_RDX, RegisterID::kX64_rdx}, | 
|  | {UNW_X86_64_RSI, RegisterID::kX64_rsi}, | 
|  | {UNW_X86_64_RDI, RegisterID::kX64_rdi}, | 
|  | {UNW_X86_64_RBP, RegisterID::kX64_rbp}, | 
|  | {UNW_X86_64_R8, RegisterID::kX64_r8}, | 
|  | {UNW_X86_64_R9, RegisterID::kX64_r9}, | 
|  | {UNW_X86_64_R10, RegisterID::kX64_r10}, | 
|  | {UNW_X86_64_R11, RegisterID::kX64_r11}, | 
|  | {UNW_X86_64_R12, RegisterID::kX64_r12}, | 
|  | {UNW_X86_64_R13, RegisterID::kX64_r13}, | 
|  | {UNW_X86_64_R14, RegisterID::kX64_r14}, | 
|  | {UNW_X86_64_R15, RegisterID::kX64_r15}}; | 
|  | #elif defined(__aarch64__) | 
|  | static NGUnwindRegisterMap kGeneral[] = { | 
|  | {UNW_AARCH64_X0, RegisterID::kARMv8_x0}, | 
|  | {UNW_AARCH64_X1, RegisterID::kARMv8_x1}, | 
|  | {UNW_AARCH64_X2, RegisterID::kARMv8_x2}, | 
|  | {UNW_AARCH64_X3, RegisterID::kARMv8_x3}, | 
|  | {UNW_AARCH64_X4, RegisterID::kARMv8_x4}, | 
|  | {UNW_AARCH64_X5, RegisterID::kARMv8_x5}, | 
|  | {UNW_AARCH64_X6, RegisterID::kARMv8_x6}, | 
|  | {UNW_AARCH64_X7, RegisterID::kARMv8_x7}, | 
|  | {UNW_AARCH64_X8, RegisterID::kARMv8_x8}, | 
|  | {UNW_AARCH64_X9, RegisterID::kARMv8_x9}, | 
|  | {UNW_AARCH64_X10, RegisterID::kARMv8_x10}, | 
|  | {UNW_AARCH64_X11, RegisterID::kARMv8_x11}, | 
|  | {UNW_AARCH64_X12, RegisterID::kARMv8_x12}, | 
|  | {UNW_AARCH64_X13, RegisterID::kARMv8_x13}, | 
|  | {UNW_AARCH64_X14, RegisterID::kARMv8_x14}, | 
|  | {UNW_AARCH64_X15, RegisterID::kARMv8_x15}, | 
|  | {UNW_AARCH64_X16, RegisterID::kARMv8_x16}, | 
|  | {UNW_AARCH64_X17, RegisterID::kARMv8_x17}, | 
|  | {UNW_AARCH64_X18, RegisterID::kARMv8_x18}, | 
|  | {UNW_AARCH64_X19, RegisterID::kARMv8_x19}, | 
|  | {UNW_AARCH64_X20, RegisterID::kARMv8_x20}, | 
|  | {UNW_AARCH64_X21, RegisterID::kARMv8_x21}, | 
|  | {UNW_AARCH64_X22, RegisterID::kARMv8_x22}, | 
|  | {UNW_AARCH64_X23, RegisterID::kARMv8_x23}, | 
|  | {UNW_AARCH64_X24, RegisterID::kARMv8_x24}, | 
|  | {UNW_AARCH64_X25, RegisterID::kARMv8_x25}, | 
|  | {UNW_AARCH64_X26, RegisterID::kARMv8_x26}, | 
|  | {UNW_AARCH64_X27, RegisterID::kARMv8_x27}, | 
|  | {UNW_AARCH64_X28, RegisterID::kARMv8_x28}, | 
|  | {UNW_AARCH64_X29, RegisterID::kARMv8_x29}, | 
|  | {UNW_AARCH64_X30, RegisterID::kARMv8_lr}}; | 
|  | #else | 
|  | #error Write for your platform | 
|  | #endif | 
|  | // clang-format on | 
|  | return containers::array_view<NGUnwindRegisterMap>(std::begin(kGeneral), std::end(kGeneral)); | 
|  | } | 
|  |  | 
|  | zx_status_t UnwindStackAndroid(const ProcessHandle& process, const ModuleList& modules, | 
|  | const ThreadHandle& thread, const GeneralRegisters& regs, | 
|  | size_t max_depth, std::vector<debug_ipc::StackFrame>* stack) { | 
|  | unwindstack::Maps maps; | 
|  | for (size_t i = 0; i < modules.modules().size(); i++) { | 
|  | // Our module currently doesn't have a size so just report the next address boundary. | 
|  | // TODO(brettw) hook up the real size. | 
|  | uint64_t end; | 
|  | if (i < modules.modules().size() - 1) | 
|  | end = modules.modules()[i + 1].base; | 
|  | else | 
|  | end = std::numeric_limits<uint64_t>::max(); | 
|  |  | 
|  | // The offset of the module is the offset in the file where the memory map starts. For | 
|  | // libraries, we can currently always assume 0. | 
|  | uint64_t offset = 0; | 
|  |  | 
|  | uint64_t flags = 0;  // We don't have flags. | 
|  |  | 
|  | // Don't know what this is, it's not set by the Android impl that reads | 
|  | // from /proc. | 
|  | uint64_t load_bias = 0; | 
|  |  | 
|  | maps.Add(modules.modules()[i].base, end, offset, flags, modules.modules()[i].name, load_bias); | 
|  | } | 
|  |  | 
|  | unwindstack::RegsFuchsia unwind_regs; | 
|  | unwind_regs.Set(regs.GetNativeRegisters()); | 
|  |  | 
|  | auto memory = std::make_shared<unwindstack::MemoryFuchsia>(process.GetNativeHandle().get()); | 
|  |  | 
|  | // Always ask for one more frame than requested so we can get the canonical frame address for the | 
|  | // frames we do return (the CFA is the previous frame's stack pointer at the time of the call). | 
|  | unwindstack::Unwinder unwinder(max_depth + 1, &maps, &unwind_regs, std::move(memory), true); | 
|  | // We don't need names from the unwinder since those are computed in the client. This will | 
|  | // generally fail anyway since the target binaries don't usually have symbols, so turning off | 
|  | // makes it a little more efficient. | 
|  | unwinder.SetResolveNames(false); | 
|  |  | 
|  | unwinder.Unwind(); | 
|  |  | 
|  | stack->reserve(unwinder.NumFrames()); | 
|  | for (size_t i = 0; i < unwinder.NumFrames(); i++) { | 
|  | const auto& src = unwinder.frames()[i]; | 
|  |  | 
|  | if (i > 0) { | 
|  | // The next frame's canonical frame address is our stack pointer. | 
|  | debug_ipc::StackFrame* next_frame = &(*stack)[i - 1]; | 
|  | next_frame->cfa = src.sp; | 
|  | } | 
|  |  | 
|  | // This termination condition is in the middle here because we don't know for sure if the | 
|  | // unwinder was able to return the number of frames we requested, and we always want to fill in | 
|  | // the CFA (above) for the returned frames if possible. | 
|  | if (i == max_depth) | 
|  | break; | 
|  |  | 
|  | debug_ipc::StackFrame* dest = &stack->emplace_back(); | 
|  | dest->ip = src.pc; | 
|  | dest->sp = src.sp; | 
|  | if (src.regs) { | 
|  | src.regs->IterateRegisters([&dest](const char* name, uint64_t val) { | 
|  | // TODO(sadmac): It'd be nice to be using some sort of ID constant instead of a converted | 
|  | // string here. | 
|  | auto id = debug_ipc::StringToRegisterID(name); | 
|  | if (id != RegisterID::kUnknown) { | 
|  | dest->regs.emplace_back(id, val); | 
|  | } | 
|  | }); | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Callback for ngunwind. | 
|  | int LookupDso(void* context, unw_word_t pc, unw_word_t* base, const char** name) { | 
|  | // Context is a ModuleVector sorted by load address, need to find the largest one smaller than or | 
|  | // equal to the pc. | 
|  | // | 
|  | // We could use lower_bound for better perf with lots of modules but we expect O(10) modules. | 
|  | const ModuleList* modules = static_cast<const ModuleList*>(context); | 
|  | for (int i = static_cast<int>(modules->modules().size()) - 1; i >= 0; i--) { | 
|  | const debug_ipc::Module& module = modules->modules()[i]; | 
|  | if (pc >= module.base) { | 
|  | *base = module.base; | 
|  | *name = module.name.c_str(); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | zx_status_t UnwindStackNgUnwind(const ProcessHandle& process, const ModuleList& modules, | 
|  | const ThreadHandle& thread, const GeneralRegisters& regs, | 
|  | size_t max_depth, std::vector<debug_ipc::StackFrame>* stack) { | 
|  | stack->clear(); | 
|  |  | 
|  | // Any of these functions can fail if the program or thread was killed out from under us. | 
|  | unw_fuchsia_info_t* fuchsia = unw_create_fuchsia( | 
|  | process.GetNativeHandle().get(), thread.GetNativeHandle().get(), (void*)&modules, &LookupDso); | 
|  | if (!fuchsia) | 
|  | return ZX_ERR_INTERNAL; | 
|  |  | 
|  | unw_addr_space_t remote_aspace = | 
|  | unw_create_addr_space(const_cast<unw_accessors_t*>(&_UFuchsia_accessors), 0); | 
|  | if (!remote_aspace) { | 
|  | unw_destroy_fuchsia(fuchsia); | 
|  | return ZX_ERR_INTERNAL; | 
|  | } | 
|  |  | 
|  | unw_cursor_t cursor; | 
|  | if (unw_init_remote(&cursor, remote_aspace, fuchsia) < 0) { | 
|  | unw_destroy_addr_space(remote_aspace); | 
|  | unw_destroy_fuchsia(fuchsia); | 
|  | return ZX_ERR_INTERNAL; | 
|  | } | 
|  |  | 
|  | // Compute the register IDs for this platform's IP/SP. | 
|  | auto arch = arch::GetCurrentArch(); | 
|  | RegisterID ip_reg_id = GetSpecialRegisterID(arch, debug_ipc::SpecialRegisterType::kIP); | 
|  | RegisterID sp_reg_id = GetSpecialRegisterID(arch, debug_ipc::SpecialRegisterType::kSP); | 
|  |  | 
|  | // Top stack frame. | 
|  | debug_ipc::StackFrame frame; | 
|  | frame.ip = regs.ip(); | 
|  | frame.sp = regs.sp(); | 
|  | frame.cfa = 0; | 
|  | regs.CopyTo(frame.regs); | 
|  | stack->push_back(std::move(frame)); | 
|  |  | 
|  | while (frame.sp >= 0x1000000 && stack->size() < max_depth + 1) { | 
|  | int ret = unw_step(&cursor); | 
|  | if (ret <= 0) | 
|  | break; | 
|  |  | 
|  | // Clear registers left over from previous frame. | 
|  | frame.regs.clear(); | 
|  |  | 
|  | unw_word_t val; | 
|  | unw_get_reg(&cursor, UNW_REG_IP, &val); | 
|  | if (val == 0) | 
|  | break;  // Null code address means we're done. | 
|  | frame.ip = val; | 
|  | frame.regs.emplace_back(ip_reg_id, val); | 
|  |  | 
|  | unw_get_reg(&cursor, UNW_REG_SP, &val); | 
|  | frame.sp = val; | 
|  | frame.regs.emplace_back(sp_reg_id, val); | 
|  |  | 
|  | // Previous frame's CFA is our SP. | 
|  | if (!stack->empty()) | 
|  | stack->back().cfa = val; | 
|  |  | 
|  | // Other registers. | 
|  | for (auto& [ng_id, reg_id] : GetNGUnwindGeneralRegisters()) { | 
|  | unw_get_reg(&cursor, ng_id, &val); | 
|  | frame.regs.emplace_back(reg_id, val); | 
|  | } | 
|  |  | 
|  | // This "if" statement prevents adding more than the max number of stack entries since we | 
|  | // requested one more from libunwind to get the CFA. | 
|  | if (stack->size() < max_depth) | 
|  | stack->push_back(frame); | 
|  | } | 
|  |  | 
|  | // The last stack entry will typically have a 0 IP address. We want to send this anyway because it | 
|  | // will hold the initial stack pointer for the thread, which in turn allows computation of the | 
|  | // first real frame's fingerprint. | 
|  |  | 
|  | unw_destroy_addr_space(remote_aspace); | 
|  | unw_destroy_fuchsia(fuchsia); | 
|  | return ZX_OK; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void SetUnwinderType(UnwinderType type) { unwinder_type = type; } | 
|  |  | 
|  | zx_status_t UnwindStack(const ProcessHandle& process, const ModuleList& modules, | 
|  | const ThreadHandle& thread, const GeneralRegisters& regs, size_t max_depth, | 
|  | std::vector<debug_ipc::StackFrame>* stack) { | 
|  | switch (unwinder_type) { | 
|  | case UnwinderType::kNgUnwind: | 
|  | return UnwindStackNgUnwind(process, modules, thread, regs, max_depth, stack); | 
|  | case UnwinderType::kAndroid: | 
|  | return UnwindStackAndroid(process, modules, thread, regs, max_depth, stack); | 
|  | } | 
|  | return ZX_ERR_NOT_SUPPORTED; | 
|  | } | 
|  |  | 
|  | }  // namespace debug_agent |