| // 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. |
| |
| #ifndef SRC_DEVELOPER_DEBUG_DEBUG_AGENT_SOFTWARE_BREAKPOINT_H_ |
| #define SRC_DEVELOPER_DEBUG_DEBUG_AGENT_SOFTWARE_BREAKPOINT_H_ |
| |
| #include <zircon/status.h> |
| |
| #include "src/developer/debug/debug_agent/arch.h" |
| #include "src/developer/debug/debug_agent/process_breakpoint.h" |
| #include "src/developer/debug/debug_agent/suspend_handle.h" |
| #include "src/developer/debug/ipc/protocol.h" |
| |
| namespace debug_agent { |
| |
| class ProcessMemoryAccessor; |
| |
| class SoftwareBreakpoint : public ProcessBreakpoint { |
| public: |
| SoftwareBreakpoint(Breakpoint* breakpoint, DebuggedProcess* process, uint64_t address); |
| virtual ~SoftwareBreakpoint(); |
| |
| debug_ipc::BreakpointType Type() const override { return debug_ipc::BreakpointType::kSoftware; } |
| |
| // Software breakpoint is either installed for all threads or no one. |
| bool Installed(zx_koid_t thread_koid) const override { return installed_; } |
| |
| // virtual picture of memory is needed, this function will replace the replacement from this |
| // breakpoint if it appears in the given block. Otherwise does nothing. |
| void FixupMemoryBlock(debug_ipc::MemoryBlock* block); |
| |
| // Public ProcessBreakpoint overrides. See ProcessBreakpoint for more details. |
| void EndStepOver(DebuggedThread* thread) override; |
| void ExecuteStepOver(DebuggedThread* thread) override; |
| void StepOverCleanup(DebuggedThread* thread) override; |
| |
| const DebuggedThread* currently_stepping_over_thread() const { |
| return currently_stepping_over_thread_.get(); |
| } |
| |
| // Returns a sorted list of the koids associated with a currently held suspend token. |
| // If a thread has more than one suspend token, it wil appear twice. |
| // |
| // Exposed mostly for testing purposes (see process_breakpoint_unittest.cc). |
| std::vector<zx_koid_t> CurrentlySuspendedThreads() const; |
| |
| private: |
| // ProcessBreakpoint overrides. |
| debug::Status Update() override; |
| |
| // A software breakpoint gets uninstalled for all the threads. |
| debug::Status Uninstall(DebuggedThread* thread) override { return Uninstall(); } |
| debug::Status Uninstall() override; |
| |
| debug::Status Install(); |
| |
| // As stepping over are queued, only one thread should be left running at a time. This makes the |
| // breakpoint get a suspend token for each other thread within the system. |
| void SuspendAllOtherThreads(zx_koid_t stepping_over_koid); |
| |
| // Set to true when the instruction has been replaced. |
| bool installed_ = false; |
| |
| // Previous memory contents before being replaced with the break instruction. |
| arch::BreakInstructionType previous_data_ = 0; |
| |
| // Tracks the threads currently single-stepping over this breakpoint. |
| // There can be only one thread stepping over, as they're serialized by the process so that only |
| // one thread is stepping at a time. |
| fxl::WeakPtr<DebuggedThread> currently_stepping_over_thread_; |
| |
| // A step is executed by putting back the original instruction, stepping the thread, and then |
| // re-inserting the breakpoint instruction. The breakpoint instruction can't be put back until |
| // there are no more threads in this map. |
| // |
| // It is a multimap because if two threads are queued on the same breakpoint (they both hit it at |
| // the same time), the breakpoint will get suspend tokens for all the threads (except the |
| // corresponding exception one) multiple times. If there is only one suspend token per koid, the |
| // breakpoint will uncorrectly resume the thread that just stepped over when the other would |
| // step over too, which is incorrect. We need the ability to have multiple tokens associated to |
| // a thread so that the interim between executing the second step over the same breakpoint can |
| // coincide with waiting for the resources of the first step over to be freed. |
| // |
| // See the implementation of |StepOverCleanup| for more details. |
| std::multimap<zx_koid_t, std::unique_ptr<SuspendHandle>> suspend_tokens_; |
| }; |
| |
| } // namespace debug_agent |
| |
| #endif // SRC_DEVELOPER_DEBUG_DEBUG_AGENT_SOFTWARE_BREAKPOINT_H_ |