Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 1 | //===-- TestClient.cpp ------------------------------------------*- C++ -*-===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "TestClient.h" |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 10 | #include "lldb/Host/HostInfo.h" |
| 11 | #include "lldb/Host/common/TCPSocket.h" |
| 12 | #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" |
Pavel Labath | 145d95c | 2018-04-17 18:53:35 +0000 | [diff] [blame] | 13 | #include "lldb/Utility/Args.h" |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 14 | #include "llvm/ADT/StringExtras.h" |
Pavel Labath | c596e90 | 2017-10-17 16:28:28 +0000 | [diff] [blame] | 15 | #include "llvm/Support/Path.h" |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 16 | #include "llvm/Testing/Support/Error.h" |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 17 | #include "gtest/gtest.h" |
| 18 | #include <cstdlib> |
| 19 | #include <future> |
| 20 | #include <sstream> |
| 21 | #include <string> |
| 22 | |
| 23 | using namespace lldb; |
| 24 | using namespace lldb_private; |
| 25 | using namespace llvm; |
Pavel Labath | a118131 | 2018-02-09 09:40:03 +0000 | [diff] [blame] | 26 | using namespace llgs_tests; |
Pavel Labath | c596e90 | 2017-10-17 16:28:28 +0000 | [diff] [blame] | 27 | |
Aaron Smith | 5146a9e | 2019-08-13 22:18:01 +0000 | [diff] [blame] | 28 | #ifdef SendMessage |
| 29 | #undef SendMessage |
| 30 | #endif |
| 31 | |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 32 | TestClient::TestClient(std::unique_ptr<Connection> Conn) { |
| 33 | SetConnection(Conn.release()); |
Pavel Labath | 691d134 | 2018-04-23 11:22:44 +0000 | [diff] [blame] | 34 | SetPacketTimeout(std::chrono::seconds(10)); |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 35 | } |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 36 | |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 37 | TestClient::~TestClient() { |
Pavel Labath | d8b3c1a | 2017-12-18 09:44:29 +0000 | [diff] [blame] | 38 | if (!IsConnected()) |
| 39 | return; |
| 40 | |
Pavel Labath | 5a84123 | 2018-03-28 10:19:10 +0000 | [diff] [blame] | 41 | EXPECT_THAT_ERROR(SendMessage("k"), Succeeded()); |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 42 | } |
| 43 | |
Pavel Labath | d2f36c8 | 2018-04-30 14:30:02 +0000 | [diff] [blame] | 44 | Error TestClient::initializeConnection() { |
| 45 | if (SendAck() == 0) |
| 46 | return make_error<StringError>("Sending initial ACK failed.", |
| 47 | inconvertibleErrorCode()); |
| 48 | |
| 49 | if (Error E = SendMessage("QStartNoAckMode")) |
| 50 | return E; |
| 51 | |
| 52 | m_send_acks = false; |
| 53 | return Error::success(); |
| 54 | } |
| 55 | |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 56 | Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log) { |
| 57 | return launch(Log, {}); |
| 58 | } |
| 59 | |
| 60 | Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log, ArrayRef<StringRef> InferiorArgs) { |
Pavel Labath | deb45f2 | 2017-12-22 11:09:21 +0000 | [diff] [blame] | 61 | return launchCustom(Log, {}, InferiorArgs); |
| 62 | } |
| 63 | |
| 64 | Expected<std::unique_ptr<TestClient>> TestClient::launchCustom(StringRef Log, ArrayRef<StringRef> ServerArgs, ArrayRef<StringRef> InferiorArgs) { |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 65 | const ArchSpec &arch_spec = HostInfo::GetArchitecture(); |
| 66 | Args args; |
| 67 | args.AppendArgument(LLDB_SERVER); |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 68 | if (IsLldbServer()) |
Pavel Labath | c596e90 | 2017-10-17 16:28:28 +0000 | [diff] [blame] | 69 | args.AppendArgument("gdbserver"); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 70 | args.AppendArgument("--reverse-connect"); |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 71 | |
| 72 | if (!Log.empty()) { |
| 73 | args.AppendArgument(("--log-file=" + Log).str()); |
| 74 | if (IsLldbServer()) |
| 75 | args.AppendArgument("--log-channels=gdb-remote packets"); |
| 76 | else |
| 77 | args.AppendArgument("--log-flags=0x800000"); |
| 78 | } |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 79 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 80 | Status status; |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 81 | TCPSocket listen_socket(true, false); |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 82 | status = listen_socket.Listen("127.0.0.1:0", 5); |
| 83 | if (status.Fail()) |
| 84 | return status.ToError(); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 85 | |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 86 | args.AppendArgument( |
Michal Gorny | f6e5594 | 2019-02-12 18:09:00 +0000 | [diff] [blame] | 87 | ("127.0.0.1:" + Twine(listen_socket.GetLocalPortNumber())).str()); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 88 | |
Pavel Labath | deb45f2 | 2017-12-22 11:09:21 +0000 | [diff] [blame] | 89 | for (StringRef arg : ServerArgs) |
| 90 | args.AppendArgument(arg); |
| 91 | |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 92 | if (!InferiorArgs.empty()) { |
| 93 | args.AppendArgument("--"); |
| 94 | for (StringRef arg : InferiorArgs) |
| 95 | args.AppendArgument(arg); |
| 96 | } |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 97 | |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 98 | ProcessLaunchInfo Info; |
| 99 | Info.SetArchitecture(arch_spec); |
| 100 | Info.SetArguments(args, true); |
Pavel Labath | 62930e5 | 2018-01-10 11:57:31 +0000 | [diff] [blame] | 101 | Info.GetEnvironment() = Host::GetEnvironment(); |
Pavel Labath | 245dd2e | 2018-05-15 13:42:26 +0000 | [diff] [blame] | 102 | // TODO: Use this callback to detect botched launches. If lldb-server does not |
| 103 | // start, we can print a nice error message here instead of hanging in |
| 104 | // Accept(). |
| 105 | Info.SetMonitorProcessCallback(&ProcessLaunchInfo::NoOpMonitorCallback, |
| 106 | false); |
Pavel Labath | 11e5917 | 2017-12-18 14:31:39 +0000 | [diff] [blame] | 107 | |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 108 | status = Host::LaunchProcess(Info); |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 109 | if (status.Fail()) |
| 110 | return status.ToError(); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 111 | |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 112 | Socket *accept_socket; |
| 113 | listen_socket.Accept(accept_socket); |
Jonas Devlieghere | a8f3ae7 | 2019-08-14 22:19:23 +0000 | [diff] [blame^] | 114 | auto Conn = std::make_unique<ConnectionFileDescriptor>(accept_socket); |
Pavel Labath | 11e5917 | 2017-12-18 14:31:39 +0000 | [diff] [blame] | 115 | auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn))); |
| 116 | |
Pavel Labath | d2f36c8 | 2018-04-30 14:30:02 +0000 | [diff] [blame] | 117 | if (Error E = Client->initializeConnection()) |
| 118 | return std::move(E); |
| 119 | |
Pavel Labath | 11e5917 | 2017-12-18 14:31:39 +0000 | [diff] [blame] | 120 | if (!InferiorArgs.empty()) { |
Pavel Labath | a118131 | 2018-02-09 09:40:03 +0000 | [diff] [blame] | 121 | if (Error E = Client->queryProcess()) |
Pavel Labath | 11e5917 | 2017-12-18 14:31:39 +0000 | [diff] [blame] | 122 | return std::move(E); |
| 123 | } |
| 124 | |
| 125 | return std::move(Client); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 126 | } |
| 127 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 128 | Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { |
Pavel Labath | 62930e5 | 2018-01-10 11:57:31 +0000 | [diff] [blame] | 129 | if (SendEnvironment(Host::GetEnvironment()) != 0) { |
| 130 | return make_error<StringError>("Failed to set launch environment", |
| 131 | inconvertibleErrorCode()); |
Pavel Labath | 7b9b30a | 2017-10-20 22:39:18 +0000 | [diff] [blame] | 132 | } |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 133 | std::stringstream command; |
| 134 | command << "A"; |
| 135 | for (size_t i = 0; i < inferior_args.size(); i++) { |
| 136 | if (i > 0) |
| 137 | command << ','; |
| 138 | std::string hex_encoded = toHex(inferior_args[i]); |
| 139 | command << hex_encoded.size() << ',' << i << ',' << hex_encoded; |
| 140 | } |
| 141 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 142 | if (Error E = SendMessage(command.str())) |
| 143 | return E; |
| 144 | if (Error E = SendMessage("qLaunchSuccess")) |
| 145 | return E; |
Pavel Labath | a118131 | 2018-02-09 09:40:03 +0000 | [diff] [blame] | 146 | if (Error E = queryProcess()) |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 147 | return E; |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 148 | return Error::success(); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 149 | } |
| 150 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 151 | Error TestClient::ListThreadsInStopReply() { |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 152 | return SendMessage("QListThreadsInStopReply"); |
| 153 | } |
| 154 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 155 | Error TestClient::SetBreakpoint(unsigned long address) { |
| 156 | return SendMessage(formatv("Z0,{0:x-},1", address).str()); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 157 | } |
| 158 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 159 | Error TestClient::ContinueAll() { return Continue("vCont;c"); } |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 160 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 161 | Error TestClient::ContinueThread(unsigned long thread_id) { |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 162 | return Continue(formatv("vCont;c:{0:x-}", thread_id).str()); |
| 163 | } |
| 164 | |
Pavel Labath | a118131 | 2018-02-09 09:40:03 +0000 | [diff] [blame] | 165 | const llgs_tests::ProcessInfo &TestClient::GetProcessInfo() { |
| 166 | return *m_process_info; |
| 167 | } |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 168 | |
Pavel Labath | 9a9556f | 2018-02-19 15:42:48 +0000 | [diff] [blame] | 169 | Expected<JThreadsInfo> TestClient::GetJThreadsInfo() { |
| 170 | return SendMessage<JThreadsInfo>("jThreadsInfo", m_register_infos); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | const StopReply &TestClient::GetLatestStopReply() { |
Pavel Labath | 93a582c | 2017-12-15 15:19:45 +0000 | [diff] [blame] | 174 | assert(m_stop_reply); |
| 175 | return *m_stop_reply; |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 176 | } |
| 177 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 178 | Error TestClient::SendMessage(StringRef message) { |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 179 | std::string dummy_string; |
| 180 | return SendMessage(message, dummy_string); |
| 181 | } |
| 182 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 183 | Error TestClient::SendMessage(StringRef message, std::string &response_string) { |
| 184 | if (Error E = SendMessage(message, response_string, PacketResult::Success)) |
| 185 | return E; |
Pavel Labath | a70512a | 2018-04-11 13:30:54 +0000 | [diff] [blame] | 186 | StringExtractorGDBRemote Extractor(response_string); |
| 187 | if (Extractor.IsErrorResponse()) |
| 188 | return Extractor.GetStatus().ToError(); |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 189 | return Error::success(); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 190 | } |
| 191 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 192 | Error TestClient::SendMessage(StringRef message, std::string &response_string, |
| 193 | PacketResult expected_result) { |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 194 | StringExtractorGDBRemote response; |
| 195 | GTEST_LOG_(INFO) << "Send Packet: " << message.str(); |
| 196 | PacketResult result = SendPacketAndWaitForResponse(message, response, false); |
| 197 | response.GetEscapedBinaryData(response_string); |
| 198 | GTEST_LOG_(INFO) << "Read Packet: " << response_string; |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 199 | if (result != expected_result) |
| 200 | return make_error<StringError>( |
| 201 | formatv("Error sending message `{0}`: {1}", message, result).str(), |
| 202 | inconvertibleErrorCode()); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 203 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 204 | return Error::success(); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 205 | } |
| 206 | |
| 207 | unsigned int TestClient::GetPcRegisterId() { |
Pavel Labath | a118131 | 2018-02-09 09:40:03 +0000 | [diff] [blame] | 208 | assert(m_pc_register != LLDB_INVALID_REGNUM); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 209 | return m_pc_register; |
| 210 | } |
| 211 | |
Pavel Labath | a118131 | 2018-02-09 09:40:03 +0000 | [diff] [blame] | 212 | Error TestClient::qProcessInfo() { |
| 213 | m_process_info = None; |
| 214 | auto InfoOr = SendMessage<ProcessInfo>("qProcessInfo"); |
| 215 | if (!InfoOr) |
| 216 | return InfoOr.takeError(); |
| 217 | m_process_info = std::move(*InfoOr); |
| 218 | return Error::success(); |
| 219 | } |
| 220 | |
| 221 | Error TestClient::qRegisterInfos() { |
| 222 | for (unsigned int Reg = 0;; ++Reg) { |
| 223 | std::string Message = formatv("qRegisterInfo{0:x-}", Reg).str(); |
| 224 | Expected<RegisterInfo> InfoOr = SendMessage<RegisterInfoParser>(Message); |
| 225 | if (!InfoOr) { |
| 226 | consumeError(InfoOr.takeError()); |
| 227 | break; |
| 228 | } |
| 229 | m_register_infos.emplace_back(std::move(*InfoOr)); |
| 230 | if (m_register_infos[Reg].kinds[eRegisterKindGeneric] == |
| 231 | LLDB_REGNUM_GENERIC_PC) |
| 232 | m_pc_register = Reg; |
| 233 | } |
| 234 | if (m_pc_register == LLDB_INVALID_REGNUM) |
| 235 | return make_parsing_error("qRegisterInfo: generic"); |
| 236 | return Error::success(); |
| 237 | } |
| 238 | |
| 239 | Error TestClient::queryProcess() { |
| 240 | if (Error E = qProcessInfo()) |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 241 | return E; |
Pavel Labath | a118131 | 2018-02-09 09:40:03 +0000 | [diff] [blame] | 242 | if (Error E = qRegisterInfos()) |
| 243 | return E; |
Pavel Labath | 671d3e6 | 2017-12-15 13:56:22 +0000 | [diff] [blame] | 244 | return Error::success(); |
| 245 | } |
| 246 | |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 247 | Error TestClient::Continue(StringRef message) { |
| 248 | assert(m_process_info.hasValue()); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 249 | |
Pavel Labath | cdf7a9f | 2018-03-04 02:12:18 +0000 | [diff] [blame] | 250 | auto StopReplyOr = SendMessage<StopReply>( |
| 251 | message, m_process_info->GetEndian(), m_register_infos); |
| 252 | if (!StopReplyOr) |
| 253 | return StopReplyOr.takeError(); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 254 | |
Pavel Labath | cdf7a9f | 2018-03-04 02:12:18 +0000 | [diff] [blame] | 255 | m_stop_reply = std::move(*StopReplyOr); |
Pavel Labath | d8b3c1a | 2017-12-18 09:44:29 +0000 | [diff] [blame] | 256 | if (!isa<StopReplyStop>(m_stop_reply)) { |
| 257 | StringExtractorGDBRemote R; |
| 258 | PacketResult result = ReadPacket(R, GetPacketTimeout(), false); |
| 259 | if (result != PacketResult::ErrorDisconnected) { |
| 260 | return make_error<StringError>( |
Pavel Labath | cdf7a9f | 2018-03-04 02:12:18 +0000 | [diff] [blame] | 261 | formatv("Expected connection close after sending {0}. Got {1}/{2} " |
Pavel Labath | d8b3c1a | 2017-12-18 09:44:29 +0000 | [diff] [blame] | 262 | "instead.", |
Pavel Labath | cdf7a9f | 2018-03-04 02:12:18 +0000 | [diff] [blame] | 263 | message, result, R.GetStringRef()) |
Pavel Labath | d8b3c1a | 2017-12-18 09:44:29 +0000 | [diff] [blame] | 264 | .str(), |
| 265 | inconvertibleErrorCode()); |
| 266 | } |
| 267 | } |
Pavel Labath | 1ebc85f | 2017-11-09 15:45:09 +0000 | [diff] [blame] | 268 | return Error::success(); |
Pavel Labath | 015f17d | 2017-06-06 13:40:18 +0000 | [diff] [blame] | 269 | } |