summaryrefslogtreecommitdiffstats
path: root/lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp
diff options
context:
space:
mode:
authorPavel Labath <labath@google.com>2016-08-09 12:04:46 +0000
committerPavel Labath <labath@google.com>2016-08-09 12:04:46 +0000
commit8c1b6bd7d2ac9c4a1a017405959185978204c5d5 (patch)
treeec8abb4d98f22b4248087064de8b6532305a7b7b /lldb/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp
parent0e0e07f436f939b844ada9671f7ce38e62ecb69b (diff)
downloadbcm5719-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.cpp428
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());
+}
OpenPOWER on IntegriCloud