blob: 89be6f95b9988a791a837e71f35c3ac2c8b0abf3 [file] [log] [blame]
// Copyright 2016 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 <cstdlib>
#include <limits>
#include <string>
#include <vector>
#include <lib/async/cpp/task.h>
#include <lib/fit/function.h>
#include <src/lib/fxl/logging.h>
#include <src/lib/fxl/strings/string_printf.h>
#include "garnet/lib/debugger_utils/breakpoints.h"
#include "garnet/lib/debugger_utils/jobs.h"
#include "garnet/lib/debugger_utils/threads.h"
#include "garnet/lib/debugger_utils/util.h"
#include "server.h"
namespace inferior_control {
Server::Server(zx::job job_for_search, zx::job job_for_launch,
std::shared_ptr<sys::ServiceDirectory> services)
: Delegate(this),
job_for_search_(std::move(job_for_search)),
job_for_launch_(std::move(job_for_launch)),
services_(std::move(services)),
message_loop_(&kAsyncLoopConfigNoAttachToThread),
exception_port_(message_loop_.dispatcher(),
fit::bind_member(this, &Server::OnProcessException),
fit::bind_member(this, &Server::OnProcessSignal)),
run_status_(true) {}
Server::~Server() {}
bool Server::CreateProcessViaBuilder(
const std::string& path, const debugger_utils::Argv& argv,
std::unique_ptr<process::ProcessBuilder>* out_builder) {
FXL_DCHECK(!argv.empty());
zx_status_t status = debugger_utils::CreateProcessBuilder(
job_for_launch_, argv[0], argv, services_, out_builder);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Unable to initialize process builder: "
<< debugger_utils::ZxErrorString(status);
return false;
}
return true;
}
zx::process Server::FindProcess(zx_koid_t pid) {
if (!job_for_search_) {
FXL_LOG(ERROR) << "No job for searching processes";
return zx::process{};
}
zx::process process = debugger_utils::FindProcess(job_for_search_, pid);
if (!process) {
FXL_LOG(ERROR) << "Cannot find process " << pid;
}
return process;
}
void Server::SetCurrentThread(Thread* thread) {
if (!thread)
current_thread_.reset();
else
current_thread_ = thread->AsWeakPtr();
}
void Server::QuitMessageLoop(bool status) {
FXL_VLOG(2) << "QuitMessageLoop: status: " << status;
run_status_ = status;
message_loop_.Quit();
}
void Server::PostQuitMessageLoop(bool status) {
async::PostTask(message_loop_.dispatcher(), [this, status] {
QuitMessageLoop(status);
});
}
void Server::WaitAsync(Thread* thread) {
exception_port_.WaitAsync(thread);
}
void Server::OnProcessException(const zx_port_packet_t& packet) {
// At the moment we only support one process.
Process* process = current_process();
FXL_DCHECK(process);
FXL_DCHECK(ZX_PKT_IS_EXCEPTION(packet.type));
zx_excp_type_t type = static_cast<zx_excp_type_t>(packet.type);
zx_koid_t tid = packet.exception.tid;
Thread* thread = nullptr;
if (tid != ZX_KOID_INVALID) {
thread = process->FindThreadById(tid);
}
// If |thread| is nullptr then the thread must have just terminated,
// and there's nothing to do. The process itself could also have terminated.
if (thread == nullptr) {
// Alas there's no robust test to verify it just terminated,
// we just have to assume it.
FXL_LOG(WARNING) << "Thread " << tid << " not found, terminated";
return;
}
// At this point the thread is either an existing thread or a new thread
// which has been fully registered in our database.
// Manage loading of dso info.
// At present this is only done at startup. TODO(dje): dlopen.
// This is done by setting ZX_PROCESS_DEBUG_ADDR_BREAK_ON_SET which causes
// a s/w breakpoint instruction to be executed after all dsos are loaded.
// TODO(dje): Handle case of hitting a breakpoint before then (highly
// unlikely, but technically possible).
if (type == ZX_EXCP_SW_BREAKPOINT) {
if (process->CheckDsosList(thread)) {
zx_status_t status =
debugger_utils::ResumeAfterSoftwareBreakpointInstruction(
thread->handle(), exception_port_.handle());
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Unable to resume thread " << thread->GetName()
<< ", status: "
<< debugger_utils::ZxErrorString(status);
}
// This is a breakpoint we introduced. No point in passing it on to
// other handlers. If resumption fails there's not much we can do.
return;
}
}
zx_exception_report_t report;
zx_status_t status = thread->GetExceptionReport(&report);
if (status == ZX_ERR_BAD_STATE) {
// Nothing more to do, let process cleanup finish things up.
return;
}
const zx_exception_context_t& context = report.context;
Delegate* delegate = process->delegate();
zx_handle_t eport = exception_port_.handle();
// First update our internal state for the thread.
thread->OnException(type, context);
// |type| could either map to an architectural exception or Zircon-defined
// synthetic exceptions.
if (ZX_EXCP_IS_ARCH(type)) {
delegate->OnArchitecturalException(process, thread, eport, type, context);
return;
}
// Must be a synthetic exception.
switch (type) {
case ZX_EXCP_THREAD_STARTING:
delegate->OnThreadStarting(process, thread, eport, context);
break;
case ZX_EXCP_THREAD_EXITING:
delegate->OnThreadExiting(process, thread, eport, context);
break;
case ZX_EXCP_POLICY_ERROR:
delegate->OnSyntheticException(process, thread, eport, type, context);
break;
default:
FXL_LOG(ERROR) << "Ignoring unrecognized synthetic exception for thread "
<< tid << ": " << type;
break;
}
}
void Server::OnProcessSignal(const zx_port_packet_t& packet) {
// At the moment we only support one process.
Process* process = current_process();
FXL_DCHECK(process);
FXL_DCHECK(packet.type == ZX_PKT_TYPE_SIGNAL_ONE);
uint64_t key = packet.key;
FXL_VLOG(4) << "Received ZX_PKT_TYPE_SIGNAL_ONE, observed 0x" << std::hex
<< packet.signal.observed << ", key " << std::dec << key;
// Process exit is sent as a regular signal.
if (key == process->id()) {
if (packet.signal.observed & ZX_PROCESS_TERMINATED) {
process->OnTermination();
// No point in installing another async-wait, process is dead.
}
}
Thread* thread = process->FindThreadById(key);
if (thread == nullptr) {
// If the process is gone this is expected.
if (process->state() != Process::State::kGone) {
FXL_LOG(WARNING) << "Unexpected signal, key " << key;
}
return;
}
thread->OnSignal(packet.signal.observed);
// Async-waits must be continually re-registered.
if (!(packet.signal.observed & ZX_THREAD_TERMINATED)) {
WaitAsync(thread);
}
}
ServerWithIO::ServerWithIO(zx::job job_for_search, zx::job job_for_launch,
std::shared_ptr<sys::ServiceDirectory> services)
: Server(std::move(job_for_search), std::move(job_for_launch),
std::move(services)),
client_sock_(-1) {}
ServerWithIO::~ServerWithIO() {
// This will invoke the IOLoop destructor which will clean up and join the
// I/O threads. This is done now because |message_loop_| and |client_sock_|
// must outlive |io_loop_|. The former is handled by virtue of being in the
// baseclass. The latter is handled here.
io_loop_.reset();
}
} // namespace inferior_control