diff options
author | Pavel Labath <labath@google.com> | 2016-08-09 12:04:46 +0000 |
---|---|---|
committer | Pavel Labath <labath@google.com> | 2016-08-09 12:04:46 +0000 |
commit | 8c1b6bd7d2ac9c4a1a017405959185978204c5d5 (patch) | |
tree | ec8abb4d98f22b4248087064de8b6532305a7b7b /lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp | |
parent | 0e0e07f436f939b844ada9671f7ce38e62ecb69b (diff) | |
download | bcm5719-llvm-8c1b6bd7d2ac9c4a1a017405959185978204c5d5.tar.gz bcm5719-llvm-8c1b6bd7d2ac9c4a1a017405959185978204c5d5.zip |
Reapply "Rewrite gdb-remote's SendContinuePacketAndWaitForResponse"
Resumbitting the commit after fixing the following problems:
- broken unit tests on windows: incorrect gtest usage on my part (TEST vs. TEST_F)
- the new code did not correctly handle the case where we went to interrupt the process, but it
stopped due to a different reason - the interrupt request would remain queued and would
interfere with the following "continue". I also added a unit test for this case.
This reapplies r277156 and r277139.
llvm-svn: 278118
Diffstat (limited to 'lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp')
-rw-r--r-- | lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp new file mode 100644 index 00000000000..be67114b332 --- /dev/null +++ b/lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp @@ -0,0 +1,428 @@ +//===-- 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 "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<Error> 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<ConnectionFileDescriptor> 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_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()); +} |