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
24using namespace lldb;
25using namespace lldb_private;
26using namespace llvm;
27using namespace llgs_tests;
28
29#ifdef SendMessage
30#undef SendMessage
31#endif
32
33TestClient::TestClient(std::unique_ptr<Connection> Conn) {
34 SetConnection(std::move(Conn));
35 SetPacketTimeout(std::chrono::seconds(10));
36}
37
38TestClient::~TestClient() {
39 if (!IsConnected())
40 return;
41
42 EXPECT_THAT_ERROR(SendMessage("k"), Succeeded());
43}
44
45Error TestClient::initializeConnection() {
46 if (SendAck() == 0)
47 return make_error<StringError>(Args: "Sending initial ACK failed.",
48 Args: inconvertibleErrorCode());
49
50 if (Error E = SendMessage(message: "QStartNoAckMode"))
51 return E;
52
53 m_send_acks = false;
54 return Error::success();
55}
56
57Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log) {
58 return launch(Log, InferiorArgs: {});
59}
60
61Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log, ArrayRef<StringRef> InferiorArgs) {
62 return launchCustom(Log, disable_stdio: false, ServerArgs: {}, InferiorArgs);
63}
64
65Expected<std::unique_ptr<TestClient>>
66TestClient::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(arg_str: "gdbserver");
74 args.AppendArgument(arg_str: "--reverse-connect");
75
76 if (!Log.empty()) {
77 args.AppendArgument(arg_str: ("--log-file=" + Log).str());
78 if (IsLldbServer())
79 args.AppendArgument(arg_str: "--log-channels=gdb-remote packets");
80 else
81 args.AppendArgument(arg_str: "--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, false);
91 status = listen_socket.Listen(name: LocalhostIP + ":0", backlog: 5);
92 if (status.Fail())
93 return status.ToError();
94
95 args.AppendArgument(
96 arg_str: formatv(Fmt: "{0}:{1}", Vals: LocalhostIP, Vals: listen_socket.GetLocalPortNumber())
97 .str());
98
99 for (StringRef arg : ServerArgs)
100 args.AppendArgument(arg_str: arg);
101
102 if (!InferiorArgs.empty()) {
103 args.AppendArgument(arg_str: "--");
104 for (StringRef arg : InferiorArgs)
105 args.AppendArgument(arg_str: arg);
106 }
107
108 ProcessLaunchInfo Info;
109 Info.SetArchitecture(arch_spec);
110 Info.SetArguments(args, first_arg_is_executable: 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(launch_info&: Info);
120 if (status.Fail())
121 return status.ToError();
122
123 Socket *accept_socket;
124 listen_socket.Accept(conn_socket&: accept_socket);
125 auto Conn = std::make_unique<ConnectionFileDescriptor>(args&: accept_socket);
126 auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn)));
127
128 if (Error E = Client->initializeConnection())
129 return std::move(E);
130
131 if (!InferiorArgs.empty()) {
132 if (Error E = Client->queryProcess())
133 return std::move(E);
134 }
135
136 return std::move(Client);
137}
138
139Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) {
140 if (SendEnvironment(env: Host::GetEnvironment()) != 0) {
141 return make_error<StringError>(Args: "Failed to set launch environment",
142 Args: inconvertibleErrorCode());
143 }
144 std::stringstream command;
145 command << "A";
146 for (size_t i = 0; i < inferior_args.size(); i++) {
147 if (i > 0)
148 command << ',';
149 std::string hex_encoded = toHex(Input: inferior_args[i]);
150 command << hex_encoded.size() << ',' << i << ',' << hex_encoded;
151 }
152
153 if (Error E = SendMessage(message: command.str()))
154 return E;
155 if (Error E = SendMessage(message: "qLaunchSuccess"))
156 return E;
157 if (Error E = queryProcess())
158 return E;
159 return Error::success();
160}
161
162Error TestClient::ListThreadsInStopReply() {
163 return SendMessage(message: "QListThreadsInStopReply");
164}
165
166Error TestClient::SetBreakpoint(unsigned long address) {
167 return SendMessage(message: formatv(Fmt: "Z0,{0:x-},1", Vals&: address).str());
168}
169
170Error TestClient::ContinueAll() { return Continue(message: "vCont;c"); }
171
172Error TestClient::ContinueThread(unsigned long thread_id) {
173 return Continue(message: formatv(Fmt: "vCont;c:{0:x-}", Vals&: thread_id).str());
174}
175
176const llgs_tests::ProcessInfo &TestClient::GetProcessInfo() {
177 return *m_process_info;
178}
179
180Expected<JThreadsInfo> TestClient::GetJThreadsInfo() {
181 return SendMessage<JThreadsInfo>(Message: "jThreadsInfo", Args&: m_register_infos);
182}
183
184const StopReply &TestClient::GetLatestStopReply() {
185 assert(m_stop_reply);
186 return *m_stop_reply;
187}
188
189Error TestClient::SendMessage(StringRef message) {
190 std::string dummy_string;
191 return SendMessage(message, response_string&: dummy_string);
192}
193
194Error TestClient::SendMessage(StringRef message, std::string &response_string) {
195 if (Error E = SendMessage(message, response_string, expected_result: PacketResult::Success))
196 return E;
197 StringExtractorGDBRemote Extractor(response_string);
198 if (Extractor.IsErrorResponse())
199 return Extractor.GetStatus().ToError();
200 return Error::success();
201}
202
203Error TestClient::SendMessage(StringRef message, std::string &response_string,
204 PacketResult expected_result) {
205 StringExtractorGDBRemote response;
206 GTEST_LOG_(INFO) << "Send Packet: " << message.str();
207 PacketResult result = SendPacketAndWaitForResponse(payload: message, response);
208 response.GetEscapedBinaryData(str&: response_string);
209 GTEST_LOG_(INFO) << "Read Packet: " << response_string;
210 if (result != expected_result)
211 return make_error<StringError>(
212 Args: formatv(Fmt: "Error sending message `{0}`: {1}", Vals&: message, Vals&: result).str(),
213 Args: inconvertibleErrorCode());
214
215 return Error::success();
216}
217
218unsigned int TestClient::GetPcRegisterId() {
219 assert(m_pc_register != LLDB_INVALID_REGNUM);
220 return m_pc_register;
221}
222
223Error TestClient::qProcessInfo() {
224 m_process_info = std::nullopt;
225 auto InfoOr = SendMessage<ProcessInfo>(Message: "qProcessInfo");
226 if (!InfoOr)
227 return InfoOr.takeError();
228 m_process_info = std::move(*InfoOr);
229 return Error::success();
230}
231
232Error TestClient::qRegisterInfos() {
233 uint32_t reg_offset = 0;
234 for (unsigned int Reg = 0;; ++Reg) {
235 std::string Message = formatv(Fmt: "qRegisterInfo{0:x-}", Vals&: Reg).str();
236 Expected<RegisterInfo> InfoOr = SendMessage<RegisterInfoParser>(Message);
237 if (!InfoOr) {
238 consumeError(Err: InfoOr.takeError());
239 break;
240 }
241 m_register_infos.emplace_back(args: std::move(*InfoOr));
242
243 if (m_register_infos[Reg].byte_offset == LLDB_INVALID_INDEX32)
244 m_register_infos[Reg].byte_offset = reg_offset;
245
246 reg_offset =
247 m_register_infos[Reg].byte_offset + m_register_infos[Reg].byte_size;
248 if (m_register_infos[Reg].kinds[eRegisterKindGeneric] ==
249 LLDB_REGNUM_GENERIC_PC)
250 m_pc_register = Reg;
251 }
252 if (m_pc_register == LLDB_INVALID_REGNUM)
253 return make_parsing_error(format: "qRegisterInfo: generic");
254 return Error::success();
255}
256
257Error TestClient::queryProcess() {
258 if (Error E = qProcessInfo())
259 return E;
260 if (Error E = qRegisterInfos())
261 return E;
262 return Error::success();
263}
264
265Error TestClient::Continue(StringRef message) {
266 assert(m_process_info);
267
268 auto StopReplyOr = SendMessage<StopReply>(
269 Message: message, Args: m_process_info->GetEndian(), Args&: m_register_infos);
270 if (!StopReplyOr)
271 return StopReplyOr.takeError();
272
273 m_stop_reply = std::move(*StopReplyOr);
274 if (!isa<StopReplyStop>(Val: m_stop_reply)) {
275 StringExtractorGDBRemote R;
276 PacketResult result = ReadPacket(response&: R, timeout: GetPacketTimeout(), sync_on_timeout: false);
277 if (result != PacketResult::ErrorDisconnected) {
278 return make_error<StringError>(
279 Args: formatv(Fmt: "Expected connection close after sending {0}. Got {1}/{2} "
280 "instead.",
281 Vals&: message, Vals&: result, Vals: R.GetStringRef())
282 .str(),
283 Args: inconvertibleErrorCode());
284 }
285 }
286 return Error::success();
287}
288

source code of lldb/unittests/tools/lldb-server/tests/TestClient.cpp