blob: 1b7a479c43be9ed27227d7310cf8edc65e0dad29 [file] [log] [blame]
// 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/debug_agent.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;
debug::Status ProcessBreakpoint::Init() { return Update(); }
fxl::WeakPtr<ProcessBreakpoint> ProcessBreakpoint::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
debug::Status ProcessBreakpoint::RegisterBreakpoint(Breakpoint* breakpoint) {
// Shouldn't get duplicates.
if (std::find(breakpoints_.begin(), breakpoints_.end(), breakpoint) != breakpoints_.end())
return debug::Status("Breakpoint already registered");
// Should be the same type.
if (Type() != breakpoint->settings().type)
return debug::Status("Breakpoint should be the same type");
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(DebuggedThread* hitting_thread,
debug_ipc::BreakpointType exception_type,
std::vector<debug_ipc::BreakpointStats>& hit_breakpoints,
std::vector<debug_ipc::ThreadRecord>& other_affected_threads) {
// This will be filled in with the largest scope to stop.
debug_ipc::Stop max_stop = debug_ipc::Stop::kNone;
DebugAgent* agent = process_->debug_agent();
// How much stack to capture for the suspended threads.
constexpr auto kSuspendedStackAmount = debug_ipc::ThreadRecord::StackAmount::kMinimal;
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.
hit_breakpoints.push_back(breakpoint->stats());
if (static_cast<uint32_t>(breakpoint->settings().stop) > static_cast<uint32_t>(max_stop))
max_stop = breakpoint->settings().stop;
}
// Apply the maximal stop mode.
switch (max_stop) {
case debug_ipc::Stop::kNone: {
// In this case the client will be in charge of resuming the thread because it may need to do
// stuff like printing a message.
break;
}
case debug_ipc::Stop::kThread: {
// The thread is already stopped, nothing to do.
break;
}
case debug_ipc::Stop::kProcess: {
// Suspend each thread in the process except the one that just hit the exception (leave it
// suspended in the exception).
std::vector<debug_ipc::ProcessThreadId> suspended_ids =
process_->ClientSuspendAllThreads(hitting_thread->koid());
// Save the record for each suspended thread.
for (const debug_ipc::ProcessThreadId& id : suspended_ids) {
if (DebuggedThread* thread = process_->GetThread(id.thread))
other_affected_threads.push_back(thread->GetThreadRecord(kSuspendedStackAmount));
}
break;
}
case debug_ipc::Stop::kAll: {
// Suspend each thread in all processes except the one that just hit the exception (leave it
// suspended in the exception).
std::vector<debug_ipc::ProcessThreadId> proc_thread_pairs =
agent->ClientSuspendAll(process_->koid(), hitting_thread->koid());
for (const debug_ipc::ProcessThreadId& id : proc_thread_pairs) {
if (DebuggedThread* thread = agent->GetDebuggedThread(id))
other_affected_threads.push_back(thread->GetThreadRecord(kSuspendedStackAmount));
}
break;
}
}
}
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