blob: d794ae28baf54086b3a6d65f833285fb3b559571 [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 "garnet/bin/zxdb/client/minidump_remote_api.h"
#include <cstring>
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/lib/debug_ipc/client_protocol.h"
#include "garnet/lib/debug_ipc/helper/message_loop.h"
#include "garnet/public/lib/fxl/strings/string_printf.h"
#include "third_party/crashpad/snapshot/memory_map_region_snapshot.h"
#include "third_party/crashpad/snapshot/minidump/process_snapshot_minidump.h"
#include "third_party/crashpad/util/misc/uuid.h"
namespace zxdb {
namespace {
Err ErrNoLive() {
return Err(ErrType::kNoConnection, "System is no longer live");
}
Err ErrNoImpl() { return Err("Feature not implemented for minidump"); }
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 ErrNoImpl(std::function<void(const Err&, ReplyType)> cb) {
debug_ipc::MessageLoop::Current()->PostTask(
FROM_HERE, [cb]() { cb(ErrNoImpl(), 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);
}
}
} // 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();
}
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()));
}
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) {
Succeed(cb, debug_ipc::HelloReply());
}
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.process_koid = minidump_->ProcessID();
notification.record.koid = thread->ThreadID();
notification.record.state = debug_ipc::ThreadRecord::State::kCoreDump;
}
Session* session = session_;
std::function<void(const Err&, debug_ipc::AttachReply)> new_cb =
[cb, notifications, session](const Err& e, debug_ipc::AttachReply a) {
cb(e, a);
for (const auto& notification : notifications) {
session->DispatchNotifyThread(
debug_ipc::MsgHeader::Type::kNotifyThreadStarting, 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;
}
for (const auto& minidump_mod : minidump_->Modules()) {
auto& mod = reply.modules.emplace_back();
mod.name = minidump_mod->Name();
mod.base = minidump_mod->Address();
crashpad::UUID uuid;
uint32_t unused_;
minidump_mod->UUIDAndAge(&uuid, &unused_);
mod.build_id = uuid.ToString();
}
Succeed(cb, reply);
}
void MinidumpRemoteAPI::SymbolTables(
const debug_ipc::SymbolTablesRequest& request,
std::function<void(const Err&, debug_ipc::SymbolTablesReply)> cb) {
if (!minidump_) {
ErrNoDump(cb);
return;
}
debug_ipc::SymbolTablesReply reply;
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.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) {
// TODO
ErrNoImpl(cb);
}
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 = nullptr;
for (const auto& item : minidump_->Threads()) {
if (item->ThreadID() == request.thread_koid) {
thread = item;
break;
}
}
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::ThreadStatus(
const debug_ipc::ThreadStatusRequest& request,
std::function<void(const Err&, debug_ipc::ThreadStatusReply)> cb) {
// TODO
ErrNoImpl(cb);
}
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