| //===-- GDBRemoteClientBaseTest.cpp -----------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #if defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) |
| // Workaround for MSVC standard library bug, which fails to include <thread> |
| // when |
| // exceptions are disabled. |
| #include <eh.h> |
| #endif |
| #include <future> |
| |
| #include "GDBRemoteTestUtils.h" |
| #include "gtest/gtest.h" |
| |
| #include "Plugins/Process/Utility/LinuxSignals.h" |
| #include "Plugins/Process/gdb-remote/GDBRemoteClientBase.h" |
| #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h" |
| |
| #include "llvm/ADT/STLExtras.h" |
| |
| using namespace lldb_private::process_gdb_remote; |
| using namespace lldb_private; |
| using namespace lldb; |
| typedef GDBRemoteCommunication::PacketResult PacketResult; |
| |
| namespace { |
| |
| struct MockDelegate : public GDBRemoteClientBase::ContinueDelegate { |
| std::string output; |
| std::string misc_data; |
| unsigned stop_reply_called = 0; |
| |
| void HandleAsyncStdout(llvm::StringRef out) { output += out; } |
| void HandleAsyncMisc(llvm::StringRef data) { misc_data += data; } |
| void HandleStopReply() { ++stop_reply_called; } |
| |
| bool HandleAsyncStructuredData(const StructuredData::ObjectSP &object_sp) { |
| // TODO work in a test here after I fix the gtest breakage. |
| return true; |
| } |
| }; |
| |
| struct TestClient : public GDBRemoteClientBase { |
| TestClient() : GDBRemoteClientBase("test.client", "test.client.listener") { |
| m_send_acks = false; |
| } |
| }; |
| |
| struct ContinueFixture { |
| MockDelegate delegate; |
| TestClient client; |
| MockServer server; |
| ListenerSP listener_sp; |
| |
| ContinueFixture(); |
| |
| StateType SendCPacket(StringExtractorGDBRemote &response) { |
| return client.SendContinuePacketAndWaitForResponse(delegate, LinuxSignals(), |
| "c", response); |
| } |
| |
| void WaitForRunEvent() { |
| EventSP event_sp; |
| listener_sp->WaitForEventForBroadcasterWithType( |
| std::chrono::microseconds(0), &client, |
| TestClient::eBroadcastBitRunPacketSent, event_sp); |
| } |
| }; |
| |
| ContinueFixture::ContinueFixture() |
| : listener_sp(Listener::MakeListener("listener")) { |
| Connect(client, server); |
| listener_sp->StartListeningForEvents(&client, |
| TestClient::eBroadcastBitRunPacketSent); |
| } |
| |
| } // end anonymous namespace |
| |
| class GDBRemoteClientBaseTest : public GDBRemoteTest {}; |
| |
| TEST_F(GDBRemoteClientBaseTest, SendContinueAndWait) { |
| StringExtractorGDBRemote response; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // Continue. The inferior will stop with a signal. |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); |
| ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); |
| ASSERT_EQ("T01", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| |
| // Continue. The inferior will exit. |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("W01")); |
| ASSERT_EQ(eStateExited, fix.SendCPacket(response)); |
| ASSERT_EQ("W01", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| |
| // Continue. The inferior will get killed. |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("X01")); |
| ASSERT_EQ(eStateExited, fix.SendCPacket(response)); |
| ASSERT_EQ("X01", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| } |
| |
| TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncSignal) { |
| StringExtractorGDBRemote continue_response, response; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // SendAsyncSignal should do nothing when we are not running. |
| ASSERT_FALSE(fix.client.SendAsyncSignal(0x47)); |
| |
| // Continue. After the run packet is sent, send an async signal. |
| std::future<StateType> continue_state = std::async( |
| std::launch::async, [&] { return fix.SendCPacket(continue_response); }); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| fix.WaitForRunEvent(); |
| |
| std::future<bool> async_result = std::async( |
| std::launch::async, [&] { return fix.client.SendAsyncSignal(0x47); }); |
| |
| // First we'll get interrupted. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("\x03", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); |
| |
| // Then we get the signal packet. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("C47", response.GetStringRef()); |
| ASSERT_TRUE(async_result.get()); |
| |
| // And we report back a signal stop. |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T47")); |
| ASSERT_EQ(eStateStopped, continue_state.get()); |
| ASSERT_EQ("T47", continue_response.GetStringRef()); |
| } |
| |
| TEST_F(GDBRemoteClientBaseTest, SendContinueAndAsyncPacket) { |
| StringExtractorGDBRemote continue_response, async_response, response; |
| const bool send_async = true; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // Continue. After the run packet is sent, send an async packet. |
| std::future<StateType> continue_state = std::async( |
| std::launch::async, [&] { return fix.SendCPacket(continue_response); }); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| fix.WaitForRunEvent(); |
| |
| // Sending without async enabled should fail. |
| ASSERT_EQ( |
| PacketResult::ErrorSendFailed, |
| fix.client.SendPacketAndWaitForResponse("qTest1", response, !send_async)); |
| |
| std::future<PacketResult> async_result = std::async(std::launch::async, [&] { |
| return fix.client.SendPacketAndWaitForResponse("qTest2", async_response, |
| send_async); |
| }); |
| |
| // First we'll get interrupted. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("\x03", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); |
| |
| // Then we get the async packet. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("qTest2", response.GetStringRef()); |
| |
| // Send the response and receive it. |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest2")); |
| ASSERT_EQ(PacketResult::Success, async_result.get()); |
| ASSERT_EQ("QTest2", async_response.GetStringRef()); |
| |
| // And we get resumed again. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); |
| ASSERT_EQ(eStateStopped, continue_state.get()); |
| ASSERT_EQ("T01", continue_response.GetStringRef()); |
| } |
| |
| TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt) { |
| StringExtractorGDBRemote continue_response, response; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // Interrupt should do nothing when we're not running. |
| ASSERT_FALSE(fix.client.Interrupt()); |
| |
| // Continue. After the run packet is sent, send an interrupt. |
| std::future<StateType> continue_state = std::async( |
| std::launch::async, [&] { return fix.SendCPacket(continue_response); }); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| fix.WaitForRunEvent(); |
| |
| std::future<bool> async_result = |
| std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); |
| |
| // We get interrupted. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("\x03", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); |
| |
| // And that's it. |
| ASSERT_EQ(eStateStopped, continue_state.get()); |
| ASSERT_EQ("T13", continue_response.GetStringRef()); |
| ASSERT_TRUE(async_result.get()); |
| } |
| |
| TEST_F(GDBRemoteClientBaseTest, SendContinueAndLateInterrupt) { |
| StringExtractorGDBRemote continue_response, response; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // Continue. After the run packet is sent, send an interrupt. |
| std::future<StateType> continue_state = std::async( |
| std::launch::async, [&] { return fix.SendCPacket(continue_response); }); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| fix.WaitForRunEvent(); |
| |
| std::future<bool> async_result = |
| std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); |
| |
| // However, the target stops due to a different reason than the original |
| // interrupt. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("\x03", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); |
| ASSERT_EQ(eStateStopped, continue_state.get()); |
| ASSERT_EQ("T01", continue_response.GetStringRef()); |
| ASSERT_TRUE(async_result.get()); |
| |
| // The subsequent continue packet should work normally. |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); |
| ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); |
| ASSERT_EQ("T01", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| } |
| |
| TEST_F(GDBRemoteClientBaseTest, SendContinueAndInterrupt2PacketBug) { |
| StringExtractorGDBRemote continue_response, async_response, response; |
| const bool send_async = true; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // Interrupt should do nothing when we're not running. |
| ASSERT_FALSE(fix.client.Interrupt()); |
| |
| // Continue. After the run packet is sent, send an async signal. |
| std::future<StateType> continue_state = std::async( |
| std::launch::async, [&] { return fix.SendCPacket(continue_response); }); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| fix.WaitForRunEvent(); |
| |
| std::future<bool> interrupt_result = |
| std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); |
| |
| // We get interrupted. We'll send two packets to simulate a buggy stub. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("\x03", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T13")); |
| |
| // We should stop. |
| ASSERT_EQ(eStateStopped, continue_state.get()); |
| ASSERT_EQ("T13", continue_response.GetStringRef()); |
| ASSERT_TRUE(interrupt_result.get()); |
| |
| // Packet stream should remain synchronized. |
| std::future<PacketResult> send_result = std::async(std::launch::async, [&] { |
| return fix.client.SendPacketAndWaitForResponse("qTest", async_response, |
| !send_async); |
| }); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("qTest", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("QTest")); |
| ASSERT_EQ(PacketResult::Success, send_result.get()); |
| ASSERT_EQ("QTest", async_response.GetStringRef()); |
| } |
| |
| TEST_F(GDBRemoteClientBaseTest, SendContinueDelegateInterface) { |
| StringExtractorGDBRemote response; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // Continue. We'll have the server send a bunch of async packets before it |
| // stops. |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4142")); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Apro")); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("O4344")); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("Afile")); |
| ASSERT_EQ(PacketResult::Success, fix.server.SendPacket("T01")); |
| ASSERT_EQ(eStateStopped, fix.SendCPacket(response)); |
| ASSERT_EQ("T01", response.GetStringRef()); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| |
| EXPECT_EQ("ABCD", fix.delegate.output); |
| EXPECT_EQ("profile", fix.delegate.misc_data); |
| EXPECT_EQ(1u, fix.delegate.stop_reply_called); |
| } |
| |
| TEST_F(GDBRemoteClientBaseTest, InterruptNoResponse) { |
| StringExtractorGDBRemote continue_response, response; |
| ContinueFixture fix; |
| if (HasFailure()) |
| return; |
| |
| // Continue. After the run packet is sent, send an interrupt. |
| std::future<StateType> continue_state = std::async( |
| std::launch::async, [&] { return fix.SendCPacket(continue_response); }); |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("c", response.GetStringRef()); |
| fix.WaitForRunEvent(); |
| |
| std::future<bool> async_result = |
| std::async(std::launch::async, [&] { return fix.client.Interrupt(); }); |
| |
| // We get interrupted, but we don't send a stop packet. |
| ASSERT_EQ(PacketResult::Success, fix.server.GetPacket(response)); |
| ASSERT_EQ("\x03", response.GetStringRef()); |
| |
| // The functions should still terminate (after a timeout). |
| ASSERT_TRUE(async_result.get()); |
| ASSERT_EQ(eStateInvalid, continue_state.get()); |
| } |