blob: b72aa4d67bf5f49ec2a7e4e1f39e60ce800d41f3 [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 "garnet/bin/debug_agent/process_watchpoint.h"
#include <gtest/gtest.h>
#include "garnet/bin/debug_agent/debugged_process.h"
#include "garnet/bin/debug_agent/debugged_thread.h"
#include "garnet/bin/debug_agent/mock_arch_provider.h"
#include "garnet/bin/debug_agent/mock_process.h"
#include "garnet/bin/debug_agent/process_watchpoint.h"
#include "garnet/bin/debug_agent/watchpoint.h"
#include "garnet/lib/debug_ipc/helper/zx_status.h"
namespace debug_agent {
namespace {
// A no-op process delegate.
class TestProcessDelegate : public Watchpoint::ProcessDelegate {
public:
using WatchpointMap =
std::multimap<debug_ipc::AddressRange, std::unique_ptr<ProcessWatchpoint>,
debug_ipc::AddressRangeCompare>;
const WatchpointMap& watchpoint_map() const { return wps_; }
void InjectMockProcess(std::unique_ptr<MockProcess> proc) {
procs_[proc->koid()] = std::move(proc);
}
// This only gets called if Breakpoint.SetSettings() is called.
zx_status_t RegisterWatchpoint(Watchpoint* wp, zx_koid_t process_koid,
const debug_ipc::AddressRange& range) override {
auto proc_it = procs_.find(process_koid);
FXL_DCHECK(proc_it != procs_.end());
auto found = wps_.find(range);
if (found != wps_.end())
return ZX_ERR_INTERNAL;
auto pwp =
std::make_unique<ProcessWatchpoint>(wp, proc_it->second.get(), range);
zx_status_t status = pwp->Init();
if (status != ZX_OK) {
FXL_LOG(ERROR) << "Failure initializing: "
<< debug_ipc::ZxStatusToString(status);
return status;
}
wps_.insert({range, std::move(pwp)});
return ZX_OK;
}
void UnregisterWatchpoint(Watchpoint* wp, zx_koid_t process_koid,
const debug_ipc::AddressRange& range) override {
// The destructor of the watchpoint will call the arch removal.
size_t remove_count = wps_.erase(range);
if (remove_count == 0)
GTEST_FAIL();
}
private:
WatchpointMap wps_;
std::map<zx_koid_t, std::unique_ptr<MockProcess>> procs_;
};
// Tests -----------------------------------------------------------------------
constexpr zx_koid_t kProcessId1 = 0x1;
constexpr zx_koid_t kProcessId2 = 0x2;
constexpr zx_koid_t kThreadId11 = 0x1;
constexpr zx_koid_t kThreadId12 = 0x2;
constexpr zx_koid_t kThreadId13 = 0x3;
constexpr zx_koid_t kThreadId21 = 0x4;
constexpr zx_koid_t kThreadId22 = 0x5;
constexpr zx_koid_t kThreadId23 = 0x6;
constexpr zx_koid_t kThreadId24 = 0x7;
constexpr zx_koid_t kThreadId25 = 0x8;
constexpr debug_ipc::AddressRange kAddressRange1 = {0x1000, 0x2000};
constexpr debug_ipc::AddressRange kAddressRange2 = {0x4000, 0x8000};
TEST(ProcessWatchpoint, InstallAndRemove) {
ScopedMockArchProvider scoped_arch_provider;
MockArchProvider* arch_provider = scoped_arch_provider.get_provider();
TestProcessDelegate process_delegate;
auto process1 = std::make_unique<MockProcess>(kProcessId1);
process1->AddThread(kThreadId11);
process1->AddThread(kThreadId12);
process1->AddThread(kThreadId13);
process_delegate.InjectMockProcess(std::move(process1));
auto process2 = std::make_unique<MockProcess>(kProcessId2);
process2->AddThread(kThreadId21);
process2->AddThread(kThreadId22);
process2->AddThread(kThreadId23);
process2->AddThread(kThreadId24);
process2->AddThread(kThreadId25);
process_delegate.InjectMockProcess(std::move(process2));
auto watchpoint = std::make_unique<Watchpoint>(&process_delegate);
// Insert the watchpoint for all threads.
debug_ipc::WatchpointSettings settings = {};
settings.watchpoint_id = 0x1;
settings.locations.push_back({kProcessId1, kThreadId11, kAddressRange1});
settings.locations.push_back({kProcessId1, kThreadId13, kAddressRange1});
// All of the process 2 threads.
settings.locations.push_back({kProcessId2, 0, kAddressRange2});
zx_status_t res = watchpoint->SetSettings(settings);
ASSERT_EQ(res, ZX_OK) << "Expected ZX_OK, got: "
<< debug_ipc::ZxStatusToString(res);
// Should have installed only one process watchpoint per process.
const auto& watchpoint_map = process_delegate.watchpoint_map();
ASSERT_EQ(watchpoint_map.count(kAddressRange1), 1u);
ASSERT_EQ(watchpoint_map.count(kAddressRange2), 1u);
// It should have installed 2 thread installations for process 1
EXPECT_EQ(arch_provider->WatchpointInstallCount(kAddressRange1), 2u);
// It should have installed 5 thread installations for process 1
EXPECT_EQ(arch_provider->WatchpointInstallCount(kAddressRange2), 5u);
// Once removed, we expect everything to go away.
watchpoint.reset();
ASSERT_EQ(watchpoint_map.count(kAddressRange1), 0u);
ASSERT_EQ(watchpoint_map.count(kAddressRange2), 0u);
EXPECT_EQ(arch_provider->WatchpointUninstallCount(kAddressRange1), 2u);
EXPECT_EQ(arch_provider->WatchpointUninstallCount(kAddressRange2), 5u);
}
} // namespace
} // namespace debug_agent