| // 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. |
| |
| #include "src/developer/debug/debug_agent/process_breakpoint.h" |
| |
| #include <inttypes.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <zircon/syscalls/exception.h> |
| |
| #include "src/developer/debug/debug_agent/breakpoint.h" |
| #include "src/developer/debug/debug_agent/debugged_thread.h" |
| #include "src/developer/debug/debug_agent/hardware_breakpoint.h" |
| #include "src/developer/debug/debug_agent/software_breakpoint.h" |
| #include "src/developer/debug/shared/logging/logging.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| namespace debug_agent { |
| |
| ProcessBreakpoint::ProcessBreakpoint(Breakpoint* breakpoint, DebuggedProcess* process, |
| uint64_t address) |
| : process_(process), address_(address), weak_factory_(this) { |
| breakpoints_.push_back(breakpoint); |
| } |
| |
| ProcessBreakpoint::~ProcessBreakpoint() = default; |
| |
| zx_status_t ProcessBreakpoint::Init() { return Update(); } |
| |
| fxl::WeakPtr<ProcessBreakpoint> ProcessBreakpoint::GetWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| zx_status_t ProcessBreakpoint::RegisterBreakpoint(Breakpoint* breakpoint) { |
| // Shouldn't get duplicates. |
| if (std::find(breakpoints_.begin(), breakpoints_.end(), breakpoint) != breakpoints_.end()) |
| return ZX_ERR_ALREADY_BOUND; |
| |
| // Should be the same type. |
| if (Type() != breakpoint->settings().type) |
| return ZX_ERR_INVALID_ARGS; |
| |
| breakpoints_.push_back(breakpoint); |
| // Check if we need to install/uninstall a breakpoint. |
| return Update(); |
| } |
| |
| bool ProcessBreakpoint::UnregisterBreakpoint(Breakpoint* breakpoint) { |
| DEBUG_LOG(Breakpoint) << "Unregistering breakpoint " << breakpoint->settings().id << " (" |
| << breakpoint->settings().name << ")."; |
| |
| auto found = std::find(breakpoints_.begin(), breakpoints_.end(), breakpoint); |
| if (found == breakpoints_.end()) { |
| FX_NOTREACHED(); // Should always be found. |
| } else { |
| breakpoints_.erase(found); |
| } |
| // Check if we need to install/uninstall a breakpoint. |
| Update(); |
| return !breakpoints_.empty(); |
| } |
| |
| bool ProcessBreakpoint::ShouldHitThread(zx_koid_t thread_koid) const { |
| for (const Breakpoint* bp : breakpoints_) { |
| if (bp->AppliesToThread(process_->koid(), thread_koid)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void ProcessBreakpoint::OnHit(debug_ipc::BreakpointType exception_type, |
| std::vector<debug_ipc::BreakpointStats>* hit_breakpoints) { |
| hit_breakpoints->clear(); |
| for (Breakpoint* breakpoint : breakpoints_) { |
| // Only care for breakpoints that match the exception type. |
| if (!Breakpoint::DoesExceptionApply(breakpoint->settings().type, exception_type)) |
| continue; |
| |
| breakpoint->OnHit(); |
| |
| // The breakpoint stats are for the client, don't tell it about our internal ones. |
| if (!breakpoint->is_debug_agent_internal()) |
| hit_breakpoints->push_back(breakpoint->stats()); |
| } |
| } |
| |
| void ProcessBreakpoint::BeginStepOver(DebuggedThread* thread) { |
| // Note that this request may get silently dropped in some edge cases (see EnqueueStepOver |
| // comment) so don't keep any state about this request. |
| process_->EnqueueStepOver(this, thread); |
| } |
| |
| } // namespace debug_agent |