blob: 33abe90d55f441b81a58a44b1f9f0080d8091571 [file] [log] [blame]
// Copyright 2019 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_watchpoint.h"
#include <lib/zx/port.h>
#include <zircon/syscalls/port.h>
#include "src/developer/debug/debug_agent/arch.h"
#include "src/developer/debug/debug_agent/debugged_process.h"
#include "src/developer/debug/debug_agent/debugged_thread.h"
#include "src/developer/debug/debug_agent/watchpoint.h"
#include "src/developer/debug/shared/logging/logging.h"
#include "src/developer/debug/shared/zx_status.h"
namespace debug_agent {
namespace {
std::string KoidsToString(const std::vector<DebuggedThread*>& threads) {
std::stringstream ss;
for (DebuggedThread* thread : threads) {
ss << thread->koid() << ", ";
}
return ss.str();
}
} // namespace
ProcessWatchpoint::ProcessWatchpoint(Watchpoint* watchpoint,
DebuggedProcess* process,
const debug_ipc::AddressRange& range)
: watchpoint_(watchpoint), process_(process), range_(range) {}
ProcessWatchpoint::~ProcessWatchpoint() { Uninstall(); }
zx_status_t ProcessWatchpoint::Init() { return Update(); }
zx_status_t ProcessWatchpoint::Update() {
std::set<zx_koid_t> watched_threads = {};
bool threads_present =
watchpoint_->ThreadsToInstall(process_->koid(), &watched_threads);
FXL_DCHECK(threads_present);
std::vector<DebuggedThread*> threads_to_remove = {};
std::vector<DebuggedThread*> threads_to_install = {};
if (watched_threads.empty()) {
// We need to install this on all the threads, so we simply check for those
// we're missing.
for (DebuggedThread* thread : process_->GetThreads()) {
// See if we already have the thread.
if (installed_threads_.find(thread->koid()) != installed_threads_.end())
continue;
threads_to_install.push_back(thread);
}
} else {
// Here we need to see what threads to remove.
for (zx_koid_t thread_koid : installed_threads_) {
// If it is not installed, we leave it as is.
if (watched_threads.find(thread_koid) != watched_threads.end())
continue;
// The thread could have exited.
DebuggedThread* thread = process_->GetThread(thread_koid);
if (!thread)
continue;
threads_to_remove.push_back(thread);
}
// We now check which ones to install.
for (zx_koid_t thread_koid : watched_threads) {
// If it's already installed, we leave it as is.
if (installed_threads_.find(thread_koid) != installed_threads_.end())
continue;
// The thread could have exited.
DebuggedThread* thread = process_->GetThread(thread_koid);
if (!thread)
continue;
threads_to_install.push_back(thread);
}
}
zx_status_t res = UpdateWatchpoints(threads_to_remove, threads_to_install);
if (res != ZX_OK)
return res;
return ZX_OK;
}
debug_ipc::BreakpointStats ProcessWatchpoint::OnHit() {
FXL_DCHECK(watchpoint_);
return watchpoint_->OnHit();
}
void ProcessWatchpoint::Uninstall() {
std::vector<DebuggedThread*> threads_to_remove = {};
for (zx_koid_t thread_koid : installed_threads_) {
DebuggedThread* thread = process_->GetThread(thread_koid);
// Thread might go away at anytime.
if (!thread)
continue;
threads_to_remove.push_back(thread);
}
// We only want to remove threads.
UpdateWatchpoints(threads_to_remove, {});
}
zx_status_t ProcessWatchpoint::UpdateWatchpoints(
const std::vector<DebuggedThread*>& threads_to_remove,
const std::vector<DebuggedThread*>& threads_to_install) {
DEBUG_LOG(Watchpoint) << "Installs: " << KoidsToString(threads_to_install)
<< "uninstalls: " << KoidsToString(threads_to_remove);
// We suspend the process synchronously.
// TODO(donosoc): If this prooves to be too intrusive, we could just stop
// the threads that will be changed.
std::vector<uint64_t> suspended_koids;
process()->SuspendAll(true, &suspended_koids);
auto& arch_provider = arch::ArchProvider::Get();
for (DebuggedThread* thread : threads_to_remove) {
auto res = arch_provider.UninstallWatchpoint(&thread->thread(), range());
if (res != ZX_OK)
return res;
installed_threads_.erase(thread->koid());
}
for (DebuggedThread* thread : threads_to_install) {
auto res = arch_provider.InstallWatchpoint(&thread->thread(), range());
if (res != ZX_OK)
return res;
installed_threads_.insert(thread->koid());
}
// We resume the threads that were affected.
for (uint64_t thread_koid : suspended_koids) {
auto* thread = process()->GetThread(thread_koid);
FXL_DCHECK(thread);
thread->Resume({});
}
return ZX_OK;
}
} // namespace debug_agent