| // 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/debugged_thread.h" | 
 |  | 
 | #include <gtest/gtest.h> | 
 |  | 
 | #include "src/developer/debug/debug_agent/arch.h" | 
 | #include "src/developer/debug/debug_agent/debugged_process.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.h" | 
 | #include "src/developer/debug/debug_agent/mock_thread_handle.h" | 
 | #include "src/developer/debug/debug_agent/remote_api.h" | 
 |  | 
 | namespace debug_agent { | 
 |  | 
 | using namespace debug_ipc; | 
 |  | 
 | namespace { | 
 |  | 
 | TEST(DebuggedThread, Resume) { | 
 |   MockDebugAgentHarness harness; | 
 |  | 
 |   constexpr zx_koid_t kProcessKoid = 0x8723456; | 
 |   MockProcess process(harness.debug_agent(), kProcessKoid); | 
 |  | 
 |   constexpr zx_koid_t kThreadKoid = 0x8723457; | 
 |   MockThread* thread = process.AddThread(kThreadKoid); | 
 |   EXPECT_FALSE(thread->in_exception()); | 
 |  | 
 |   ExceptionHandle::Resolution resolution = ExceptionHandle::Resolution::kTryNext; | 
 |   debug_ipc::ExceptionStrategy exception_strategy = debug_ipc::ExceptionStrategy::kNone; | 
 |   auto exception = std::make_unique<MockExceptionHandle>( | 
 |       [&resolution](ExceptionHandle::Resolution new_res) { resolution = new_res; }, | 
 |       [&exception_strategy](debug_ipc::ExceptionStrategy new_strategy) { | 
 |         exception_strategy = new_strategy; | 
 |       }); | 
 |   thread->set_exception_handle(std::move(exception)); | 
 |   EXPECT_TRUE(thread->in_exception()); | 
 |   thread->ClientResume( | 
 |       debug_ipc::ResumeRequest{.how = debug_ipc::ResumeRequest::How::kResolveAndContinue}); | 
 |   EXPECT_FALSE(thread->in_exception()); | 
 |   EXPECT_EQ(resolution, ExceptionHandle::Resolution::kHandled); | 
 |   EXPECT_EQ(exception_strategy, debug_ipc::ExceptionStrategy::kNone); | 
 |  | 
 |   resolution = ExceptionHandle::Resolution::kTryNext; | 
 |   exception_strategy = debug_ipc::ExceptionStrategy::kNone; | 
 |   exception = std::make_unique<MockExceptionHandle>( | 
 |       [&resolution](ExceptionHandle::Resolution new_res) { resolution = new_res; }, | 
 |       [&exception_strategy](debug_ipc::ExceptionStrategy new_strategy) { | 
 |         exception_strategy = new_strategy; | 
 |       }); | 
 |   thread->set_exception_handle(std::move(exception)); | 
 |   EXPECT_TRUE(thread->in_exception()); | 
 |   thread->ClientResume( | 
 |       debug_ipc::ResumeRequest{.how = debug_ipc::ResumeRequest::How::kForwardAndContinue}); | 
 |   EXPECT_FALSE(thread->in_exception()); | 
 |   EXPECT_EQ(resolution, ExceptionHandle::Resolution::kTryNext); | 
 |   EXPECT_EQ(exception_strategy, debug_ipc::ExceptionStrategy::kSecondChance); | 
 | } | 
 |  | 
 | TEST(DebuggedThread, OnException) { | 
 |   MockDebugAgentHarness harness; | 
 |   RemoteAPI* remote_api = harness.debug_agent(); | 
 |  | 
 |   constexpr zx_koid_t kProcessKoid = 0x8723456; | 
 |   MockProcess process(harness.debug_agent(), kProcessKoid); | 
 |  | 
 |   constexpr zx_koid_t kThreadKoid = 0x8723457; | 
 |   MockThread* thread = process.AddThread(kThreadKoid); | 
 |   EXPECT_FALSE(thread->in_exception()); | 
 |  | 
 |   // Policy: general exceptions initially handled as first-chance. | 
 |   // Exception: general, first-chance. | 
 |   // Expected: no applied strategy. | 
 |   { | 
 |     debug_ipc::ExceptionStrategy applied_strategy = debug_ipc::ExceptionStrategy::kNone; | 
 |     auto exception = std::make_unique<MockExceptionHandle>( | 
 |         [](ExceptionHandle::Resolution) {}, | 
 |         [&applied_strategy](debug_ipc::ExceptionStrategy new_strategy) { | 
 |           applied_strategy = new_strategy; | 
 |         }); | 
 |     exception->set_type(debug_ipc::ExceptionType::kGeneral); | 
 |     exception->SetStrategy(debug_ipc::ExceptionStrategy::kFirstChance); | 
 |     applied_strategy = debug_ipc::ExceptionStrategy::kNone;  // Clear previously set. | 
 |  | 
 |     thread->OnException(std::move(exception)); | 
 |  | 
 |     EXPECT_EQ(debug_ipc::ExceptionStrategy::kNone, applied_strategy); | 
 |   } | 
 |  | 
 |   // Policy: general exceptions initially handled as first-chance. | 
 |   // Exception: general, second-chance. | 
 |   // Expected: no applied strategy (as this isn't our initial handling). | 
 |   { | 
 |     debug_ipc::ExceptionStrategy applied_strategy = debug_ipc::ExceptionStrategy::kNone; | 
 |     auto exception = std::make_unique<MockExceptionHandle>( | 
 |         [](ExceptionHandle::Resolution) {}, | 
 |         [&applied_strategy](debug_ipc::ExceptionStrategy new_strategy) { | 
 |           applied_strategy = new_strategy; | 
 |         }); | 
 |     exception->set_type(debug_ipc::ExceptionType::kGeneral); | 
 |     exception->SetStrategy(debug_ipc::ExceptionStrategy::kSecondChance); | 
 |     applied_strategy = debug_ipc::ExceptionStrategy::kNone;  // Clear previously set. | 
 |  | 
 |     thread->OnException(std::move(exception)); | 
 |  | 
 |     EXPECT_EQ(debug_ipc::ExceptionStrategy::kNone, applied_strategy); | 
 |   } | 
 |  | 
 |   // Update policy so that general exceptions are handled initially as | 
 |   // second-chance. | 
 |   const debug_ipc::UpdateGlobalSettingsRequest request = { | 
 |       .exception_strategies = | 
 |           { | 
 |               { | 
 |                   .type = debug_ipc::ExceptionType::kGeneral, | 
 |                   .value = debug_ipc::ExceptionStrategy::kSecondChance, | 
 |               }, | 
 |           }, | 
 |   }; | 
 |   debug_ipc::UpdateGlobalSettingsReply reply; | 
 |   remote_api->OnUpdateGlobalSettings(request, &reply); | 
 |   EXPECT_EQ(ZX_OK, reply.status); | 
 |  | 
 |   // Policy: general exceptions initially handled as second-chance. | 
 |   // Exception: general, first-chance. | 
 |   // Expected: applied strategy of second-chance. | 
 |   { | 
 |     debug_ipc::ExceptionStrategy applied_strategy = debug_ipc::ExceptionStrategy::kNone; | 
 |     auto exception = std::make_unique<MockExceptionHandle>( | 
 |         [](ExceptionHandle::Resolution) {}, | 
 |         [&applied_strategy](debug_ipc::ExceptionStrategy new_strategy) { | 
 |           applied_strategy = new_strategy; | 
 |         }); | 
 |     exception->set_type(debug_ipc::ExceptionType::kGeneral); | 
 |     exception->SetStrategy(debug_ipc::ExceptionStrategy::kFirstChance); | 
 |     applied_strategy = debug_ipc::ExceptionStrategy::kNone;  // Clear previously set. | 
 |  | 
 |     thread->OnException(std::move(exception)); | 
 |  | 
 |     EXPECT_EQ(debug_ipc::ExceptionStrategy::kSecondChance, applied_strategy); | 
 |     // Since we didn't handle the exception, we expect it to have been closed. | 
 |     EXPECT_EQ(nullptr, thread->exception_handle()); | 
 |   } | 
 |  | 
 |   // Policy: general exceptions initially handled as second-chance. | 
 |   // Exception: general, second-chance. | 
 |   // Expected: no applied strategy. | 
 |   { | 
 |     debug_ipc::ExceptionStrategy applied_strategy = debug_ipc::ExceptionStrategy::kNone; | 
 |     auto exception = std::make_unique<MockExceptionHandle>( | 
 |         [](ExceptionHandle::Resolution) {}, | 
 |         [&applied_strategy](debug_ipc::ExceptionStrategy new_strategy) { | 
 |           applied_strategy = new_strategy; | 
 |         }); | 
 |     exception->set_type(debug_ipc::ExceptionType::kGeneral); | 
 |     exception->SetStrategy(debug_ipc::ExceptionStrategy::kSecondChance); | 
 |     applied_strategy = debug_ipc::ExceptionStrategy::kNone;  // Clear previously set. | 
 |  | 
 |     thread->OnException(std::move(exception)); | 
 |  | 
 |     EXPECT_EQ(debug_ipc::ExceptionStrategy::kNone, applied_strategy); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | }  // namespace debug_agent |