blob: d7164b0057fb74b0b9146c9207a68d2f5c726c49 [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/debug_agent.h"
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include <gtest/gtest.h>
#include "src/developer/debug/debug_agent/arch.h"
#include "src/developer/debug/debug_agent/mock_debug_agent_harness.h"
#include "src/developer/debug/debug_agent/mock_exception_handle.h"
#include "src/developer/debug/debug_agent/mock_process.h"
#include "src/developer/debug/debug_agent/mock_process_handle.h"
#include "src/developer/debug/debug_agent/mock_thread_handle.h"
#include "src/developer/debug/debug_agent/test_utils.h"
#include "src/developer/debug/ipc/agent_protocol.h"
#include "src/developer/debug/ipc/message_writer.h"
#include "src/developer/debug/shared/logging/debug.h"
#include "src/developer/debug/shared/test_with_loop.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace debug_agent {
namespace {
bool HasAttachedProcessWithKoid(DebugAgent* debug_agent, zx_koid_t koid) {
DebuggedProcess* proc = debug_agent->GetDebuggedProcess(koid);
if (!proc)
return false;
// All of our process handles should be mock ones.
return static_cast<MockProcessHandle&>(proc->process_handle()).is_attached();
}
// Setup -------------------------------------------------------------------------------------------
class DebugAgentMockProcess : public MockProcess {
public:
DebugAgentMockProcess(DebugAgent* debug_agent, zx_koid_t koid, std::string name)
: MockProcess(debug_agent, koid, std::move(name)) {}
~DebugAgentMockProcess() = default;
void SuspendAndSendModulesIfKnown() override {
// Send the modules over to the ipc.
debug_ipc::MessageWriter writer;
debug_ipc::WriteNotifyModules(modules_to_send_, &writer);
debug_agent()->stream()->Write(writer.MessageComplete());
};
void set_modules_to_send(debug_ipc::NotifyModules m) { modules_to_send_ = std::move(m); }
private:
debug_ipc::NotifyModules modules_to_send_;
};
} // namespace
// Tests -------------------------------------------------------------------------------------------
class DebugAgentTests : public debug_ipc::TestWithLoop {};
TEST_F(DebugAgentTests, OnGlobalStatus) {
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
debug_ipc::StatusRequest request = {};
debug_ipc::StatusReply reply = {};
remote_api->OnStatus(request, &reply);
ASSERT_EQ(reply.processes.size(), 0u);
constexpr uint64_t kProcessKoid1 = 0x1234;
const std::string kProcessName1 = "process-1";
constexpr uint64_t kProcess1ThreadKoid1 = 0x1;
auto process1 = std::make_unique<MockProcess>(nullptr, kProcessKoid1, kProcessName1);
process1->AddThread(kProcess1ThreadKoid1);
harness.debug_agent()->InjectProcessForTest(std::move(process1));
reply = {};
remote_api->OnStatus(request, &reply);
ASSERT_EQ(reply.processes.size(), 1u);
EXPECT_EQ(reply.processes[0].process_koid, kProcessKoid1);
EXPECT_EQ(reply.processes[0].process_name, kProcessName1);
ASSERT_EQ(reply.processes[0].threads.size(), 1u);
EXPECT_EQ(reply.processes[0].threads[0].process_koid, kProcessKoid1);
EXPECT_EQ(reply.processes[0].threads[0].thread_koid, kProcess1ThreadKoid1);
constexpr uint64_t kProcessKoid2 = 0x5678;
const std::string kProcessName2 = "process-2";
constexpr uint64_t kProcess2ThreadKoid1 = 0x1;
constexpr uint64_t kProcess2ThreadKoid2 = 0x2;
auto process2 = std::make_unique<MockProcess>(nullptr, kProcessKoid2, kProcessName2);
process2->AddThread(kProcess2ThreadKoid1);
process2->AddThread(kProcess2ThreadKoid2);
harness.debug_agent()->InjectProcessForTest(std::move(process2));
reply = {};
remote_api->OnStatus(request, &reply);
ASSERT_EQ(reply.processes.size(), 2u);
EXPECT_EQ(reply.processes[0].process_koid, kProcessKoid1);
EXPECT_EQ(reply.processes[0].process_name, kProcessName1);
ASSERT_EQ(reply.processes[0].threads.size(), 1u);
EXPECT_EQ(reply.processes[0].threads[0].process_koid, kProcessKoid1);
EXPECT_EQ(reply.processes[0].threads[0].thread_koid, kProcess1ThreadKoid1);
EXPECT_EQ(reply.processes[1].process_koid, kProcessKoid2);
EXPECT_EQ(reply.processes[1].process_name, kProcessName2);
ASSERT_EQ(reply.processes[1].threads.size(), 2u);
EXPECT_EQ(reply.processes[1].threads[0].process_koid, kProcessKoid2);
EXPECT_EQ(reply.processes[1].threads[0].thread_koid, kProcess2ThreadKoid1);
EXPECT_EQ(reply.processes[1].threads[1].process_koid, kProcessKoid2);
EXPECT_EQ(reply.processes[1].threads[1].thread_koid, kProcess2ThreadKoid2);
// Set a limbo provider.
constexpr zx_koid_t kProcKoid1 = 100;
constexpr zx_koid_t kThreadKoid1 = 101;
harness.system_interface()->mock_limbo_provider().AppendException(
MockProcessHandle(kProcKoid1, "proc1"), MockThreadHandle(kThreadKoid1, "thread1"),
MockExceptionHandle(kThreadKoid1));
constexpr zx_koid_t kProcKoid2 = 102;
constexpr zx_koid_t kThreadKoid2 = 103;
harness.system_interface()->mock_limbo_provider().AppendException(
MockProcessHandle(kProcKoid2, "proc2"), MockThreadHandle(kThreadKoid1, "thread2"),
MockExceptionHandle(kThreadKoid2));
reply = {};
remote_api->OnStatus(request, &reply);
// The attached processes should still be there.
ASSERT_EQ(reply.processes.size(), 2u);
// The limbo processes should be there.
ASSERT_EQ(reply.limbo.size(), 2u);
EXPECT_EQ(reply.limbo[0].process_koid, kProcKoid1);
EXPECT_EQ(reply.limbo[0].process_name, "proc1");
ASSERT_EQ(reply.limbo[0].threads.size(), 1u);
// TODO(donosoc): Add exception type.
}
TEST_F(DebugAgentTests, OnProcessStatus) {
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
constexpr uint64_t kProcessKoid1 = 0x1234;
std::string kProcessName1 = "process-1";
auto process1 =
std::make_unique<DebugAgentMockProcess>(harness.debug_agent(), kProcessKoid1, kProcessName1);
harness.debug_agent()->InjectProcessForTest(std::move(process1));
constexpr uint64_t kProcessKoid2 = 0x5678;
std::string kProcessName2 = "process-2";
auto process2 =
std::make_unique<DebugAgentMockProcess>(harness.debug_agent(), kProcessKoid2, kProcessName2);
auto* process2_ptr = process2.get();
harness.debug_agent()->InjectProcessForTest(std::move(process2));
// Asking for a un-existent process should fail.
debug_ipc::ProcessStatusRequest request = {};
request.process_koid = 0xdeadbeef;
debug_ipc::ProcessStatusReply reply = {};
remote_api->OnProcessStatus(request, &reply);
EXPECT_EQ(reply.status, (uint32_t)ZX_ERR_NOT_FOUND) << zx_status_get_string(reply.status);
debug_ipc::NotifyModules modules_to_send = {};
modules_to_send.process_koid = kProcessKoid2;
modules_to_send.modules.push_back({"module-1", 0x1, 0x5, "build-1"});
modules_to_send.modules.push_back({"module-2", 0x2, 0x7, "build-2"});
process2_ptr->set_modules_to_send(modules_to_send);
// Asking for an existent one should send the process and modules notification.
request.process_koid = kProcessKoid2;
remote_api->OnProcessStatus(request, &reply);
EXPECT_EQ(reply.status, (uint32_t)ZX_OK) << zx_status_get_string(reply.status);
loop().RunUntilNoTasks();
auto& process_starts = harness.stream_backend()->process_starts();
ASSERT_EQ(process_starts.size(), 1u);
EXPECT_EQ(process_starts[0].koid, kProcessKoid2);
EXPECT_EQ(process_starts[0].name, kProcessName2);
auto& modules = harness.stream_backend()->modules();
ASSERT_EQ(modules.size(), 1u);
EXPECT_EQ(modules[0].process_koid, kProcessKoid2);
ASSERT_EQ(modules[0].modules.size(), modules_to_send.modules.size());
ASSERT_EQ(modules[0].modules[0].name, modules_to_send.modules[0].name);
ASSERT_EQ(modules[0].modules[0].base, modules_to_send.modules[0].base);
ASSERT_EQ(modules[0].modules[0].debug_address, modules_to_send.modules[0].debug_address);
ASSERT_EQ(modules[0].modules[0].build_id, modules_to_send.modules[0].build_id);
ASSERT_EQ(modules[0].modules[1].name, modules_to_send.modules[1].name);
ASSERT_EQ(modules[0].modules[1].base, modules_to_send.modules[1].base);
ASSERT_EQ(modules[0].modules[1].debug_address, modules_to_send.modules[1].debug_address);
ASSERT_EQ(modules[0].modules[1].build_id, modules_to_send.modules[1].build_id);
}
TEST_F(DebugAgentTests, OnAttachNotFound) {
uint32_t transaction_id = 1u;
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
debug_ipc::AttachRequest attach_request;
attach_request.type = debug_ipc::TaskType::kProcess;
attach_request.koid = -1;
remote_api->OnAttach(transaction_id++, attach_request);
{
// Should've gotten an attach reply.
auto& attach_replies = harness.stream_backend()->attach_replies();
ASSERT_EQ(attach_replies.size(), 1u);
EXPECT_ZX_EQ(attach_replies[0].status, ZX_ERR_NOT_FOUND);
}
constexpr zx_koid_t kProcKoid1 = 100;
constexpr zx_koid_t kThreadKoid1 = 101;
harness.system_interface()->mock_limbo_provider().AppendException(
MockProcessHandle(kProcKoid1, "proc1"), MockThreadHandle(kThreadKoid1, "thread1"),
MockExceptionHandle(kThreadKoid1));
// Even with limbo it should fail.
remote_api->OnAttach(transaction_id++, attach_request);
{
// Should've gotten an attach reply.
auto& attach_replies = harness.stream_backend()->attach_replies();
ASSERT_EQ(attach_replies.size(), 2u);
EXPECT_ZX_EQ(attach_replies[1].status, ZX_ERR_NOT_FOUND);
}
}
TEST_F(DebugAgentTests, OnAttach) {
constexpr zx_koid_t kProcess1Koid = 11u; // Koid for job1-p2 from the GetMockJobTree() hierarchy.
uint32_t transaction_id = 1u;
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
debug_ipc::AttachRequest attach_request;
attach_request.type = debug_ipc::TaskType::kProcess;
attach_request.koid = kProcess1Koid;
remote_api->OnAttach(transaction_id++, attach_request);
// We should've received a watch command (which does the low level exception watching).
EXPECT_TRUE(HasAttachedProcessWithKoid(harness.debug_agent(), kProcess1Koid));
// We should've gotten an attach reply.
auto& attach_replies = harness.stream_backend()->attach_replies();
auto reply = attach_replies.back();
ASSERT_EQ(attach_replies.size(), 1u);
EXPECT_ZX_EQ(reply.status, ZX_OK);
EXPECT_EQ(reply.koid, kProcess1Koid);
EXPECT_EQ(reply.name, "job1-p2");
// Asking for some invalid process should fail.
attach_request.koid = 0x231315; // Some invalid value.
remote_api->OnAttach(transaction_id++, attach_request);
// We should've gotten an error reply.
ASSERT_EQ(attach_replies.size(), 2u);
reply = attach_replies.back();
EXPECT_ZX_EQ(reply.status, ZX_ERR_NOT_FOUND);
// Attaching to a third process should work.
attach_request.koid = 21u;
remote_api->OnAttach(transaction_id++, attach_request);
ASSERT_EQ(attach_replies.size(), 3u);
reply = attach_replies.back();
EXPECT_ZX_EQ(reply.status, ZX_OK);
EXPECT_EQ(reply.koid, 21u);
EXPECT_EQ(reply.name, "job121-p2");
// Attaching again to a process should fail.
remote_api->OnAttach(transaction_id++, attach_request);
ASSERT_EQ(attach_replies.size(), 4u);
reply = attach_replies.back();
EXPECT_ZX_EQ(reply.status, ZX_ERR_ALREADY_BOUND);
}
TEST_F(DebugAgentTests, AttachToLimbo) {
// debug_ipc::SetDebugMode(true);
// debug_ipc::SetLogCategories({debug_ipc::LogCategory::kAll});
uint32_t transaction_id = 1u;
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
constexpr zx_koid_t kProcKoid = 100;
constexpr zx_koid_t kThreadKoid = 101;
MockProcessHandle mock_process(kProcKoid, "proc");
MockThreadHandle mock_thread(kThreadKoid, "thread");
mock_process.set_threads({mock_thread});
harness.system_interface()->mock_limbo_provider().AppendException(
mock_process, mock_thread, MockExceptionHandle(kThreadKoid));
debug_ipc::AttachRequest attach_request = {};
attach_request.type = debug_ipc::TaskType::kProcess;
attach_request.koid = kProcKoid;
remote_api->OnAttach(transaction_id++, attach_request);
// Process should be watching.
EXPECT_TRUE(HasAttachedProcessWithKoid(harness.debug_agent(), kProcKoid));
// We should've gotten an attach reply.
auto& attach_replies = harness.stream_backend()->attach_replies();
ASSERT_EQ(attach_replies.size(), 1u);
auto reply = attach_replies.back();
EXPECT_ZX_EQ(reply.status, ZX_OK);
EXPECT_EQ(reply.koid, kProcKoid);
EXPECT_EQ(reply.name, "proc");
{
DebuggedProcess* process = harness.debug_agent()->GetDebuggedProcess(kProcKoid);
ASSERT_TRUE(process);
auto threads = process->GetThreads();
ASSERT_EQ(threads.size(), 1u);
// Search for the exception thread.
DebuggedThread* exception_thread = nullptr;
for (DebuggedThread* thread : threads) {
if (thread->koid() == kThreadKoid) {
exception_thread = thread;
break;
}
}
ASSERT_TRUE(exception_thread);
ASSERT_TRUE(exception_thread->in_exception());
EXPECT_EQ(exception_thread->exception_handle()->GetThreadHandle()->GetKoid(), kThreadKoid);
}
}
TEST_F(DebugAgentTests, OnEnterLimbo) {
MockDebugAgentHarness harness;
constexpr zx_koid_t kProcKoid1 = 100;
constexpr zx_koid_t kThreadKoid1 = 101;
harness.system_interface()->mock_limbo_provider().AppendException(
MockProcessHandle(kProcKoid1, "proc1"), MockThreadHandle(kThreadKoid1, "thread1"),
MockExceptionHandle(kThreadKoid1));
// Call the limbo.
harness.system_interface()->mock_limbo_provider().CallOnEnterLimbo();
// Should've sent a notification.
{
ASSERT_EQ(harness.stream_backend()->process_starts().size(), 1u);
auto& process_start = harness.stream_backend()->process_starts()[0];
EXPECT_EQ(process_start.type, debug_ipc::NotifyProcessStarting::Type::kLimbo);
EXPECT_EQ(process_start.koid, kProcKoid1);
EXPECT_EQ(process_start.component_id, 0u);
EXPECT_EQ(process_start.name, "proc1");
}
}
TEST_F(DebugAgentTests, DetachFromLimbo) {
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
constexpr zx_koid_t kProcKoid = 14; // MockJobTree job11-p1
// Attempting to detach to a process that doesn't exist should fail.
{
debug_ipc::DetachRequest request = {};
request.type = debug_ipc::TaskType::kProcess;
request.koid = kProcKoid;
debug_ipc::DetachReply reply = {};
remote_api->OnDetach(request, &reply);
ASSERT_ZX_EQ(reply.status, ZX_ERR_NOT_FOUND);
ASSERT_EQ(harness.system_interface()->mock_limbo_provider().release_calls().size(), 0u);
}
// Adding it should now find it and remove it.
constexpr zx_koid_t kProcKoid1 = 100;
constexpr zx_koid_t kThreadKoid1 = 101;
harness.system_interface()->mock_limbo_provider().AppendException(
MockProcessHandle(kProcKoid1, "proc1"), MockThreadHandle(kThreadKoid1, "thread1"),
MockExceptionHandle(kThreadKoid1));
{
debug_ipc::DetachRequest request = {};
request.type = debug_ipc::TaskType::kProcess;
request.koid = kProcKoid1;
debug_ipc::DetachReply reply = {};
remote_api->OnDetach(request, &reply);
ASSERT_ZX_EQ(reply.status, ZX_OK);
ASSERT_EQ(harness.system_interface()->mock_limbo_provider().release_calls().size(), 1u);
EXPECT_EQ(harness.system_interface()->mock_limbo_provider().release_calls()[0], kProcKoid1);
}
// This should've remove it from limbo, trying it again should fail.
{
debug_ipc::DetachRequest request = {};
request.type = debug_ipc::TaskType::kProcess;
request.koid = kProcKoid1;
debug_ipc::DetachReply reply = {};
remote_api->OnDetach(request, &reply);
ASSERT_ZX_EQ(reply.status, ZX_ERR_NOT_FOUND);
ASSERT_EQ(harness.system_interface()->mock_limbo_provider().release_calls().size(), 1u);
EXPECT_EQ(harness.system_interface()->mock_limbo_provider().release_calls()[0], kProcKoid1);
}
}
TEST_F(DebugAgentTests, Kill) {
uint32_t transaction_id = 1u;
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
constexpr zx_koid_t kProcKoid = 14; // MockJobTree job11-p1
// Attempt to kill a process that's not there should fail.
{
debug_ipc::KillRequest kill_request = {};
kill_request.process_koid = kProcKoid;
debug_ipc::KillReply kill_reply = {};
remote_api->OnKill(kill_request, &kill_reply);
ASSERT_ZX_EQ(kill_reply.status, ZX_ERR_NOT_FOUND);
}
// Attach to a process so that the debugger knows about it.
{
debug_ipc::AttachRequest attach_request = {};
attach_request.type = debug_ipc::TaskType::kProcess;
attach_request.koid = kProcKoid;
remote_api->OnAttach(transaction_id++, attach_request);
// There should be a process.
ASSERT_EQ(harness.debug_agent()->procs_.size(), 1u);
// Should not come from limbo.
EXPECT_FALSE(harness.debug_agent()->procs_.begin()->second->from_limbo());
}
// Killing now should work.
{
debug_ipc::KillRequest kill_request = {};
kill_request.process_koid = kProcKoid;
debug_ipc::KillReply kill_reply = {};
remote_api->OnKill(kill_request, &kill_reply);
// There should be no more processes.
ASSERT_EQ(harness.debug_agent()->procs_.size(), 0u);
// Killing again should fail.
remote_api->OnKill(kill_request, &kill_reply);
ASSERT_ZX_EQ(kill_reply.status, ZX_ERR_NOT_FOUND);
}
// Add the process to the limbo.
constexpr zx_koid_t kLimboProcKoid = 100;
constexpr zx_koid_t kLimboThreadKoid = 101;
MockProcessHandle mock_process(kLimboProcKoid, "proc");
// This is a limbo process so we can not kill it.
mock_process.set_kill_status(ZX_ERR_ACCESS_DENIED);
MockThreadHandle mock_thread(kLimboThreadKoid, "thread");
MockExceptionHandle mock_exception(kLimboThreadKoid);
mock_process.set_threads({mock_thread});
harness.system_interface()->mock_limbo_provider().AppendException(mock_process, mock_thread,
mock_exception);
// There should be no more processes.
ASSERT_EQ(harness.debug_agent()->procs_.size(), 0u);
// Killing now should release it.
{
debug_ipc::KillRequest kill_request = {};
kill_request.process_koid = kLimboProcKoid;
debug_ipc::KillReply kill_reply = {};
remote_api->OnKill(kill_request, &kill_reply);
ASSERT_ZX_EQ(kill_reply.status, ZX_OK);
ASSERT_EQ(harness.system_interface()->mock_limbo_provider().release_calls().size(), 1u);
EXPECT_EQ(harness.system_interface()->mock_limbo_provider().release_calls()[0], kLimboProcKoid);
// Killing again should not find it.
remote_api->OnKill(kill_request, &kill_reply);
ASSERT_ZX_EQ(kill_reply.status, ZX_ERR_NOT_FOUND);
}
harness.system_interface()->mock_limbo_provider().AppendException(mock_process, mock_thread,
mock_exception);
debug_ipc::AttachRequest attach_request = {};
attach_request.type = debug_ipc::TaskType::kProcess;
attach_request.koid = kLimboProcKoid;
remote_api->OnAttach(transaction_id++, attach_request);
// There should be a process.
ASSERT_EQ(harness.debug_agent()->procs_.size(), 1u);
{
auto it = harness.debug_agent()->procs_.find(kLimboProcKoid);
ASSERT_NE(it, harness.debug_agent()->procs_.end());
EXPECT_TRUE(harness.debug_agent()->procs_.begin()->second->from_limbo());
// Killing it should free the process.
debug_ipc::KillRequest kill_request = {};
kill_request.process_koid = kLimboProcKoid;
debug_ipc::KillReply kill_reply = {};
remote_api->OnKill(kill_request, &kill_reply);
ASSERT_ZX_EQ(kill_reply.status, ZX_OK);
ASSERT_EQ(harness.debug_agent()->procs_.size(), 0u);
// There should be a limbo process to be killed.
ASSERT_EQ(harness.debug_agent()->killed_limbo_procs_.size(), 1u);
EXPECT_EQ(harness.debug_agent()->killed_limbo_procs_.count(kLimboProcKoid), 1u);
// There should've have been more release calls (yet).
ASSERT_EQ(harness.system_interface()->mock_limbo_provider().release_calls().size(), 1u);
// When the process "re-enters" the limbo, it should be removed.
harness.system_interface()->mock_limbo_provider().AppendException(mock_process, mock_thread,
mock_exception);
harness.system_interface()->mock_limbo_provider().CallOnEnterLimbo();
// There should not be an additional proc in the agent.
ASSERT_EQ(harness.debug_agent()->procs_.size(), 0u);
// There should've been a release call.
ASSERT_EQ(harness.system_interface()->mock_limbo_provider().release_calls().size(), 2u);
EXPECT_EQ(harness.system_interface()->mock_limbo_provider().release_calls()[1], kLimboProcKoid);
}
}
TEST_F(DebugAgentTests, OnUpdateGlobalSettings) {
MockDebugAgentHarness harness;
RemoteAPI* remote_api = harness.debug_agent();
// The default strategy should be first chance for a type that has yet to be
// updated.
EXPECT_EQ(debug_ipc::ExceptionStrategy::kFirstChance,
harness.debug_agent()->GetExceptionStrategy(debug_ipc::ExceptionType::kGeneral));
EXPECT_EQ(debug_ipc::ExceptionStrategy::kFirstChance,
harness.debug_agent()->GetExceptionStrategy(debug_ipc::ExceptionType::kPageFault));
{
const debug_ipc::UpdateGlobalSettingsRequest request = {
.exception_strategies =
{
{
.type = debug_ipc::ExceptionType::kGeneral,
.value = debug_ipc::ExceptionStrategy::kSecondChance,
},
{
.type = debug_ipc::ExceptionType::kPageFault,
.value = debug_ipc::ExceptionStrategy::kSecondChance,
},
},
};
debug_ipc::UpdateGlobalSettingsReply reply;
remote_api->OnUpdateGlobalSettings(request, &reply);
EXPECT_EQ(ZX_OK, reply.status);
}
EXPECT_EQ(debug_ipc::ExceptionStrategy::kSecondChance,
harness.debug_agent()->GetExceptionStrategy(debug_ipc::ExceptionType::kGeneral));
EXPECT_EQ(debug_ipc::ExceptionStrategy::kSecondChance,
harness.debug_agent()->GetExceptionStrategy(debug_ipc::ExceptionType::kPageFault));
{
const debug_ipc::UpdateGlobalSettingsRequest request = {
.exception_strategies =
{
{
.type = debug_ipc::ExceptionType::kGeneral,
.value = debug_ipc::ExceptionStrategy::kFirstChance,
},
},
};
debug_ipc::UpdateGlobalSettingsReply reply;
remote_api->OnUpdateGlobalSettings(request, &reply);
EXPECT_EQ(ZX_OK, reply.status);
}
EXPECT_EQ(debug_ipc::ExceptionStrategy::kFirstChance,
harness.debug_agent()->GetExceptionStrategy(debug_ipc::ExceptionType::kGeneral));
EXPECT_EQ(debug_ipc::ExceptionStrategy::kSecondChance,
harness.debug_agent()->GetExceptionStrategy(debug_ipc::ExceptionType::kPageFault));
}
} // namespace debug_agent