xref: /llvm-project/lldb/unittests/tools/lldb-server/tests/TestClient.cpp (revision c1dff7152592f1beee9059ee8e2cb3cc68baea4d)
1 //===-- TestClient.cpp ----------------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "TestClient.h"
10 #include "TestingSupport/Host/SocketTestUtilities.h"
11 #include "lldb/Host/HostInfo.h"
12 #include "lldb/Host/common/TCPSocket.h"
13 #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
14 #include "lldb/Utility/Args.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/Support/Path.h"
17 #include "llvm/Testing/Support/Error.h"
18 #include "gtest/gtest.h"
19 #include <cstdlib>
20 #include <future>
21 #include <sstream>
22 #include <string>
23 
24 using namespace lldb;
25 using namespace lldb_private;
26 using namespace llvm;
27 using namespace llgs_tests;
28 
29 static std::chrono::seconds GetDefaultTimeout() {
30   return std::chrono::seconds{10};
31 }
32 
33 TestClient::TestClient(std::unique_ptr<Connection> Conn) {
34   SetConnection(std::move(Conn));
35   SetPacketTimeout(GetDefaultTimeout());
36 }
37 
38 TestClient::~TestClient() {
39   if (!IsConnected())
40     return;
41 
42   EXPECT_THAT_ERROR(SendMessage("k"), Succeeded());
43 }
44 
45 Error TestClient::initializeConnection() {
46   if (SendAck() == 0)
47     return make_error<StringError>("Sending initial ACK failed.",
48                                    inconvertibleErrorCode());
49 
50   if (Error E = SendMessage("QStartNoAckMode"))
51     return E;
52 
53   m_send_acks = false;
54   return Error::success();
55 }
56 
57 Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log) {
58   return launch(Log, {});
59 }
60 
61 Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log, ArrayRef<StringRef> InferiorArgs) {
62   return launchCustom(Log, false, {}, InferiorArgs);
63 }
64 
65 Expected<std::unique_ptr<TestClient>>
66 TestClient::launchCustom(StringRef Log, bool disable_stdio,
67                          ArrayRef<StringRef> ServerArgs,
68                          ArrayRef<StringRef> InferiorArgs) {
69   const ArchSpec &arch_spec = HostInfo::GetArchitecture();
70   Args args;
71   args.AppendArgument(LLDB_SERVER);
72   if (IsLldbServer())
73     args.AppendArgument("gdbserver");
74   args.AppendArgument("--reverse-connect");
75 
76   if (!Log.empty()) {
77     args.AppendArgument(("--log-file=" + Log).str());
78     if (IsLldbServer())
79       args.AppendArgument("--log-channels=gdb-remote packets");
80     else
81       args.AppendArgument("--log-flags=0x800000");
82   }
83 
84   auto LocalhostIPOrErr = GetLocalhostIP();
85   if (!LocalhostIPOrErr)
86     return LocalhostIPOrErr.takeError();
87   const std::string &LocalhostIP = *LocalhostIPOrErr;
88 
89   Status status;
90   TCPSocket listen_socket(true);
91   status = listen_socket.Listen(LocalhostIP + ":0", 5);
92   if (status.Fail())
93     return status.ToError();
94 
95   args.AppendArgument(
96       formatv("{0}:{1}", LocalhostIP, listen_socket.GetLocalPortNumber())
97           .str());
98 
99   for (StringRef arg : ServerArgs)
100     args.AppendArgument(arg);
101 
102   if (!InferiorArgs.empty()) {
103     args.AppendArgument("--");
104     for (StringRef arg : InferiorArgs)
105       args.AppendArgument(arg);
106   }
107 
108   ProcessLaunchInfo Info;
109   Info.SetArchitecture(arch_spec);
110   Info.SetArguments(args, true);
111   Info.GetEnvironment() = Host::GetEnvironment();
112   // TODO: Use this callback to detect botched launches. If lldb-server does not
113   // start, we can print a nice error message here instead of hanging in
114   // Accept().
115   Info.SetMonitorProcessCallback(&ProcessLaunchInfo::NoOpMonitorCallback);
116 
117   if (disable_stdio)
118     Info.GetFlags().Set(lldb::eLaunchFlagDisableSTDIO);
119   status = Host::LaunchProcess(Info);
120   if (status.Fail())
121     return status.ToError();
122 
123   Socket *accept_socket;
124   if (llvm::Error E =
125           listen_socket.Accept(2 * GetDefaultTimeout(), accept_socket)
126               .takeError())
127     return E;
128   auto Conn = std::make_unique<ConnectionFileDescriptor>(accept_socket);
129   auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn)));
130 
131   if (Error E = Client->initializeConnection())
132     return std::move(E);
133 
134   if (!InferiorArgs.empty()) {
135     if (Error E = Client->queryProcess())
136       return std::move(E);
137   }
138 
139   return std::move(Client);
140 }
141 
142 Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) {
143   if (SendEnvironment(Host::GetEnvironment()) != 0) {
144     return make_error<StringError>("Failed to set launch environment",
145                                    inconvertibleErrorCode());
146   }
147   std::stringstream command;
148   command << "A";
149   for (size_t i = 0; i < inferior_args.size(); i++) {
150     if (i > 0)
151       command << ',';
152     std::string hex_encoded = toHex(inferior_args[i]);
153     command << hex_encoded.size() << ',' << i << ',' << hex_encoded;
154   }
155 
156   if (Error E = SendMessage(command.str()))
157     return E;
158   if (Error E = SendMessage("qLaunchSuccess"))
159     return E;
160   if (Error E = queryProcess())
161     return E;
162   return Error::success();
163 }
164 
165 Error TestClient::ListThreadsInStopReply() {
166   return SendMessage("QListThreadsInStopReply");
167 }
168 
169 Error TestClient::SetBreakpoint(unsigned long address) {
170   return SendMessage(formatv("Z0,{0:x-},1", address).str());
171 }
172 
173 Error TestClient::ContinueAll() { return Continue("vCont;c"); }
174 
175 Error TestClient::ContinueThread(unsigned long thread_id) {
176   return Continue(formatv("vCont;c:{0:x-}", thread_id).str());
177 }
178 
179 const llgs_tests::ProcessInfo &TestClient::GetProcessInfo() {
180   return *m_process_info;
181 }
182 
183 Expected<JThreadsInfo> TestClient::GetJThreadsInfo() {
184   return SendMessage<JThreadsInfo>("jThreadsInfo", m_register_infos);
185 }
186 
187 const StopReply &TestClient::GetLatestStopReply() {
188   assert(m_stop_reply);
189   return *m_stop_reply;
190 }
191 
192 Error TestClient::SendMessage(StringRef message) {
193   std::string dummy_string;
194   return SendMessage(message, dummy_string);
195 }
196 
197 Error TestClient::SendMessage(StringRef message, std::string &response_string) {
198   if (Error E = SendMessage(message, response_string, PacketResult::Success))
199     return E;
200   StringExtractorGDBRemote Extractor(response_string);
201   if (Extractor.IsErrorResponse())
202     return Extractor.GetStatus().ToError();
203   return Error::success();
204 }
205 
206 Error TestClient::SendMessage(StringRef message, std::string &response_string,
207                               PacketResult expected_result) {
208   StringExtractorGDBRemote response;
209   GTEST_LOG_(INFO) << "Send Packet: " << message.str();
210   PacketResult result = SendPacketAndWaitForResponse(message, response);
211   response.GetEscapedBinaryData(response_string);
212   GTEST_LOG_(INFO) << "Read Packet: " << response_string;
213   if (result != expected_result)
214     return make_error<StringError>(
215         formatv("Error sending message `{0}`: {1}", message, result).str(),
216         inconvertibleErrorCode());
217 
218   return Error::success();
219 }
220 
221 unsigned int TestClient::GetPcRegisterId() {
222   assert(m_pc_register != LLDB_INVALID_REGNUM);
223   return m_pc_register;
224 }
225 
226 Error TestClient::qProcessInfo() {
227   m_process_info = std::nullopt;
228   auto InfoOr = SendMessage<ProcessInfo>("qProcessInfo");
229   if (!InfoOr)
230     return InfoOr.takeError();
231   m_process_info = std::move(*InfoOr);
232   return Error::success();
233 }
234 
235 Error TestClient::qRegisterInfos() {
236   uint32_t reg_offset = 0;
237   for (unsigned int Reg = 0;; ++Reg) {
238     std::string Message = formatv("qRegisterInfo{0:x-}", Reg).str();
239     Expected<RegisterInfo> InfoOr = SendMessage<RegisterInfoParser>(Message);
240     if (!InfoOr) {
241       consumeError(InfoOr.takeError());
242       break;
243     }
244     m_register_infos.emplace_back(std::move(*InfoOr));
245 
246     if (m_register_infos[Reg].byte_offset == LLDB_INVALID_INDEX32)
247       m_register_infos[Reg].byte_offset = reg_offset;
248 
249     reg_offset =
250         m_register_infos[Reg].byte_offset + m_register_infos[Reg].byte_size;
251     if (m_register_infos[Reg].kinds[eRegisterKindGeneric] ==
252         LLDB_REGNUM_GENERIC_PC)
253       m_pc_register = Reg;
254   }
255   if (m_pc_register == LLDB_INVALID_REGNUM)
256     return make_parsing_error("qRegisterInfo: generic");
257   return Error::success();
258 }
259 
260 Error TestClient::queryProcess() {
261   if (Error E = qProcessInfo())
262     return E;
263   if (Error E = qRegisterInfos())
264     return E;
265   return Error::success();
266 }
267 
268 Error TestClient::Continue(StringRef message) {
269   assert(m_process_info);
270 
271   auto StopReplyOr = SendMessage<StopReply>(
272       message, m_process_info->GetEndian(), m_register_infos);
273   if (!StopReplyOr)
274     return StopReplyOr.takeError();
275 
276   m_stop_reply = std::move(*StopReplyOr);
277   if (!isa<StopReplyStop>(m_stop_reply)) {
278     StringExtractorGDBRemote R;
279     PacketResult result = ReadPacket(R, GetPacketTimeout(), false);
280     if (result != PacketResult::ErrorDisconnected) {
281       return make_error<StringError>(
282           formatv("Expected connection close after sending {0}. Got {1}/{2} "
283                   "instead.",
284                   message, result, R.GetStringRef())
285               .str(),
286           inconvertibleErrorCode());
287     }
288   }
289   return Error::success();
290 }
291