blob: 09016cb72740f3411105b1d14bf042146a06e3b8 [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/breakpoint.h"
#include "src/developer/debug/debug_agent/process_breakpoint.h"
#include "src/developer/debug/shared/logging/logging.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace debug_agent {
// Breakpoint::DoesExceptionApply ------------------------------------------------------------------
bool Breakpoint::DoesExceptionApply(debug_ipc::BreakpointType exception_type,
debug_ipc::BreakpointType bp_type) {
if (exception_type == debug_ipc::BreakpointType::kLast ||
bp_type == debug_ipc::BreakpointType::kLast) {
FX_NOTREACHED() << "Wrong exception (" << static_cast<uint32_t>(exception_type)
<< ") or bp_type (" << static_cast<uint32_t>(bp_type) << ").";
return false;
}
if (exception_type == debug_ipc::BreakpointType::kSoftware)
return bp_type == debug_ipc::BreakpointType::kSoftware;
if (exception_type == debug_ipc::BreakpointType::kHardware)
return bp_type == debug_ipc::BreakpointType::kHardware;
// Now only watchpoint types are left.
if (!IsWatchpointType(bp_type))
return false;
// If any the types is a read write, it targets this type.
if (exception_type == debug_ipc::BreakpointType::kReadWrite ||
bp_type == debug_ipc::BreakpointType::kReadWrite)
return true;
// R/W case are already covered.
return exception_type == bp_type;
}
// Breakpoint::ProcessDelegate ---------------------------------------------------------------------
zx_status_t Breakpoint::ProcessDelegate::RegisterBreakpoint(Breakpoint* bp, zx_koid_t process_koid,
uint64_t address) {
FX_NOTREACHED() << "Should override.";
return ZX_ERR_NOT_SUPPORTED;
}
// Called When the breakpoint no longer applies to this location.
void Breakpoint::ProcessDelegate::UnregisterBreakpoint(Breakpoint* bp, zx_koid_t process_koid,
uint64_t address) {
FX_NOTREACHED() << "Should override.";
}
zx_status_t Breakpoint::ProcessDelegate::RegisterWatchpoint(Breakpoint* bp, zx_koid_t process_koid,
const debug_ipc::AddressRange& range) {
FX_NOTREACHED() << "Should override.";
return ZX_ERR_NOT_SUPPORTED;
}
void Breakpoint::ProcessDelegate::UnregisterWatchpoint(Breakpoint* bp, zx_koid_t process_koid,
const debug_ipc::AddressRange& range) {
FX_NOTREACHED() << "Should override.";
}
// Breakpoint --------------------------------------------------------------------------------------
namespace {
std::string Preamble(const Breakpoint* bp) {
return fxl::StringPrintf("[Breakpoint %u (%s)] ", bp->settings().id, bp->settings().name.c_str());
}
// Debug logging to see if a breakpoint applies to a thread.
void LogAppliesToThread(const Breakpoint* bp, zx_koid_t pid, zx_koid_t tid, bool applies) {
DEBUG_LOG(Breakpoint) << Preamble(bp) << "applies to [P: " << pid << ", T: " << tid << "]? "
<< applies;
}
void LogSetSettings(debug_ipc::FileLineFunction location, const Breakpoint* bp) {
std::stringstream ss;
// Print a list of locations (process + thread + address) place of an actual breakpoint.
ss << "Updating locations: ";
for (auto& location : bp->settings().locations) {
// Log the process.
ss << std::dec << "[P: " << location.process_koid;
// |thread_koid| == 0 means that it applies to all the threads.
if (location.thread_koid != 0)
ss << ", T: " << location.thread_koid;
// Print the actual location.
ss << "], addr: 0x" << std::hex << location.address
<< ", range: " << location.address_range.ToString();
}
DEBUG_LOG_WITH_LOCATION(Breakpoint, location) << Preamble(bp) << ss.str();
}
} // namespace
Breakpoint::Breakpoint(ProcessDelegate* process_delegate, bool is_debug_agent_internal)
: process_delegate_(process_delegate), is_debug_agent_internal_(is_debug_agent_internal) {}
Breakpoint::~Breakpoint() {
DEBUG_LOG(Breakpoint) << Preamble(this) << "Deleting.";
for (const auto& [process_koid, address] : locations_) {
DEBUG_LOG(Breakpoint) << "- Proc " << process_koid << " at address 0x " << std::hex << address;
process_delegate_->UnregisterBreakpoint(this, process_koid, address);
}
for (const auto& [process_koid, address_range] : watchpoint_locations_) {
DEBUG_LOG(Breakpoint) << "- Proc " << process_koid << " at address " << std::hex
<< address_range.ToString();
process_delegate_->UnregisterWatchpoint(this, process_koid, address_range);
}
}
zx_status_t Breakpoint::SetSettings(const debug_ipc::BreakpointSettings& settings) {
FX_DCHECK(settings.type != debug_ipc::BreakpointType::kLast);
settings_ = settings;
LogSetSettings(FROM_HERE, this);
// The stats needs to reference the current ID. We assume setting the
// settings doesn't update the stats (an option to do this may need to be
// added in the future).
stats_.id = settings_.id;
switch (settings_.type) {
case debug_ipc::BreakpointType::kSoftware:
case debug_ipc::BreakpointType::kHardware:
return SetBreakpointLocations(settings);
case debug_ipc::BreakpointType::kReadWrite:
case debug_ipc::BreakpointType::kWrite:
return SetWatchpointLocations(settings);
case debug_ipc::BreakpointType::kLast:
break;
}
FX_NOTREACHED() << "Invalid breakpoint type: " << static_cast<int>(settings_.type);
return ZX_ERR_INVALID_ARGS;
}
zx_status_t Breakpoint::SetSettings(std::string name, zx_koid_t process_koid, uint64_t address) {
debug_ipc::BreakpointSettings settings;
settings.id = debug_ipc::kDebugAgentInternalBreakpointId;
settings.name = std::move(name);
debug_ipc::ProcessBreakpointSettings& location = settings.locations.emplace_back();
location.process_koid = process_koid;
location.address = address;
return SetSettings(settings);
}
zx_status_t Breakpoint::SetBreakpointLocations(const debug_ipc::BreakpointSettings& settings) {
zx_status_t result = ZX_OK;
// The set of new locations.
std::set<LocationPair> new_set;
for (const auto& cur : settings.locations)
new_set.emplace(cur.process_koid, cur.address);
// Removed locations.
for (const auto& loc : locations_) {
if (new_set.find(loc) == new_set.end())
process_delegate_->UnregisterBreakpoint(this, loc.first, loc.second);
}
// Added locations.
for (const auto& loc : new_set) {
if (locations_.find(loc) == locations_.end()) {
zx_status_t process_status =
process_delegate_->RegisterBreakpoint(this, loc.first, loc.second);
if (process_status != ZX_OK)
result = process_status;
}
}
locations_ = std::move(new_set);
return result;
}
zx_status_t Breakpoint::SetWatchpointLocations(const debug_ipc::BreakpointSettings& settings) {
zx_status_t result = ZX_OK;
// The set of new locations.
std::set<WatchpointLocationPair, WatchpointLocationPairCompare> new_set;
for (const auto& cur : settings.locations)
new_set.emplace(cur.process_koid, cur.address_range);
// Removed locations.
for (const auto& loc : watchpoint_locations_) {
if (new_set.find(loc) == new_set.end())
process_delegate_->UnregisterWatchpoint(this, loc.first, loc.second);
}
// Added locations.
for (const auto& loc : new_set) {
if (watchpoint_locations_.find(loc) == watchpoint_locations_.end()) {
zx_status_t process_status =
process_delegate_->RegisterWatchpoint(this, loc.first, loc.second);
if (process_status != ZX_OK)
result = process_status;
}
}
watchpoint_locations_ = std::move(new_set);
return result;
}
bool Breakpoint::AppliesToThread(zx_koid_t pid, zx_koid_t tid) const {
for (auto& location : settings_.locations) {
if (location.process_koid == pid) {
if (location.thread_koid == 0 || location.thread_koid == tid) {
LogAppliesToThread(this, pid, tid, true);
return true;
}
}
}
LogAppliesToThread(this, pid, tid, false);
return false;
}
// In the future we will want to have breakpoints that trigger on a specific
// hit count or other conditions and will need a "kContinue" result.
Breakpoint::HitResult Breakpoint::OnHit() {
stats_.hit_count++;
if (settings_.one_shot) {
DEBUG_LOG(Breakpoint) << Preamble(this) << "One-shot breakpoint. Will be deleted.";
stats_.should_delete = true;
return HitResult::kOneShotHit;
}
return HitResult::kHit;
}
// WatchpointLocationPairCompare -------------------------------------------------------------------
bool Breakpoint::WatchpointLocationPairCompare::operator()(
const WatchpointLocationPair& lhs, const WatchpointLocationPair& rhs) const {
if (lhs.first != rhs.first)
return lhs.first < rhs.first;
if (lhs.second.begin() != rhs.second.begin())
return lhs.second.begin() < rhs.second.begin();
return lhs.second.end() < rhs.second.end();
}
} // namespace debug_agent