| // 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 <lib/zx/thread.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" | |
| namespace debug_agent { | |
| // 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 zx::thread& GetNativeHandle() const = 0; | |
| virtual zx::thread& 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_ |