blob: ae278cb47b80b0b4964ec4743fba7cb5e8e454a4 [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.
#ifndef GARNET_LIB_INFERIOR_CONTROL_PROCESS_H_
#define GARNET_LIB_INFERIOR_CONTROL_PROCESS_H_
#include <memory>
#include <string>
#include <unordered_map>
#include <zircon/syscalls/exception.h>
#include <zircon/types.h>
#include "garnet/lib/debugger_utils/dso_list.h"
#include "garnet/lib/debugger_utils/util.h"
#include "garnet/lib/process/process_builder.h"
#include "lib/fxl/macros.h"
#include "breakpoint.h"
#include "exception_port.h"
#include "memory_process.h"
#include "thread.h"
namespace inferior_control {
class Server;
class Thread;
// Represents an inferior process that we're attached to.
class Process final {
public:
public:
enum class State { kNew, kStarting, kRunning, kGone };
// Delegate interface for listening to Process life-time events.
class Delegate {
public:
virtual ~Delegate() = default;
// Called when a new thread that is part of this process has been started.
// This is indicated by ZX_EXCP_THREAD_STARTING.
virtual void OnThreadStarting(Process* process, Thread* thread,
const zx_exception_context_t& context) = 0;
// Called when |thread| has exited (ZX_EXCP_THREAD_EXITING).
virtual void OnThreadExiting(Process* process, Thread* thread,
const zx_exception_context_t& context) = 0;
// Called when |process| has exited.
virtual void OnProcessExit(Process* process) = 0;
// Called when the kernel reports an architectural exception.
virtual void OnArchitecturalException(
Process* process, Thread* thread, const zx_excp_type_t type,
const zx_exception_context_t& context) = 0;
// Called when |thread| has gets a synthetic exception
// (e.g., ZX_EXCP_POLICY_ERROR) that is akin to an architectural
// exception: the program got an error and by default crashes.
virtual void OnSyntheticException(
Process* process, Thread* thread, const zx_excp_type_t type,
const zx_exception_context_t& context) = 0;
};
explicit Process(Server* server, Delegate* delegate_,
std::shared_ptr<component::Services> services);
~Process();
std::string GetName() const;
// Note: This includes the program in |argv[0]|.
const debugger_utils::Argv& argv() { return argv_; }
void set_argv(const debugger_utils::Argv& argv) { argv_ = argv; }
// Add extra handles to the process.
//
// Must be called before |Initialize|.
void AddStartupHandle(fuchsia::process::HandleInfo handle);
// Returns the current state of this process.
State state() const { return state_; }
// Change the state to |new_state|.
void set_state(State new_state);
static const char* StateName(Process::State state);
// Creates and initializes the inferior process, via ProcessBuilder, but does
// not start it. set_argv() must have already been called.
// This also "attaches" to the inferior: A debug-capable process handle is
// obtained and the debugger exception port is bound to.
// Returns false if there is an error.
// Do not call this if the process is currently live (state is kStarting or
// kRunning).
bool Initialize();
// Attach to running program with pid |pid|.
// A debug-capable process handle is obtained and the debugger exception
// port is bound to.
// Returns false if there is an error.
// Do not call this if the process is currently live (state is kStarting or
// kRunning).
bool Attach(zx_koid_t pid);
// Detach from an attached process, and return to pre-attached state.
// This includes unbinding from the exception port and closing the process
// handle. To keep things simple and clean "detach" means "release all
// connections with the inferior". After detaching we should have absolutely
// no effect on the inferior, including not preserving the lifetime of the
// kernel process instance because we still have a handle of the process.
// Returns true on success, or false if already detached.
bool Detach();
// Starts running the process. Returns false in case of an error.
// Initialize() MUST be called successfully before calling Start().
bool Start();
// Terminate the process.
bool Kill();
// Returns true if the process is running or has been running.
bool IsLive() const;
// Returns true if the process is currently attached.
bool IsAttached() const;
// Returns the process handle. This handle is owned and managed by this
// Process instance, thus the caller should not close the handle.
zx_handle_t handle() const { return handle_; }
// Returns the process ID.
zx_koid_t id() const { return id_; }
Server* server() { return server_; }
// Returns a mutable handle to the set of breakpoints managed by this process.
ProcessBreakpointSet* breakpoints() { return &breakpoints_; }
// Returns the base load address of the dynamic linker.
zx_vaddr_t base_address() const { return base_address_; }
// Returns the entry point of the dynamic linker.
zx_vaddr_t entry_address() const { return entry_address_; }
// Returns the thread with the thread ID |thread_id| that's owned by this
// process. Returns nullptr if no such thread exists. The returned pointer is
// owned and managed by this Process instance.
Thread* FindThreadById(zx_koid_t thread_id);
// Returns an arbitrary thread that is owned by this process. This picks the
// first thread that is returned from zx_object_get_info for the
// ZX_INFO_PROCESS_THREADS topic. This will refresh all threads.
// TODO(dje): ISTR GNU gdbserver being more random to avoid starving threads.
Thread* PickOneThread();
// If the thread map might be stale, refresh it.
void EnsureThreadMapFresh();
// Refreshes the complete Thread list for this process. Returns false if an
// error is returned from a syscall.
bool RefreshAllThreads();
// Iterates through all cached threads and invokes |callback| for each of
// them. |callback| is guaranteed to get called only before ForEachThread()
// returns, so it is safe to bind local variables to |callback|.
using ThreadCallback = fit::function<void(Thread*)>;
void ForEachThread(const ThreadCallback& callback);
// Same as ForEachThread except ignores State::Gone threads.
void ForEachLiveThread(const ThreadCallback& callback);
// Reads the block of memory of length |length| bytes starting at address
// |address| into |out_buffer|. |out_buffer| must be at least as large as
// |length|. Returns true on success or false on failure.
bool ReadMemory(uintptr_t address, void* out_buffer, size_t length);
// Writes the block of memory of length |length| bytes from |data| to the
// memory address |address| of this process. Returns true on success or false
// on failure.
bool WriteMemory(uintptr_t address, const void* data, size_t length);
// Fetch the process's exit code.
int ExitCode();
bool attached_running() const { return attached_running_; }
// Build list of loaded dsos.
// This must be called at a point where it is safe to read the list.
// If |check_ldso_bkpt| is true then verify |thread| is stopped at the
// dynamic linker breakpoint. If not then skip trying. Otherwise |thread|
// must be nullptr.
// TODO(dje): Maybe just pass |thread|, later.
// TODO(dje): For the rsp server this is only called once, after the main
// executable has been loaded. Therefore this list will not contain
// subsequently loaded dsos.
void TryBuildLoadedDsosList(Thread* thread, bool check_ldso_bkpt);
// Return true if dsos, including the main executable, have been loaded
// into the inferior.
bool DsosLoaded() { return dsos_ != nullptr; }
// Return list of loaded dsos.
// Returns nullptr if none loaded yet or loading failed.
// TODO(dje): constness wip
debugger_utils::dsoinfo_t* GetDsos() const { return dsos_; }
// Return the DSO for |pc| or nullptr if none.
// TODO(dje): Result is not const for debug file lookup support.
debugger_utils::dsoinfo_t* LookupDso(zx_vaddr_t pc) const;
// Return the entry for the main executable from the dsos list.
// Returns nullptr if not present (could happen if inferior data structure
// has been clobbered).
const debugger_utils::dsoinfo_t* GetExecDso();
private:
// The exception handler invoked by ExceptionPort.
void OnExceptionOrSignal(const zx_port_packet_t& packet,
const zx_exception_context_t& context);
// Debug handle mgmt.
bool AllocDebugHandle(process::ProcessBuilder* builder);
bool AllocDebugHandle(zx_koid_t pid);
void CloseDebugHandle();
// Exception port mgmt.
bool BindExceptionPort();
void UnbindExceptionPort();
// Detach from the inferior, but don't clear out any data structures.
void RawDetach();
// Release all resources held by the process.
// Called after all other processing of a process exit has been done.
void Clear();
// The server that owns us.
Server* server_; // weak
// The delegate that we send life-cycle notifications to.
Delegate* delegate_; // weak
// Handle containing services available to this process.
std::shared_ptr<component::Services> services_;
// The argv that this process was initialized with.
debugger_utils::Argv argv_;
// Extra handles to pass to process during creation.
std::vector<fuchsia::process::HandleInfo> extra_handles_;
// The process::ProcessBuilder instance used to bootstrap and run the process.
std::unique_ptr<process::ProcessBuilder> builder_;
// The debug-capable handle that we use to invoke zx_debug_* syscalls.
zx_handle_t handle_ = ZX_HANDLE_INVALID;
// The current state of this process.
State state_ = State::kNew;
// The process ID (also the kernel object ID).
zx_koid_t id_ = ZX_KOID_INVALID;
// The base load address of the dynamic linker.
zx_vaddr_t base_address_ = 0;
// The entry point of the dynamic linker.
zx_vaddr_t entry_address_ = 0;
// The key we receive after binding an exception port.
ExceptionPort::Key eport_key_ = 0;
// True if we attached, or will attach, to a running program.
// Otherwise we're launching a program from scratch.
bool attached_running_ = false;
// The API to access memory.
std::shared_ptr<debugger_utils::ByteBlock> memory_;
// The collection of breakpoints that belong to this process.
ProcessBreakpointSet breakpoints_;
// The threads owned by this process. This is map is populated lazily when
// threads are requested through FindThreadById(). It can also be repopulated
// from scratch, e.g., when attaching to an already running program.
using ThreadMap = std::unordered_map<zx_koid_t, std::unique_ptr<Thread>>;
ThreadMap threads_;
// If true then |threads_| needs to be recalculated from scratch.
bool thread_map_stale_ = false;
// List of dsos loaded.
// NULL if none have been loaded yet (including main executable).
// TODO(dje): Code taking from crashlogger, to be rewritten.
// TODO(dje): Doesn't include dsos loaded later.
debugger_utils::dsoinfo_t* dsos_ = nullptr;
// If true then building the dso list failed, don't try again.
bool dsos_build_failed_ = false;
FXL_DISALLOW_COPY_AND_ASSIGN(Process);
};
} // namespace inferior_control
#endif // GARNET_LIB_INFERIOR_CONTROL_PROCESS_H_