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/Host/HostInfo.h" 12 #include "lldb/Host/common/TCPSocket.h" 13 #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" 14 #include "lldb/Interpreter/Args.h" 15 #include "lldb/Target/ProcessLaunchInfo.h" 16 #include "llvm/ADT/StringExtras.h" 17 #include "llvm/Support/Path.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 bool TestClient::IsDebugServer() { 32 return sys::path::filename(LLDB_SERVER).contains("debugserver"); 33 } 34 35 bool TestClient::IsLldbServer() { return !IsDebugServer(); } 36 37 TestClient::TestClient(const std::string &test_name, 38 const std::string &test_case_name) 39 : m_test_name(test_name), m_test_case_name(test_case_name), 40 m_pc_register(UINT_MAX) {} 41 42 TestClient::~TestClient() {} 43 44 llvm::Error TestClient::StartDebugger() { 45 const ArchSpec &arch_spec = HostInfo::GetArchitecture(); 46 Args args; 47 args.AppendArgument(LLDB_SERVER); 48 if (IsLldbServer()) { 49 args.AppendArgument("gdbserver"); 50 args.AppendArgument("--log-channels=gdb-remote packets"); 51 } else { 52 args.AppendArgument("--log-flags=0x800000"); 53 } 54 args.AppendArgument("--reverse-connect"); 55 std::string log_file_name = GenerateLogFileName(arch_spec); 56 if (log_file_name.size()) 57 args.AppendArgument("--log-file=" + log_file_name); 58 59 Status status; 60 TCPSocket listen_socket(true, false); 61 status = listen_socket.Listen("127.0.0.1:0", 5); 62 if (status.Fail()) 63 return status.ToError(); 64 65 char connect_remote_address[64]; 66 snprintf(connect_remote_address, sizeof(connect_remote_address), 67 "localhost:%u", listen_socket.GetLocalPortNumber()); 68 69 args.AppendArgument(connect_remote_address); 70 71 m_server_process_info.SetArchitecture(arch_spec); 72 m_server_process_info.SetArguments(args, true); 73 status = Host::LaunchProcess(m_server_process_info); 74 if (status.Fail()) 75 return status.ToError(); 76 77 char connect_remote_uri[64]; 78 snprintf(connect_remote_uri, sizeof(connect_remote_uri), "connect://%s", 79 connect_remote_address); 80 Socket *accept_socket; 81 listen_socket.Accept(accept_socket); 82 SetConnection(new ConnectionFileDescriptor(accept_socket)); 83 84 SendAck(); // Send this as a handshake. 85 return llvm::Error::success(); 86 } 87 88 llvm::Error TestClient::StopDebugger() { 89 std::string response; 90 // Debugserver (non-conformingly?) sends a reply to the k packet instead of 91 // simply closing the connection. 92 PacketResult result = 93 IsDebugServer() ? PacketResult::Success : PacketResult::ErrorDisconnected; 94 return SendMessage("k", response, result); 95 } 96 97 Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) { 98 StringList env; 99 Host::GetEnvironment(env); 100 for (size_t i = 0; i < env.GetSize(); ++i) { 101 if (SendEnvironmentPacket(env[i].c_str()) != 0) { 102 return make_error<StringError>( 103 formatv("Failed to set environment variable: {0}", env[i]).str(), 104 inconvertibleErrorCode()); 105 } 106 } 107 std::stringstream command; 108 command << "A"; 109 for (size_t i = 0; i < inferior_args.size(); i++) { 110 if (i > 0) 111 command << ','; 112 std::string hex_encoded = toHex(inferior_args[i]); 113 command << hex_encoded.size() << ',' << i << ',' << hex_encoded; 114 } 115 116 if (Error E = SendMessage(command.str())) 117 return E; 118 if (Error E = SendMessage("qLaunchSuccess")) 119 return E; 120 std::string response; 121 if (Error E = SendMessage("qProcessInfo", response)) 122 return E; 123 auto create_or_error = ProcessInfo::Create(response); 124 if (auto create_error = create_or_error.takeError()) 125 return create_error; 126 127 m_process_info = *create_or_error; 128 return Error::success(); 129 } 130 131 Error TestClient::ListThreadsInStopReply() { 132 return SendMessage("QListThreadsInStopReply"); 133 } 134 135 Error TestClient::SetBreakpoint(unsigned long address) { 136 return SendMessage(formatv("Z0,{0:x-},1", address).str()); 137 } 138 139 Error TestClient::ContinueAll() { return Continue("vCont;c"); } 140 141 Error TestClient::ContinueThread(unsigned long thread_id) { 142 return Continue(formatv("vCont;c:{0:x-}", thread_id).str()); 143 } 144 145 const ProcessInfo &TestClient::GetProcessInfo() { return *m_process_info; } 146 147 Optional<JThreadsInfo> TestClient::GetJThreadsInfo() { 148 std::string response; 149 if (SendMessage("jThreadsInfo", response)) 150 return llvm::None; 151 auto creation = JThreadsInfo::Create(response, m_process_info->GetEndian()); 152 if (auto create_error = creation.takeError()) { 153 GTEST_LOG_(ERROR) << toString(std::move(create_error)); 154 return llvm::None; 155 } 156 157 return std::move(*creation); 158 } 159 160 const StopReply &TestClient::GetLatestStopReply() { 161 return m_stop_reply.getValue(); 162 } 163 164 Error TestClient::SendMessage(StringRef message) { 165 std::string dummy_string; 166 return SendMessage(message, dummy_string); 167 } 168 169 Error TestClient::SendMessage(StringRef message, std::string &response_string) { 170 if (Error E = SendMessage(message, response_string, PacketResult::Success)) 171 return E; 172 if (response_string[0] == 'E') { 173 return make_error<StringError>( 174 formatv("Error `{0}` while sending message: {1}", response_string, 175 message) 176 .str(), 177 inconvertibleErrorCode()); 178 } 179 return Error::success(); 180 } 181 182 Error TestClient::SendMessage(StringRef message, std::string &response_string, 183 PacketResult expected_result) { 184 StringExtractorGDBRemote response; 185 GTEST_LOG_(INFO) << "Send Packet: " << message.str(); 186 PacketResult result = SendPacketAndWaitForResponse(message, response, false); 187 response.GetEscapedBinaryData(response_string); 188 GTEST_LOG_(INFO) << "Read Packet: " << response_string; 189 if (result != expected_result) 190 return make_error<StringError>( 191 formatv("Error sending message `{0}`: {1}", message, result).str(), 192 inconvertibleErrorCode()); 193 194 return Error::success(); 195 } 196 197 unsigned int TestClient::GetPcRegisterId() { 198 if (m_pc_register != UINT_MAX) 199 return m_pc_register; 200 201 for (unsigned int register_id = 0;; register_id++) { 202 std::string message = formatv("qRegisterInfo{0:x-}", register_id).str(); 203 std::string response; 204 if (SendMessage(message, response)) { 205 GTEST_LOG_(ERROR) << "Unable to query register ID for PC register."; 206 return UINT_MAX; 207 } 208 209 auto elements_or_error = SplitUniquePairList("GetPcRegisterId", response); 210 if (auto split_error = elements_or_error.takeError()) { 211 GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: " 212 << response; 213 return UINT_MAX; 214 } 215 216 auto elements = *elements_or_error; 217 if (elements["alt-name"] == "pc" || elements["generic"] == "pc") { 218 m_pc_register = register_id; 219 break; 220 } 221 } 222 223 return m_pc_register; 224 } 225 226 Error TestClient::Continue(StringRef message) { 227 assert(m_process_info.hasValue()); 228 229 std::string response; 230 if (Error E = SendMessage(message, response)) 231 return E; 232 auto creation = StopReply::Create(response, m_process_info->GetEndian()); 233 if (Error E = creation.takeError()) 234 return E; 235 236 m_stop_reply = std::move(*creation); 237 return Error::success(); 238 } 239 240 std::string TestClient::GenerateLogFileName(const ArchSpec &arch) const { 241 char *log_directory = getenv("LOG_FILE_DIRECTORY"); 242 if (!log_directory) 243 return ""; 244 245 if (!llvm::sys::fs::is_directory(log_directory)) { 246 GTEST_LOG_(WARNING) << "Cannot access log directory: " << log_directory; 247 return ""; 248 } 249 250 std::string log_file_name; 251 raw_string_ostream log_file(log_file_name); 252 log_file << log_directory << "/lldb-" << m_test_case_name << '-' 253 << m_test_name << '-' << arch.GetArchitectureName() << ".log"; 254 return log_file.str(); 255 } 256 257 } // namespace llgs_tests 258