blob: c3e467885c7e77f346d4a356c71d3145479ec371 [file] [log] [blame]
// 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/zxdb/client/minidump_remote_api.h"
// This has to go up here due to some strange header conflicts.
#include "src/lib/elflib/elflib.h"
#include <algorithm>
#include <cstring>
#include "garnet/third_party/libunwindstack/include/unwindstack/UcontextArm64.h"
#include "garnet/third_party/libunwindstack/include/unwindstack/UcontextX86_64.h"
#include "garnet/third_party/libunwindstack/include/unwindstack/Unwinder.h"
#include "src/developer/debug/ipc/client_protocol.h"
#include "src/developer/debug/shared/message_loop.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/common/string_util.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "third_party/crashpad/snapshot/memory_map_region_snapshot.h"
#include "third_party/crashpad/util/misc/uuid.h"
namespace zxdb {
namespace {
Err ErrNoLive() {
return Err(ErrType::kNoConnection, "System is no longer live");
}
Err ErrNoDump() { return Err("Core dump failed to open"); }
Err ErrNoArch() { return Err("Architecture not supported"); }
template <typename ReplyType>
void ErrNoLive(std::function<void(const Err&, ReplyType)> cb) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb]() { cb(ErrNoLive(), ReplyType()); });
}
template <typename ReplyType>
void ErrNoDump(std::function<void(const Err&, ReplyType)> cb) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb]() { cb(ErrNoDump(), ReplyType()); });
}
template <typename ReplyType>
void ErrNoArch(std::function<void(const Err&, ReplyType)> cb) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb]() { cb(ErrNoArch(), ReplyType()); });
}
template <typename ReplyType>
void Succeed(std::function<void(const Err&, ReplyType)> cb, ReplyType r) {
debug_ipc::MessageLoop::Current()->PostTask(FROM_HERE,
[cb, r]() { cb(Err(), r); });
}
template <typename ValueType>
void AddReg(debug_ipc::RegisterCategory* category, debug_ipc::RegisterID id,
const ValueType& value) {
auto& reg = category->registers.emplace_back();
reg.id = id;
reg.data.resize(sizeof(ValueType));
std::memcpy(reg.data.data(), reinterpret_cast<const void*>(&value),
reg.data.size());
}
template <typename IterType>
debug_ipc::RegisterCategory* MakeCategory(
IterType& pos, debug_ipc::RegisterCategory::Type type,
debug_ipc::ReadRegistersReply* reply) {
if (*pos == type) {
pos++;
auto category = &reply->categories.emplace_back();
category->type = type;
return category;
}
return nullptr;
}
void PopulateRegistersARM64(const crashpad::CPUContextARM64& ctx,
const debug_ipc::ReadRegistersRequest& request,
debug_ipc::ReadRegistersReply* reply) {
auto pos = request.categories.begin();
using R = debug_ipc::RegisterID;
auto category =
MakeCategory(pos, debug_ipc::RegisterCategory::Type::kGeneral, reply);
if (category != nullptr) {
AddReg(category, R::kARMv8_x0, ctx.regs[0]);
AddReg(category, R::kARMv8_x1, ctx.regs[1]);
AddReg(category, R::kARMv8_x2, ctx.regs[2]);
AddReg(category, R::kARMv8_x3, ctx.regs[3]);
AddReg(category, R::kARMv8_x4, ctx.regs[4]);
AddReg(category, R::kARMv8_x5, ctx.regs[5]);
AddReg(category, R::kARMv8_x6, ctx.regs[6]);
AddReg(category, R::kARMv8_x7, ctx.regs[7]);
AddReg(category, R::kARMv8_x8, ctx.regs[8]);
AddReg(category, R::kARMv8_x9, ctx.regs[9]);
AddReg(category, R::kARMv8_x10, ctx.regs[10]);
AddReg(category, R::kARMv8_x11, ctx.regs[11]);
AddReg(category, R::kARMv8_x12, ctx.regs[12]);
AddReg(category, R::kARMv8_x13, ctx.regs[13]);
AddReg(category, R::kARMv8_x14, ctx.regs[14]);
AddReg(category, R::kARMv8_x15, ctx.regs[15]);
AddReg(category, R::kARMv8_x16, ctx.regs[16]);
AddReg(category, R::kARMv8_x17, ctx.regs[17]);
AddReg(category, R::kARMv8_x18, ctx.regs[18]);
AddReg(category, R::kARMv8_x19, ctx.regs[19]);
AddReg(category, R::kARMv8_x20, ctx.regs[20]);
AddReg(category, R::kARMv8_x21, ctx.regs[21]);
AddReg(category, R::kARMv8_x22, ctx.regs[22]);
AddReg(category, R::kARMv8_x23, ctx.regs[23]);
AddReg(category, R::kARMv8_x24, ctx.regs[24]);
AddReg(category, R::kARMv8_x25, ctx.regs[25]);
AddReg(category, R::kARMv8_x26, ctx.regs[26]);
AddReg(category, R::kARMv8_x27, ctx.regs[27]);
AddReg(category, R::kARMv8_x28, ctx.regs[28]);
AddReg(category, R::kARMv8_x29, ctx.regs[29]);
AddReg(category, R::kARMv8_lr, ctx.regs[30]);
AddReg(category, R::kARMv8_sp, ctx.sp);
AddReg(category, R::kARMv8_pc, ctx.pc);
AddReg(category, R::kARMv8_cpsr, ctx.spsr);
}
// ARM doesn't define any registers in this category.
MakeCategory(pos, debug_ipc::RegisterCategory::Type::kFP, reply);
category =
MakeCategory(pos, debug_ipc::RegisterCategory::Type::kVector, reply);
if (category != nullptr) {
AddReg(category, R::kARMv8_fpcr, ctx.fpcr);
AddReg(category, R::kARMv8_fpsr, ctx.fpsr);
AddReg(category, R::kARMv8_v0, ctx.fpsimd[0]);
AddReg(category, R::kARMv8_v1, ctx.fpsimd[1]);
AddReg(category, R::kARMv8_v2, ctx.fpsimd[2]);
AddReg(category, R::kARMv8_v3, ctx.fpsimd[3]);
AddReg(category, R::kARMv8_v4, ctx.fpsimd[4]);
AddReg(category, R::kARMv8_v5, ctx.fpsimd[5]);
AddReg(category, R::kARMv8_v6, ctx.fpsimd[6]);
AddReg(category, R::kARMv8_v7, ctx.fpsimd[7]);
AddReg(category, R::kARMv8_v8, ctx.fpsimd[8]);
AddReg(category, R::kARMv8_v9, ctx.fpsimd[9]);
AddReg(category, R::kARMv8_v10, ctx.fpsimd[10]);
AddReg(category, R::kARMv8_v11, ctx.fpsimd[11]);
AddReg(category, R::kARMv8_v12, ctx.fpsimd[12]);
AddReg(category, R::kARMv8_v13, ctx.fpsimd[13]);
AddReg(category, R::kARMv8_v14, ctx.fpsimd[14]);
AddReg(category, R::kARMv8_v15, ctx.fpsimd[15]);
AddReg(category, R::kARMv8_v16, ctx.fpsimd[16]);
AddReg(category, R::kARMv8_v17, ctx.fpsimd[17]);
AddReg(category, R::kARMv8_v18, ctx.fpsimd[18]);
AddReg(category, R::kARMv8_v19, ctx.fpsimd[19]);
AddReg(category, R::kARMv8_v20, ctx.fpsimd[20]);
AddReg(category, R::kARMv8_v21, ctx.fpsimd[21]);
AddReg(category, R::kARMv8_v22, ctx.fpsimd[22]);
AddReg(category, R::kARMv8_v23, ctx.fpsimd[23]);
AddReg(category, R::kARMv8_v24, ctx.fpsimd[24]);
AddReg(category, R::kARMv8_v25, ctx.fpsimd[25]);
AddReg(category, R::kARMv8_v26, ctx.fpsimd[26]);
AddReg(category, R::kARMv8_v27, ctx.fpsimd[27]);
AddReg(category, R::kARMv8_v28, ctx.fpsimd[28]);
AddReg(category, R::kARMv8_v29, ctx.fpsimd[29]);
AddReg(category, R::kARMv8_v30, ctx.fpsimd[30]);
AddReg(category, R::kARMv8_v31, ctx.fpsimd[31]);
}
// ARM Doesn't define any registers in this category either.
MakeCategory(pos, debug_ipc::RegisterCategory::Type::kDebug, reply);
}
void PopulateRegistersX86_64(const crashpad::CPUContextX86_64& ctx,
const debug_ipc::ReadRegistersRequest& request,
debug_ipc::ReadRegistersReply* reply) {
auto pos = request.categories.begin();
using R = debug_ipc::RegisterID;
auto category =
MakeCategory(pos, debug_ipc::RegisterCategory::Type::kGeneral, reply);
if (category != nullptr) {
AddReg(category, R::kX64_rax, ctx.rax);
AddReg(category, R::kX64_rbx, ctx.rbx);
AddReg(category, R::kX64_rcx, ctx.rcx);
AddReg(category, R::kX64_rdx, ctx.rdx);
AddReg(category, R::kX64_rsi, ctx.rsi);
AddReg(category, R::kX64_rdi, ctx.rdi);
AddReg(category, R::kX64_rbp, ctx.rbp);
AddReg(category, R::kX64_rsp, ctx.rsp);
AddReg(category, R::kX64_r8, ctx.r8);
AddReg(category, R::kX64_r9, ctx.r9);
AddReg(category, R::kX64_r10, ctx.r10);
AddReg(category, R::kX64_r11, ctx.r11);
AddReg(category, R::kX64_r12, ctx.r12);
AddReg(category, R::kX64_r13, ctx.r13);
AddReg(category, R::kX64_r14, ctx.r14);
AddReg(category, R::kX64_r15, ctx.r15);
AddReg(category, R::kX64_rip, ctx.rip);
AddReg(category, R::kX64_rflags, ctx.rflags);
}
category = MakeCategory(pos, debug_ipc::RegisterCategory::Type::kFP, reply);
if (category != nullptr) {
AddReg(category, R::kX64_fcw, ctx.fxsave.fcw);
AddReg(category, R::kX64_fsw, ctx.fxsave.fsw);
AddReg(category, R::kX64_ftw, ctx.fxsave.ftw);
AddReg(category, R::kX64_fop, ctx.fxsave.fop);
AddReg(category, R::kX64_fip, ctx.fxsave.fpu_ip_64);
AddReg(category, R::kX64_fdp, ctx.fxsave.fpu_dp_64);
AddReg(category, R::kX64_st0, ctx.fxsave.st_mm[0]);
AddReg(category, R::kX64_st1, ctx.fxsave.st_mm[1]);
AddReg(category, R::kX64_st2, ctx.fxsave.st_mm[2]);
AddReg(category, R::kX64_st3, ctx.fxsave.st_mm[3]);
AddReg(category, R::kX64_st4, ctx.fxsave.st_mm[4]);
AddReg(category, R::kX64_st5, ctx.fxsave.st_mm[5]);
AddReg(category, R::kX64_st6, ctx.fxsave.st_mm[6]);
AddReg(category, R::kX64_st7, ctx.fxsave.st_mm[7]);
}
category =
MakeCategory(pos, debug_ipc::RegisterCategory::Type::kVector, reply);
if (category != nullptr) {
AddReg(category, R::kX64_mxcsr, ctx.fxsave.mxcsr);
AddReg(category, R::kX64_xmm0, ctx.fxsave.xmm[0]);
AddReg(category, R::kX64_xmm1, ctx.fxsave.xmm[1]);
AddReg(category, R::kX64_xmm2, ctx.fxsave.xmm[2]);
AddReg(category, R::kX64_xmm3, ctx.fxsave.xmm[3]);
AddReg(category, R::kX64_xmm4, ctx.fxsave.xmm[4]);
AddReg(category, R::kX64_xmm5, ctx.fxsave.xmm[5]);
AddReg(category, R::kX64_xmm6, ctx.fxsave.xmm[6]);
AddReg(category, R::kX64_xmm7, ctx.fxsave.xmm[7]);
AddReg(category, R::kX64_xmm8, ctx.fxsave.xmm[8]);
AddReg(category, R::kX64_xmm9, ctx.fxsave.xmm[9]);
AddReg(category, R::kX64_xmm10, ctx.fxsave.xmm[10]);
AddReg(category, R::kX64_xmm11, ctx.fxsave.xmm[11]);
AddReg(category, R::kX64_xmm12, ctx.fxsave.xmm[12]);
AddReg(category, R::kX64_xmm13, ctx.fxsave.xmm[13]);
AddReg(category, R::kX64_xmm14, ctx.fxsave.xmm[14]);
AddReg(category, R::kX64_xmm15, ctx.fxsave.xmm[15]);
// YMM registers are missing from minidump at this time.
}
category =
MakeCategory(pos, debug_ipc::RegisterCategory::Type::kDebug, reply);
if (category != nullptr) {
AddReg(category, R::kX64_dr0, ctx.dr0);
AddReg(category, R::kX64_dr1, ctx.dr1);
AddReg(category, R::kX64_dr2, ctx.dr2);
AddReg(category, R::kX64_dr3, ctx.dr3);
AddReg(category, R::kX64_dr6, ctx.dr6);
AddReg(category, R::kX64_dr7, ctx.dr7);
}
}
class MinidumpReadDelegate : public crashpad::MemorySnapshot::Delegate {
public:
// Construct a delegate object for reading minidump memory regions.
//
// Minidump will always give us a pointer to the whole region and its size.
// We give an offset and size of a portion of that region to read. Then when
// the MemorySnapshotDelegateRead function is called, just that section will
// be copied out into the ptr we give here.
explicit MinidumpReadDelegate(uint64_t offset, size_t size, uint8_t* ptr)
: offset_(offset), size_(size), ptr_(ptr) {}
bool MemorySnapshotDelegateRead(void* data, size_t size) override {
if (offset_ + size_ > size) {
return false;
}
auto data_u8 = reinterpret_cast<uint8_t*>(data);
data_u8 += offset_;
std::copy(data_u8, data_u8 + size_, ptr_);
return true;
}
private:
uint64_t offset_;
size_t size_;
uint8_t* ptr_;
};
class SnapshotMemoryRegion : public MinidumpRemoteAPI::MemoryRegion {
public:
// Construct a memory region from a crashpad MemorySnapshot. The pointer
// should always be derived from the minidump_ object, and will thus always
// share its lifetime.
explicit SnapshotMemoryRegion(const crashpad::MemorySnapshot* snapshot)
: MinidumpRemoteAPI::MemoryRegion(snapshot->Address(), snapshot->Size()),
snapshot_(snapshot) {}
virtual ~SnapshotMemoryRegion() = default;
std::optional<std::vector<uint8_t>> Read(uint64_t offset,
size_t size) const override;
private:
const crashpad::MemorySnapshot* snapshot_;
};
std::optional<std::vector<uint8_t>> SnapshotMemoryRegion::Read(
uint64_t offset, size_t size) const {
std::vector<uint8_t> data;
data.resize(size);
MinidumpReadDelegate d(offset, size, data.data());
if (!snapshot_->Read(&d)) {
return std::nullopt;
}
return std::move(data);
}
class ElfMemoryRegion : public MinidumpRemoteAPI::MemoryRegion {
public:
// Construct a memory region from a crashpad MemorySnapshot. The pointer
// should always be derived from the minidump_ object, and will thus always
// share its lifetime.
explicit ElfMemoryRegion(std::shared_ptr<elflib::ElfLib>& elf,
uint64_t start_in, size_t size_in, size_t idx)
: MinidumpRemoteAPI::MemoryRegion(start_in, size_in),
idx_(idx),
elf_(elf) {}
virtual ~ElfMemoryRegion() = default;
std::optional<std::vector<uint8_t>> Read(uint64_t offset,
size_t size) const override;
private:
size_t idx_;
std::shared_ptr<elflib::ElfLib> elf_;
};
std::optional<std::vector<uint8_t>> ElfMemoryRegion::Read(uint64_t offset,
size_t size) const {
if (offset + size > this->size) {
return std::nullopt;
}
auto got = elf_->GetSegmentData(idx_);
if (!got.ptr) {
return std::nullopt;
}
size_t read_end = std::min(got.size, static_cast<size_t>(offset + size));
std::vector<uint8_t> data;
std::copy(got.ptr + offset, got.ptr + read_end, std::back_inserter(data));
// If the mapped size is larger than the file data, we pad with zeros per
// spec.
data.resize(size, 0);
return std::move(data);
}
std::string MinidumpGetUUID(const crashpad::ModuleSnapshot& mod) {
auto build_id = mod.BuildID();
if (build_id.empty()) {
return std::string();
}
// 2 hex characters per 1 byte, so the string size is twice the data size.
// Hopefully we'll be overwriting the zeros we're filling with.
std::string ret(build_id.size() * 2, '\0');
char* pos = &ret[0];
for (const auto& byte : build_id) {
sprintf(pos, "%02hhx", byte);
pos += 2;
}
return ret;
}
class MinidumpUnwindMemory : public unwindstack::Memory {
public:
MinidumpUnwindMemory(
const std::vector<std::unique_ptr<MinidumpRemoteAPI::MemoryRegion>>&
regions)
: regions_(regions) {}
size_t Read(uint64_t addr, void* dst, size_t size) override {
uint8_t* dst8 = reinterpret_cast<uint8_t*>(dst);
size_t read = 0;
for (const auto& region : regions_) {
if (region->start > addr) {
return read;
}
if ((region->start + region->size) <= addr) {
continue;
}
size_t offset = addr - region->start;
size_t to_read = std::min(region->size - offset, size);
auto data = region->Read(offset, to_read);
if (!data) {
return read;
}
std::copy(data->begin(), data->end(), dst8);
dst8 += data->size();
addr += data->size();
size -= data->size();
read += data->size();
if (!size) {
break;
}
}
return read;
}
private:
const std::vector<std::unique_ptr<MinidumpRemoteAPI::MemoryRegion>>& regions_;
};
} // namespace
MinidumpRemoteAPI::MinidumpRemoteAPI(Session* session) : session_(session) {}
MinidumpRemoteAPI::~MinidumpRemoteAPI() = default;
std::string MinidumpRemoteAPI::ProcessName() {
if (!minidump_) {
return std::string();
}
auto mods = minidump_->Modules();
if (mods.size() == 0) {
return "<core dump>";
}
return mods[0]->Name();
}
std::vector<debug_ipc::Module> MinidumpRemoteAPI::GetModules() {
if (!minidump_) {
return {};
}
std::vector<debug_ipc::Module> ret;
for (const auto& minidump_mod : minidump_->Modules()) {
auto& mod = ret.emplace_back();
mod.name = minidump_mod->Name();
mod.base = minidump_mod->Address();
mod.build_id = MinidumpGetUUID(*minidump_mod);
}
return ret;
}
const crashpad::ThreadSnapshot* MinidumpRemoteAPI::GetThreadById(uint64_t id) {
for (const auto& item : minidump_->Threads()) {
if (item->ThreadID() == id) {
return item;
}
}
return nullptr;
}
std::unique_ptr<unwindstack::Regs> MinidumpRemoteAPI::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> MinidumpRemoteAPI::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));
}
void MinidumpRemoteAPI::CollectMemory() {
for (const auto& thread : minidump_->Threads()) {
const auto& stack = thread->Stack();
if (!stack) {
continue;
}
memory_.push_back(std::make_unique<SnapshotMemoryRegion>(stack));
}
auto& build_id_index = session_->system().GetSymbols()->build_id_index();
for (const auto& minidump_mod : minidump_->Modules()) {
uint64_t base = minidump_mod->Address();
auto path = build_id_index.FileForBuildID(MinidumpGetUUID(*minidump_mod),
DebugSymbolFileType::kBinary);
std::shared_ptr<elflib::ElfLib> elf = elflib::ElfLib::Create(path);
if (!elf) {
continue;
}
const auto& segments = elf->GetSegmentHeaders();
for (size_t i = 0; i < segments.size(); i++) {
const auto& segment = segments[i];
// Only PT_LOAD segments are actually mapped. The rest are informational.
if (segment.p_type != PT_LOAD) {
continue;
}
if (segment.p_flags & PF_W) {
// Writable segment. Data in the ELF file might not match what was
// present at the time of the crash.
continue;
}
memory_.push_back(std::make_unique<ElfMemoryRegion>(
elf, segment.p_vaddr + base, segment.p_memsz, i));
}
}
std::sort(memory_.begin(), memory_.end(),
[](const std::unique_ptr<MinidumpRemoteAPI::MemoryRegion>& a,
const std::unique_ptr<MinidumpRemoteAPI::MemoryRegion>& b) {
return a->start < b->start;
});
}
Err MinidumpRemoteAPI::Open(const std::string& path) {
crashpad::FileReader reader;
if (minidump_) {
return Err("Dump already open");
}
if (!reader.Open(base::FilePath(path))) {
return Err(fxl::StringPrintf("Could not open %s", path.c_str()));
}
minidump_ = std::make_unique<crashpad::ProcessSnapshotMinidump>();
bool success = minidump_->Initialize(&reader);
reader.Close();
if (!success) {
minidump_.release();
return Err(fxl::StringPrintf("Minidump %s not valid", path.c_str()));
}
CollectMemory();
return Err();
}
Err MinidumpRemoteAPI::Close() {
if (!minidump_) {
return Err("No open dump to close");
}
minidump_.reset();
return Err();
}
void MinidumpRemoteAPI::Hello(
const debug_ipc::HelloRequest& request,
std::function<void(const Err&, debug_ipc::HelloReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::HelloReply reply;
const auto& threads = minidump_->Threads();
if (threads.empty()) {
Succeed(cb, reply);
}
const auto& context = *threads[0]->Context();
switch (context.architecture) {
case crashpad::CPUArchitecture::kCPUArchitectureARM64:
reply.arch = debug_ipc::Arch::kArm64;
break;
case crashpad::CPUArchitecture::kCPUArchitectureX86_64:
reply.arch = debug_ipc::Arch::kX64;
break;
default:
break;
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::Launch(
const debug_ipc::LaunchRequest& request,
std::function<void(const Err&, debug_ipc::LaunchReply)> cb) {
ErrNoLive(cb);
}
void MinidumpRemoteAPI::Kill(
const debug_ipc::KillRequest& request,
std::function<void(const Err&, debug_ipc::KillReply)> cb) {
ErrNoLive(cb);
}
constexpr uint32_t kAttachOk = 0;
constexpr uint32_t kAttachNotFound = 1;
void MinidumpRemoteAPI::Attach(
const debug_ipc::AttachRequest& request,
std::function<void(const Err&, debug_ipc::AttachReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::AttachReply reply;
reply.name = ProcessName();
if (static_cast<pid_t>(request.koid) != minidump_->ProcessID()) {
reply.status = kAttachNotFound;
Succeed(cb, reply);
return;
}
reply.status = kAttachOk;
attached_ = true;
std::vector<debug_ipc::NotifyThread> notifications;
for (const auto& thread : minidump_->Threads()) {
auto& notification = notifications.emplace_back();
notification.record.process_koid = minidump_->ProcessID();
notification.record.thread_koid = thread->ThreadID();
notification.record.state = debug_ipc::ThreadRecord::State::kCoreDump;
}
Session* session = session_;
debug_ipc::NotifyModules mod_notification;
mod_notification.process_koid = minidump_->ProcessID();
mod_notification.modules = GetModules();
std::function<void(const Err&, debug_ipc::AttachReply)> new_cb =
[cb, notifications, mod_notification, session](const Err& e,
debug_ipc::AttachReply a) {
cb(e, a);
for (const auto& notification : notifications) {
session->DispatchNotifyThreadStarting(notification);
}
session->DispatchNotifyModules(mod_notification);
};
Succeed(new_cb, reply);
}
void MinidumpRemoteAPI::Detach(
const debug_ipc::DetachRequest& request,
std::function<void(const Err&, debug_ipc::DetachReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::DetachReply reply;
if (static_cast<pid_t>(request.koid) == minidump_->ProcessID() && attached_) {
reply.status = kAttachOk;
attached_ = false;
} else {
reply.status = kAttachNotFound;
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::Modules(
const debug_ipc::ModulesRequest& request,
std::function<void(const Err&, debug_ipc::ModulesReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::ModulesReply reply;
if (static_cast<pid_t>(request.process_koid) != minidump_->ProcessID()) {
Succeed(cb, reply);
return;
}
reply.modules = GetModules();
Succeed(cb, reply);
}
void MinidumpRemoteAPI::Pause(
const debug_ipc::PauseRequest& request,
std::function<void(const Err&, debug_ipc::PauseReply)> cb) {
ErrNoLive(cb);
}
void MinidumpRemoteAPI::Resume(
const debug_ipc::ResumeRequest& request,
std::function<void(const Err&, debug_ipc::ResumeReply)> cb) {
ErrNoLive(cb);
}
void MinidumpRemoteAPI::ProcessTree(
const debug_ipc::ProcessTreeRequest& request,
std::function<void(const Err&, debug_ipc::ProcessTreeReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::ProcessTreeRecord record;
record.type = debug_ipc::ProcessTreeRecord::Type::kProcess;
record.name = ProcessName();
record.koid = minidump_->ProcessID();
debug_ipc::ProcessTreeReply reply{
.root = record,
};
Succeed(cb, reply);
}
void MinidumpRemoteAPI::Threads(
const debug_ipc::ThreadsRequest& request,
std::function<void(const Err&, debug_ipc::ThreadsReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::ThreadsReply reply;
if (static_cast<pid_t>(request.process_koid) == minidump_->ProcessID()) {
for (const auto& thread : minidump_->Threads()) {
auto& record = reply.threads.emplace_back();
record.process_koid = request.process_koid;
record.thread_koid = thread->ThreadID();
record.state = debug_ipc::ThreadRecord::State::kCoreDump;
}
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::ReadMemory(
const debug_ipc::ReadMemoryRequest& request,
std::function<void(const Err&, debug_ipc::ReadMemoryReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::ReadMemoryReply reply;
uint64_t loc = request.address;
uint64_t end = request.address + request.size;
if (static_cast<pid_t>(request.process_koid) != minidump_->ProcessID()) {
Succeed(cb, reply);
return;
}
for (const auto& reg : memory_) {
if (loc == end) {
break;
}
if (reg->start + reg->size <= loc) {
continue;
}
if (reg->start > loc) {
uint64_t stop = std::min(reg->start, end);
reply.blocks.emplace_back();
reply.blocks.back().address = loc;
reply.blocks.back().valid = false;
reply.blocks.back().size = static_cast<uint32_t>(stop - loc);
loc = stop;
if (loc == end) {
break;
}
}
uint64_t stop = std::min(reg->start + reg->size, end);
auto data = reg->Read(loc - reg->start, stop - loc);
reply.blocks.emplace_back();
reply.blocks.back().address = loc;
reply.blocks.back().valid = !!data;
reply.blocks.back().size = static_cast<uint32_t>(stop - loc);
reply.blocks.back().data = std::move(*data);
loc += reply.blocks.back().size;
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::ReadRegisters(
const debug_ipc::ReadRegistersRequest& request,
std::function<void(const Err&, debug_ipc::ReadRegistersReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::ReadRegistersReply reply;
if (static_cast<pid_t>(request.process_koid) != minidump_->ProcessID()) {
Succeed(cb, reply);
return;
}
const crashpad::ThreadSnapshot* thread = GetThreadById(request.thread_koid);
if (thread == nullptr) {
Succeed(cb, reply);
return;
}
const auto& context = *thread->Context();
switch (context.architecture) {
case crashpad::CPUArchitecture::kCPUArchitectureARM64:
PopulateRegistersARM64(*context.arm64, request, &reply);
break;
case crashpad::CPUArchitecture::kCPUArchitectureX86_64:
PopulateRegistersX86_64(*context.x86_64, request, &reply);
break;
default:
ErrNoArch(cb);
return;
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::AddOrChangeBreakpoint(
const debug_ipc::AddOrChangeBreakpointRequest& request,
std::function<void(const Err&, debug_ipc::AddOrChangeBreakpointReply)> cb) {
ErrNoLive(cb);
}
void MinidumpRemoteAPI::RemoveBreakpoint(
const debug_ipc::RemoveBreakpointRequest& request,
std::function<void(const Err&, debug_ipc::RemoveBreakpointReply)> cb) {
ErrNoLive(cb);
}
void MinidumpRemoteAPI::SysInfo(
const debug_ipc::SysInfoRequest& request,
std::function<void(const Err&, debug_ipc::SysInfoReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::SysInfoReply reply;
reply.version = minidump_->System()->OSVersionFull();
reply.num_cpus = minidump_->System()->CPUCount();
reply.memory_mb = 0;
reply.hw_breakpoint_count = 0;
reply.hw_watchpoint_count = 0;
Succeed(cb, reply);
}
void MinidumpRemoteAPI::ThreadStatus(
const debug_ipc::ThreadStatusRequest& request,
std::function<void(const Err&, debug_ipc::ThreadStatusReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::ThreadStatusReply reply;
if (static_cast<pid_t>(request.process_koid) != minidump_->ProcessID()) {
Succeed(cb, reply);
return;
}
const crashpad::ThreadSnapshot* thread = GetThreadById(request.thread_koid);
if (thread == nullptr) {
Succeed(cb, reply);
return;
}
reply.record.process_koid = request.process_koid;
reply.record.thread_koid = thread->ThreadID();
reply.record.state = debug_ipc::ThreadRecord::State::kCoreDump;
reply.record.stack_amount = debug_ipc::ThreadRecord::StackAmount::kFull;
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;
uint64_t bp;
switch (context.architecture) {
case crashpad::CPUArchitecture::kCPUArchitectureARM64:
regs = GetUnwindRegsARM64(*context.arm64, stack_size);
bp = context.arm64->regs[29];
break;
case crashpad::CPUArchitecture::kCPUArchitectureX86_64:
regs = GetUnwindRegsX86_64(*context.x86_64, stack_size);
bp = context.x86_64->rbp;
break;
default:
ErrNoArch(cb);
return;
}
auto modules = minidump_->Modules();
std::sort(
modules.begin(), modules.end(),
[](const crashpad::ModuleSnapshot* a, const crashpad::ModuleSnapshot* 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(), std::make_shared<MinidumpUnwindMemory>(memory_));
unwinder.Unwind();
reply.record.frames.resize(unwinder.NumFrames());
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
const auto& src = unwinder.frames()[i];
debug_ipc::StackFrame& dest = reply.record.frames[i];
dest.ip = src.pc;
dest.sp = src.sp;
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::AddressSpace(
const debug_ipc::AddressSpaceRequest& request,
std::function<void(const Err&, debug_ipc::AddressSpaceReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::AddressSpaceReply reply;
if (static_cast<pid_t>(request.process_koid) == minidump_->ProcessID()) {
for (const auto& region_object : minidump_->MemoryMap()) {
const auto& region = region_object->AsMinidumpMemoryInfo();
if (request.address > 0 &&
(request.address < region.BaseAddress ||
request.address >= region.BaseAddress + region.RegionSize)) {
continue;
}
auto& record = reply.map.emplace_back();
record.base = region.BaseAddress;
record.size = region.RegionSize;
}
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::JobFilter(
const debug_ipc::JobFilterRequest& request,
std::function<void(const Err&, debug_ipc::JobFilterReply)> cb) {
ErrNoLive(cb);
}
void MinidumpRemoteAPI::WriteMemory(
const debug_ipc::WriteMemoryRequest& request,
std::function<void(const Err&, debug_ipc::WriteMemoryReply)> cb) {
ErrNoLive(cb);
}
} // namespace zxdb