1 //===-- TestClient.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "TestClient.h"
11 #include "lldb/Core/ArchSpec.h"
12 #include "lldb/Host/HostInfo.h"
13 #include "lldb/Host/common/TCPSocket.h"
14 #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
15 #include "lldb/Interpreter/Args.h"
16 #include "lldb/Target/ProcessLaunchInfo.h"
17 #include "llvm/ADT/StringExtras.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 
28 namespace llgs_tests {
29 void TestClient::Initialize() { HostInfo::Initialize(); }
30 
31 TestClient::TestClient(const std::string &test_name,
32                        const std::string &test_case_name)
33     : m_test_name(test_name), m_test_case_name(test_case_name),
34       m_pc_register(UINT_MAX) {}
35 
36 TestClient::~TestClient() {}
37 
38 bool TestClient::StartDebugger() {
39   const ArchSpec &arch_spec = HostInfo::GetArchitecture();
40   Args args;
41   args.AppendArgument(LLDB_SERVER);
42   args.AppendArgument("gdbserver");
43   args.AppendArgument("--log-channels=gdb-remote packets");
44   args.AppendArgument("--reverse-connect");
45   std::string log_file_name = GenerateLogFileName(arch_spec);
46   if (log_file_name.size()) {
47     args.AppendArgument("--log-file=" + log_file_name);
48   }
49 
50   Status error;
51   TCPSocket listen_socket(true, false);
52   error = listen_socket.Listen("127.0.0.1:0", 5);
53   if (error.Fail()) {
54     GTEST_LOG_(ERROR) << "Unable to open listen socket.";
55     return false;
56   }
57 
58   char connect_remote_address[64];
59   snprintf(connect_remote_address, sizeof(connect_remote_address),
60            "localhost:%u", listen_socket.GetLocalPortNumber());
61 
62   args.AppendArgument(connect_remote_address);
63 
64   m_server_process_info.SetArchitecture(arch_spec);
65   m_server_process_info.SetArguments(args, true);
66   Status status = Host::LaunchProcess(m_server_process_info);
67   if (status.Fail()) {
68     GTEST_LOG_(ERROR)
69         << formatv("Failure to launch lldb server: {0}.", status).str();
70     return false;
71   }
72 
73   char connect_remote_uri[64];
74   snprintf(connect_remote_uri, sizeof(connect_remote_uri), "connect://%s",
75            connect_remote_address);
76   Socket *accept_socket;
77   listen_socket.Accept(accept_socket);
78   SetConnection(new ConnectionFileDescriptor(accept_socket));
79 
80   SendAck(); // Send this as a handshake.
81   return true;
82 }
83 
84 bool TestClient::StopDebugger() {
85   std::string response;
86   return SendMessage("k", response, PacketResult::ErrorDisconnected);
87 }
88 
89 bool TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) {
90   std::stringstream command;
91   command << "A";
92   for (size_t i = 0; i < inferior_args.size(); i++) {
93     if (i > 0)
94       command << ',';
95     std::string hex_encoded = toHex(inferior_args[i]);
96     command << hex_encoded.size() << ',' << i << ',' << hex_encoded;
97   }
98 
99   if (!SendMessage(command.str()))
100     return false;
101   if (!SendMessage("qLaunchSuccess"))
102     return false;
103   std::string response;
104   if (!SendMessage("qProcessInfo", response))
105     return false;
106   auto create_or_error = ProcessInfo::Create(response);
107   if (auto create_error = create_or_error.takeError()) {
108     GTEST_LOG_(ERROR) << toString(std::move(create_error));
109     return false;
110   }
111 
112   m_process_info = *create_or_error;
113   return true;
114 }
115 
116 bool TestClient::ListThreadsInStopReply() {
117   return SendMessage("QListThreadsInStopReply");
118 }
119 
120 bool TestClient::SetBreakpoint(unsigned long address) {
121   std::stringstream command;
122   command << "Z0," << std::hex << address << ",1";
123   return SendMessage(command.str());
124 }
125 
126 bool TestClient::ContinueAll() { return Continue("vCont;c"); }
127 
128 bool TestClient::ContinueThread(unsigned long thread_id) {
129   return Continue(formatv("vCont;c:{0:x-}", thread_id).str());
130 }
131 
132 const ProcessInfo &TestClient::GetProcessInfo() { return *m_process_info; }
133 
134 Optional<JThreadsInfo> TestClient::GetJThreadsInfo() {
135   std::string response;
136   if (!SendMessage("jThreadsInfo", response))
137     return llvm::None;
138   auto creation = JThreadsInfo::Create(response, m_process_info->GetEndian());
139   if (auto create_error = creation.takeError()) {
140     GTEST_LOG_(ERROR) << toString(std::move(create_error));
141     return llvm::None;
142   }
143 
144   return std::move(*creation);
145 }
146 
147 const StopReply &TestClient::GetLatestStopReply() {
148   return m_stop_reply.getValue();
149 }
150 
151 bool TestClient::SendMessage(StringRef message) {
152   std::string dummy_string;
153   return SendMessage(message, dummy_string);
154 }
155 
156 bool TestClient::SendMessage(StringRef message, std::string &response_string) {
157   if (!SendMessage(message, response_string, PacketResult::Success))
158     return false;
159   else if (response_string[0] == 'E') {
160     GTEST_LOG_(ERROR) << "Error " << response_string
161                       << " while sending message: " << message.str();
162     return false;
163   }
164 
165   return true;
166 }
167 
168 bool TestClient::SendMessage(StringRef message, std::string &response_string,
169                              PacketResult expected_result) {
170   StringExtractorGDBRemote response;
171   GTEST_LOG_(INFO) << "Send Packet: " << message.str();
172   PacketResult result = SendPacketAndWaitForResponse(message, response, false);
173   response.GetEscapedBinaryData(response_string);
174   GTEST_LOG_(INFO) << "Read Packet: " << response_string;
175   if (result != expected_result) {
176     GTEST_LOG_(ERROR) << FormatFailedResult(message, result);
177     return false;
178   }
179 
180   return true;
181 }
182 
183 unsigned int TestClient::GetPcRegisterId() {
184   if (m_pc_register != UINT_MAX)
185     return m_pc_register;
186 
187   for (unsigned int register_id = 0;; register_id++) {
188     std::string message = formatv("qRegisterInfo{0:x-}", register_id).str();
189     std::string response;
190     if (!SendMessage(message, response)) {
191       GTEST_LOG_(ERROR) << "Unable to query register ID for PC register.";
192       return UINT_MAX;
193     }
194 
195     auto elements_or_error = SplitPairList("GetPcRegisterId", response);
196     if (auto split_error = elements_or_error.takeError()) {
197       GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: "
198                         << response;
199       return UINT_MAX;
200     }
201 
202     auto elements = *elements_or_error;
203     if (elements["alt-name"] == "pc" || elements["generic"] == "pc") {
204       m_pc_register = register_id;
205       break;
206     }
207   }
208 
209   return m_pc_register;
210 }
211 
212 bool TestClient::Continue(StringRef message) {
213   if (!m_process_info.hasValue()) {
214     GTEST_LOG_(ERROR) << "Continue() called before m_process_info initialized.";
215     return false;
216   }
217 
218   std::string response;
219   if (!SendMessage(message, response))
220     return false;
221   auto creation = StopReply::Create(response, m_process_info->GetEndian());
222   if (auto create_error = creation.takeError()) {
223     GTEST_LOG_(ERROR) << toString(std::move(create_error));
224     return false;
225   }
226 
227   m_stop_reply = std::move(*creation);
228   return true;
229 }
230 
231 std::string TestClient::GenerateLogFileName(const ArchSpec &arch) const {
232   char *log_directory = getenv("LOG_FILE_DIRECTORY");
233   if (!log_directory)
234     return "";
235 
236   if (!llvm::sys::fs::is_directory(log_directory)) {
237     GTEST_LOG_(WARNING) << "Cannot access log directory: " << log_directory;
238     return "";
239   }
240 
241   std::string log_file_name;
242   raw_string_ostream log_file(log_file_name);
243   log_file << log_directory << "/lldb-" << m_test_case_name << '-'
244            << m_test_name << '-' << arch.GetArchitectureName() << ".log";
245   return log_file.str();
246 }
247 
248 std::string TestClient::FormatFailedResult(const std::string &message,
249                                            PacketResult result) {
250   std::string formatted_error;
251   raw_string_ostream error_stream(formatted_error);
252   error_stream << "Failure sending message: " << message << " Result: ";
253 
254   switch (result) {
255   case PacketResult::ErrorSendFailed:
256     error_stream << "ErrorSendFailed";
257     break;
258   case PacketResult::ErrorSendAck:
259     error_stream << "ErrorSendAck";
260     break;
261   case PacketResult::ErrorReplyFailed:
262     error_stream << "ErrorReplyFailed";
263     break;
264   case PacketResult::ErrorReplyTimeout:
265     error_stream << "ErrorReplyTimeout";
266     break;
267   case PacketResult::ErrorReplyInvalid:
268     error_stream << "ErrorReplyInvalid";
269     break;
270   case PacketResult::ErrorReplyAck:
271     error_stream << "ErrorReplyAck";
272     break;
273   case PacketResult::ErrorDisconnected:
274     error_stream << "ErrorDisconnected";
275     break;
276   case PacketResult::ErrorNoSequenceLock:
277     error_stream << "ErrorNoSequenceLock";
278     break;
279   default:
280     error_stream << "Unknown Error";
281   }
282 
283   error_stream.str();
284   return formatted_error;
285 }
286 } // namespace llgs_tests
287