//===-- 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 when // exceptions are disabled. #include #endif #include #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 "lldb/Host/common/TCPSocket.h" #include "lldb/Host/posix/ConnectionFileDescriptorPosix.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; } }; struct MockServer : public GDBRemoteCommunicationServer { MockServer() : GDBRemoteCommunicationServer("mock-server", "mock-server.listener") { m_send_acks = false; } bool GetThreadSuffixSupported() override { return false; } PacketResult SendPacket(llvm::StringRef payload) { return GDBRemoteCommunicationServer::SendPacketNoLock(payload.data(), payload.size()); } PacketResult GetPacket(StringExtractorGDBRemote &response) { const unsigned timeout_usec = 1000000; // 1s const bool sync_on_timeout = false; return WaitForPacketWithTimeoutMicroSecondsNoLock(response, timeout_usec, sync_on_timeout); } }; struct TestClient : public GDBRemoteClientBase { TestClient() : GDBRemoteClientBase("test.client", "test.client.listener") { m_send_acks = false; } bool GetThreadSuffixSupported() override { return 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")) { bool child_processes_inherit = false; Error error; TCPSocket listen_socket(child_processes_inherit, error); EXPECT_FALSE(error.Fail()); error = listen_socket.Listen("127.0.0.1:0", 5); EXPECT_FALSE(error.Fail()); Socket *accept_socket; std::future accept_error = std::async(std::launch::async, [&] { return listen_socket.Accept("127.0.0.1:0", child_processes_inherit, accept_socket); }); char connect_remote_address[64]; snprintf(connect_remote_address, sizeof(connect_remote_address), "connect://localhost:%u", listen_socket.GetLocalPortNumber()); std::unique_ptr conn_ap(new ConnectionFileDescriptor()); EXPECT_EQ(conn_ap->Connect(connect_remote_address, nullptr), lldb::eConnectionStatusSuccess); client.SetConnection(conn_ap.release()); EXPECT_TRUE(accept_error.get().Success()); server.SetConnection(new ConnectionFileDescriptor(accept_socket)); listener_sp->StartListeningForEvents(&client, TestClient::eBroadcastBitRunPacketSent); } } // end anonymous namespace class GDBRemoteClientBaseTest : public testing::Test { public: static void SetUpTestCase() { #if defined(_MSC_VER) WSADATA data; ::WSAStartup(MAKEWORD(2, 2), &data); #endif } static void TearDownTestCase() { #if defined(_MSC_VER) ::WSACleanup(); #endif } }; TEST(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(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 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 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(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 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 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(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 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 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(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 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 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 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(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(GDBRemoteClientBaseTest, InterruptNoResponse) { StringExtractorGDBRemote continue_response, response; ContinueFixture fix; if (HasFailure()) return; // Continue. After the run packet is sent, send an interrupt. std::future 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 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()); }