| // 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_BREAKPOINT_H_ | |
| #define SRC_DEVELOPER_DEBUG_DEBUG_AGENT_BREAKPOINT_H_ | |
| #include <zircon/types.h> | |
| #include <set> | |
| #include <string> | |
| #include "src/developer/debug/ipc/records.h" | |
| #include "src/lib/fxl/macros.h" | |
| namespace debug_agent { | |
| // A single breakpoint may apply to many processes and many addresses (even in | |
| // the same process). These instances are called ProcessBreakpoints. | |
| // | |
| // Multiple Breakpoints can also correspond to the same ProcessBreakpoint if | |
| // they have the same process/address. | |
| class Breakpoint { | |
| public: | |
| // Normally an exception applies to a breakpoint only if both types match. But in the case of | |
| // watchpoints, they can be triggered either by a read or a write exception, so a same address | |
| // could apply or not depending on the type of the breakpoint (eg. a read exception would apply | |
| // to both read and read/write breakpoints). | |
| // | |
| // All checks to see if an exception matches a breakpoint should be done by this function and not | |
| // directly checking |type()|. | |
| static bool DoesExceptionApply(debug_ipc::BreakpointType exception, | |
| debug_ipc::BreakpointType bp_type); | |
| enum class HitResult { | |
| // Breakpoint was hit and the hit count was incremented. | |
| kHit, | |
| // One-shot breakpoint hit. The caller should delete this breakpoint | |
| // when it sees this result. | |
| kOneShotHit, | |
| // This will need to be expanded to include "kContinue" to indicate that | |
| // this doesn't count as hitting the breakpoint (for when we implement | |
| // "break on hit count == 5" or "multiple of 7"). | |
| }; | |
| // The process delegate should outlive the Breakpoint object. It allows | |
| // Breakpoint dependencies to be mocked for testing. | |
| class ProcessDelegate { | |
| public: | |
| // Called to register a new ProcessBreakpoint with the appropriate | |
| // location. If this fails, the breakpoint has not been set. | |
| virtual zx_status_t RegisterBreakpoint(Breakpoint* bp, zx_koid_t process_koid, | |
| uint64_t address); | |
| // Called When the breakpoint no longer applies to this location. | |
| virtual void UnregisterBreakpoint(Breakpoint* bp, zx_koid_t process_koid, uint64_t address); | |
| virtual zx_status_t RegisterWatchpoint(Breakpoint* bp, zx_koid_t process_koid, | |
| const debug_ipc::AddressRange& range); | |
| virtual void UnregisterWatchpoint(Breakpoint* bp, zx_koid_t process_koid, | |
| const debug_ipc::AddressRange& range); | |
| }; | |
| // Some breakpoints are internally generated by the debug_agent. These are different than the | |
| // internal breakpoints set by the zxdb frontend which we see as nrmal breakpoints. Our internal | |
| // breakpoints are never sent to the client. | |
| explicit Breakpoint(ProcessDelegate* process_delegate, bool is_debug_agent_internal = false); | |
| ~Breakpoint(); | |
| bool is_debug_agent_internal() const { return is_debug_agent_internal_; } | |
| const debug_ipc::BreakpointStats& stats() const { return stats_; } | |
| // Calling SetSettings() will update the settings and install/move the breakpoint as required. | |
| // The one taking an address is for internal process-wide software breakpoints that have no ID. | |
| zx_status_t SetSettings(const debug_ipc::BreakpointSettings& settings); | |
| zx_status_t SetSettings(std::string name, zx_koid_t process_koid, uint64_t address); | |
| const debug_ipc::BreakpointSettings& settings() const { return settings_; } | |
| // A breakpoint can be set to apply to a specific set of threads. A thread | |
| // hitting an exception needs to query whether it should apply to it or not. | |
| bool AppliesToThread(zx_koid_t process_koid, zx_koid_t thread_koid) const; | |
| // Notification that this breakpoint was just hit. | |
| HitResult OnHit(); | |
| private: | |
| zx_status_t SetBreakpointLocations(const debug_ipc::BreakpointSettings&); | |
| zx_status_t SetWatchpointLocations(const debug_ipc::BreakpointSettings&); | |
| // A process koid + address identifies one unique location. | |
| using LocationPair = std::pair<zx_koid_t, uint64_t>; | |
| using WatchpointLocationPair = std::pair<zx_koid_t, debug_ipc::AddressRange>; | |
| struct WatchpointLocationPairCompare { | |
| bool operator()(const WatchpointLocationPair&, const WatchpointLocationPair&) const; | |
| }; | |
| ProcessDelegate* process_delegate_; // Non-owning. | |
| bool is_debug_agent_internal_; | |
| debug_ipc::BreakpointSettings settings_; | |
| debug_ipc::BreakpointStats stats_; | |
| std::set<LocationPair> locations_; | |
| std::set<WatchpointLocationPair, WatchpointLocationPairCompare> watchpoint_locations_; | |
| FXL_DISALLOW_COPY_AND_ASSIGN(Breakpoint); | |
| }; | |
| } // namespace debug_agent | |
| #endif // SRC_DEVELOPER_DEBUG_DEBUG_AGENT_BREAKPOINT_H_ |