blob: 18c366e09fb6dfd14a522c2217c1c6fa03a49135 [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/debug_agent/debug_agent.h"
#include <inttypes.h>
#include <zircon/syscalls/debug.h>
#include <zircon/syscalls/exception.h>
#include "garnet/bin/debug_agent/arch.h"
#include "garnet/bin/debug_agent/debugged_thread.h"
#include "garnet/bin/debug_agent/launcher.h"
#include "garnet/bin/debug_agent/object_util.h"
#include "garnet/bin/debug_agent/process_breakpoint.h"
#include "garnet/bin/debug_agent/process_info.h"
#include "garnet/bin/debug_agent/system_info.h"
#include "garnet/lib/debug_ipc/agent_protocol.h"
#include "garnet/lib/debug_ipc/helper/stream_buffer.h"
#include "garnet/lib/debug_ipc/message_reader.h"
#include "garnet/lib/debug_ipc/message_writer.h"
#include "garnet/public/lib/fxl/logging.h"
#include "garnet/public/lib/fxl/strings/string_printf.h"
namespace debug_agent {
DebugAgent::DebugAgent(debug_ipc::StreamBuffer* stream) : stream_(stream) {}
DebugAgent::~DebugAgent() {}
void DebugAgent::RemoveDebuggedProcess(zx_koid_t process_koid) {
auto found = procs_.find(process_koid);
if (found == procs_.end())
FXL_NOTREACHED();
else
procs_.erase(found);
}
void DebugAgent::OnHello(const debug_ipc::HelloRequest& request,
debug_ipc::HelloReply* reply) {
// Version and signature are default-initialized to their current values.
reply->arch = arch::GetArch();
}
void DebugAgent::OnLaunch(const debug_ipc::LaunchRequest& request,
debug_ipc::LaunchReply* reply) {
Launcher launcher;
reply->status = launcher.Setup(request.argv);
if (reply->status != ZX_OK)
return;
zx::process process = launcher.GetProcess();
zx_koid_t process_koid = KoidForObject(process);
DebuggedProcess* debugged_process =
AddDebuggedProcess(process_koid, std::move(process));
if (!debugged_process)
return;
reply->status = launcher.Start();
if (reply->status != ZX_OK) {
RemoveDebuggedProcess(process_koid);
return;
}
// Success, fill out the reply.
reply->process_koid = process_koid;
reply->process_name = NameForObject(process);
}
void DebugAgent::OnKill(const debug_ipc::KillRequest& request,
debug_ipc::KillReply* reply) {
auto debug_process = GetDebuggedProcess(request.process_koid);
if (!debug_process || !debug_process->process().is_valid()) {
reply->status = ZX_ERR_NOT_FOUND;
return;
}
debug_process->OnKill(request, reply);
}
void DebugAgent::OnAttach(std::vector<char> serialized) {
debug_ipc::MessageReader reader(std::move(serialized));
debug_ipc::AttachRequest request;
uint32_t transaction_id = 0;
if (!debug_ipc::ReadRequest(&reader, &request, &transaction_id)) {
fprintf(stderr, "Got bad debugger attach request, ignoring.\n");
return;
}
// Don't return early since we must send the reply at the bottom.
debug_ipc::AttachReply reply;
reply.status = ZX_ERR_NOT_FOUND;
zx::process process = GetProcessFromKoid(request.koid);
DebuggedProcess* new_process = nullptr;
if (process.is_valid()) {
reply.process_name = NameForObject(process);
new_process = AddDebuggedProcess(request.koid, std::move(process));
if (new_process)
reply.status = ZX_OK;
}
// Send the reply.
debug_ipc::MessageWriter writer;
debug_ipc::WriteReply(reply, transaction_id, &writer);
stream()->Write(writer.MessageComplete());
// For valid attaches, follow up with the current thread list.
if (new_process)
new_process->PopulateCurrentThreads();
}
void DebugAgent::OnDetach(const debug_ipc::DetachRequest& request,
debug_ipc::DetachReply* reply) {
auto debug_process = GetDebuggedProcess(request.process_koid);
if (debug_process->process().is_valid()) {
RemoveDebuggedProcess(request.process_koid);
reply->status = ZX_OK;
} else {
reply->status = ZX_ERR_NOT_FOUND;
}
}
void DebugAgent::OnPause(const debug_ipc::PauseRequest& request,
debug_ipc::PauseReply* reply) {
if (request.process_koid) {
// Single process.
DebuggedProcess* proc = GetDebuggedProcess(request.process_koid);
if (proc)
proc->OnPause(request);
} else {
// All debugged processes.
for (const auto& pair : procs_)
pair.second->OnPause(request);
}
}
void DebugAgent::OnResume(const debug_ipc::ResumeRequest& request,
debug_ipc::ResumeReply* reply) {
if (request.process_koid) {
// Single process.
DebuggedProcess* proc = GetDebuggedProcess(request.process_koid);
if (proc)
proc->OnResume(request);
} else {
// All debugged processes.
for (const auto& pair : procs_)
pair.second->OnResume(request);
}
}
void DebugAgent::OnModules(const debug_ipc::ModulesRequest& request,
debug_ipc::ModulesReply* reply) {
DebuggedProcess* proc = GetDebuggedProcess(request.process_koid);
if (proc)
GetModulesForProcess(proc->process(), &reply->modules);
}
void DebugAgent::OnProcessTree(const debug_ipc::ProcessTreeRequest& request,
debug_ipc::ProcessTreeReply* reply) {
GetProcessTree(&reply->root);
}
void DebugAgent::OnThreads(const debug_ipc::ThreadsRequest& request,
debug_ipc::ThreadsReply* reply) {
auto found = procs_.find(request.process_koid);
if (found == procs_.end())
return;
GetProcessThreads(found->second->process().get(), &reply->threads);
}
void DebugAgent::OnReadMemory(const debug_ipc::ReadMemoryRequest& request,
debug_ipc::ReadMemoryReply* reply) {
DebuggedProcess* proc = GetDebuggedProcess(request.process_koid);
if (proc)
proc->OnReadMemory(request, reply);
}
void DebugAgent::OnAddOrChangeBreakpoint(
const debug_ipc::AddOrChangeBreakpointRequest& request,
debug_ipc::AddOrChangeBreakpointReply* reply) {
uint32_t id = request.breakpoint.breakpoint_id;
auto found = breakpoints_.find(id);
if (found == breakpoints_.end()) {
found = breakpoints_
.emplace(std::piecewise_construct, std::forward_as_tuple(id),
std::forward_as_tuple(this))
.first;
}
reply->status = found->second.SetSettings(request.breakpoint);
}
void DebugAgent::OnRemoveBreakpoint(
const debug_ipc::RemoveBreakpointRequest& request,
debug_ipc::RemoveBreakpointReply* reply) {
auto found = breakpoints_.find(request.breakpoint_id);
if (found != breakpoints_.end())
breakpoints_.erase(found);
}
void DebugAgent::OnBacktrace(const debug_ipc::BacktraceRequest& request,
debug_ipc::BacktraceReply* reply) {
DebuggedThread* thread =
GetDebuggedThread(request.process_koid, request.thread_koid);
if (thread)
thread->GetBacktrace(&reply->frames);
}
zx_status_t DebugAgent::RegisterBreakpoint(Breakpoint* bp,
zx_koid_t process_koid,
uint64_t address) {
DebuggedProcess* proc = GetDebuggedProcess(process_koid);
if (proc)
return proc->RegisterBreakpoint(bp, address);
// The process might legitimately be not found if there was a race between
// the process terminating and a breakpoint add/change.
return ZX_ERR_NOT_FOUND;
}
void DebugAgent::UnregisterBreakpoint(Breakpoint* bp, zx_koid_t process_koid,
uint64_t address) {
// The process might legitimately be not found if it was terminated.
DebuggedProcess* proc = GetDebuggedProcess(process_koid);
if (proc)
proc->UnregisterBreakpoint(bp, address);
}
void DebugAgent::OnAddressSpace(const debug_ipc::AddressSpaceRequest& request,
debug_ipc::AddressSpaceReply* reply) {
DebuggedProcess* proc = GetDebuggedProcess(request.process_koid);
if (proc)
proc->OnAddressSpace(request, reply);
}
DebuggedProcess* DebugAgent::GetDebuggedProcess(zx_koid_t koid) {
auto found = procs_.find(koid);
if (found == procs_.end())
return nullptr;
return found->second.get();
}
DebuggedThread* DebugAgent::GetDebuggedThread(zx_koid_t process_koid,
zx_koid_t thread_koid) {
DebuggedProcess* process = GetDebuggedProcess(process_koid);
if (!process)
return nullptr;
return process->GetThread(thread_koid);
}
DebuggedProcess* DebugAgent::AddDebuggedProcess(zx_koid_t process_koid,
zx::process zx_proc) {
auto proc =
std::make_unique<DebuggedProcess>(this, process_koid, std::move(zx_proc));
if (!proc->Init())
return nullptr;
DebuggedProcess* result = proc.get();
procs_[process_koid] = std::move(proc);
return result;
}
} // namespace debug_agent