blob: 2ab591ae1f637d7ab58f2c7212588df7e48eff6d [file] [log] [blame]
// Copyright 2020 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 SRC_DEVELOPER_DEBUG_DEBUG_AGENT_THREAD_HANDLE_H_
#define SRC_DEVELOPER_DEBUG_DEBUG_AGENT_THREAD_HANDLE_H_
#include <optional>
#include <vector>
#include "src/developer/debug/debug_agent/debug_registers.h"
#include "src/developer/debug/debug_agent/general_registers.h"
#include "src/developer/debug/debug_agent/suspend_handle.h"
#include "src/developer/debug/debug_agent/time.h"
#include "src/developer/debug/debug_agent/watchpoint_info.h"
#include "src/developer/debug/ipc/records.h"
#include "src/developer/debug/shared/register_info.h"
#include "src/lib/fxl/memory/ref_counted.h"
#if defined(__Fuchsia__)
#include <lib/zx/thread.h>
#elif defined(__linux__)
#include "src/developer/debug/debug_agent/linux_task.h"
#endif
namespace debug_agent {
#if defined(__Fuchsia__)
using NativeThreadHandle = zx::thread;
#elif defined(__linux__)
using NativeThreadHandle = fxl::RefPtr<LinuxTask>;
#else
#error Unknown platform
#endif
// An abstract wrapper around an OS thread primitive. This abstraction is to allow mocking.
class ThreadHandle {
public:
struct State {
explicit State(debug_ipc::ThreadRecord::State s = debug_ipc::ThreadRecord::State::kRunning,
debug_ipc::ThreadRecord::BlockedReason br =
debug_ipc::ThreadRecord::BlockedReason::kNotBlocked)
: state(s), blocked_reason(br) {}
// Creates a blocked state with the given reason.
explicit State(debug_ipc::ThreadRecord::BlockedReason br)
: state(debug_ipc::ThreadRecord::State::kBlocked), blocked_reason(br) {}
// This state is common to check for and requires a combination of things to check.
bool is_blocked_on_exception() const {
return state == debug_ipc::ThreadRecord::State::kBlocked &&
blocked_reason == debug_ipc::ThreadRecord::BlockedReason::kException;
}
debug_ipc::ThreadRecord::State state;
debug_ipc::ThreadRecord::BlockedReason blocked_reason;
};
virtual ~ThreadHandle() = default;
// Access to the underlying native thread object. This is for porting purposes, ideally this
// object would encapsulate all details about the thread for testing purposes and this getter
// would be removed. In testing situations, the returned value may be an empty object,
// TODO(brettw) Remove this.
virtual const NativeThreadHandle& GetNativeHandle() const = 0;
virtual NativeThreadHandle& GetNativeHandle() = 0;
virtual zx_koid_t GetKoid() const = 0;
virtual std::string GetName() const = 0;
virtual State GetState() const = 0;
// Fills in everything but the stack into the returned thread record. Since the process koid
// isn't known by the thread handle, it is passed in.
virtual debug_ipc::ThreadRecord GetThreadRecord(zx_koid_t process_koid) const = 0;
// ExceptionRecord.valid will be false on failure.
virtual debug_ipc::ExceptionRecord GetExceptionRecord() const = 0;
// Asynchronously suspends the thread. The thread will remain suspended as long as any suspend
// handle is alive. See also WaitForSuspension().
virtual std::unique_ptr<SuspendHandle> Suspend() = 0;
// Waits for a previous suspend call to take effect. Does nothing if the thread is already
// suspended. Returns true if we could find a valid suspension condition (either suspended or on
// an exception). False if timeout or error.
virtual bool WaitForSuspension(TickTimePoint deadline) const = 0;
// Registers -------------------------------------------------------------------------------------
// Reads and writes the general thread registers.
virtual std::optional<GeneralRegisters> GetGeneralRegisters() const = 0;
virtual void SetGeneralRegisters(const GeneralRegisters& regs) = 0;
// Reads and writes the debug thread registers.
virtual std::optional<DebugRegisters> GetDebugRegisters() const = 0;
virtual bool SetDebugRegisters(const DebugRegisters& regs) = 0;
// Puts the thread in or out of single-step mode.
virtual void SetSingleStep(bool single_step) = 0;
// Returns the current values of the given register categories.
virtual std::vector<debug::RegisterValue> ReadRegisters(
const std::vector<debug::RegisterCategory>& cats_to_get) const = 0;
// Returns the new value of the registers that may have changed which is the result of reading
// them after the write. This helps the client stay in sync. The may include other registers that
// weren't updated.
virtual std::vector<debug::RegisterValue> WriteRegisters(
const std::vector<debug::RegisterValue>& regs) = 0;
// Hardware breakpoints --------------------------------------------------------------------------
// Installs or uninstalls hardware breakpoints.
virtual bool InstallHWBreakpoint(uint64_t address) = 0;
virtual bool UninstallHWBreakpoint(uint64_t address) = 0;
// NOTE: AddressRange is what is used to differentiate watchpoints, not |type|.
virtual std::optional<WatchpointInfo> InstallWatchpoint(debug_ipc::BreakpointType type,
const debug::AddressRange& range) = 0;
virtual bool UninstallWatchpoint(const debug::AddressRange& range) = 0;
};
} // namespace debug_agent
#endif // SRC_DEVELOPER_DEBUG_DEBUG_AGENT_THREAD_HANDLE_H_