| // 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_TRUE(reply.status.ok()); |
| |
| // 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 |