| // 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::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].id.process, kProcessKoid1); | |
| EXPECT_EQ(reply.processes[0].threads[0].id.thread, 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].id.process, kProcessKoid1); | |
| EXPECT_EQ(reply.processes[0].threads[0].id.thread, 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].id.process, kProcessKoid2); | |
| EXPECT_EQ(reply.processes[1].threads[0].id.thread, kProcess2ThreadKoid1); | |
| EXPECT_EQ(reply.processes[1].threads[1].id.process, kProcessKoid2); | |
| EXPECT_EQ(reply.processes[1].threads[1].id.thread, 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_TRUE(reply.status.has_error()); | |
| 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_TRUE(reply.status.ok()); | |
| 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_TRUE(attach_replies[0].status.has_error()); | |
| } | |
| 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_TRUE(attach_replies[1].status.has_error()); | |
| } | |
| } | |
| 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_TRUE(reply.status.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_TRUE(reply.status.has_error()); | |
| // 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_TRUE(reply.status.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_TRUE(reply.status.has_error()); | |
| } | |
| TEST_F(DebugAgentTests, AttachToLimbo) { | |
| // debug::SetDebugMode(true); | |
| // debug::SetLogCategories({debug::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_TRUE(reply.status.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_TRUE(reply.status.has_error()); | |
| 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_TRUE(reply.status.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_TRUE(reply.status.has_error()); | |
| 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_TRUE(kill_reply.status.has_error()); | |
| } | |
| // 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_TRUE(kill_reply.status.has_error()); | |
| } | |
| // 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(debug::Status("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_TRUE(kill_reply.status.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_TRUE(kill_reply.status.has_error()); | |
| } | |
| 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_TRUE(kill_reply.status.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_TRUE(reply.status.ok()); | |
| } | |
| 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_TRUE(reply.status.ok()); | |
| } | |
| 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 |