xref: /aosp_15_r20/external/deqp/execserver/tools/xsClient.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Execution Server
3  * ---------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief ExecServer Client.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "xsDefs.hpp"
25 #include "xsProtocol.hpp"
26 #include "deSocket.hpp"
27 #include "deUniquePtr.hpp"
28 
29 #include "deString.h"
30 
31 #include <memory>
32 #include <sstream>
33 #include <fstream>
34 #include <cstdio>
35 #include <cstdlib>
36 
37 using std::string;
38 using std::vector;
39 
40 namespace xs
41 {
42 
43 typedef de::UniquePtr<Message> ScopedMsgPtr;
44 
45 class SocketError : public Error
46 {
47 public:
SocketError(deSocketResult result,const char * message,const char * file,int line)48     SocketError(deSocketResult result, const char *message, const char *file, int line)
49         : Error(message, deGetSocketResultName(result), file, line)
50         , m_result(result)
51     {
52     }
53 
getResult(void) const54     deSocketResult getResult(void) const
55     {
56         return m_result;
57     }
58 
59 private:
60     deSocketResult m_result;
61 };
62 
63 // Helpers.
sendMessage(de::Socket & socket,const Message & message)64 void sendMessage(de::Socket &socket, const Message &message)
65 {
66     // Format message.
67     vector<uint8_t> buf;
68     message.write(buf);
69 
70     // Write to socket.
71     size_t pos = 0;
72     while (pos < buf.size())
73     {
74         size_t numLeft        = buf.size() - pos;
75         size_t numSent        = 0;
76         deSocketResult result = socket.send(&buf[pos], numLeft, &numSent);
77 
78         if (result != DE_SOCKETRESULT_SUCCESS)
79             throw SocketError(result, "send() failed", __FILE__, __LINE__);
80 
81         pos += numSent;
82     }
83 }
84 
readBytes(de::Socket & socket,vector<uint8_t> & dst,size_t numBytes)85 void readBytes(de::Socket &socket, vector<uint8_t> &dst, size_t numBytes)
86 {
87     size_t numRead = 0;
88     dst.resize(numBytes);
89     while (numRead < numBytes)
90     {
91         size_t numLeft        = numBytes - numRead;
92         size_t curNumRead     = 0;
93         deSocketResult result = socket.receive(&dst[numRead], numLeft, &curNumRead);
94 
95         if (result != DE_SOCKETRESULT_SUCCESS)
96             throw SocketError(result, "receive() failed", __FILE__, __LINE__);
97 
98         numRead += curNumRead;
99     }
100 }
101 
readMessage(de::Socket & socket)102 Message *readMessage(de::Socket &socket)
103 {
104     // Header.
105     vector<uint8_t> header;
106     readBytes(socket, header, MESSAGE_HEADER_SIZE);
107 
108     MessageType type;
109     size_t messageSize;
110     Message::parseHeader(&header[0], (int)header.size(), type, messageSize);
111 
112     // Simple messages without any data.
113     switch (type)
114     {
115     case MESSAGETYPE_KEEPALIVE:
116         return new KeepAliveMessage();
117     case MESSAGETYPE_PROCESS_STARTED:
118         return new ProcessStartedMessage();
119     default:
120         break; // Read message with data.
121     }
122 
123     vector<uint8_t> messageBuf;
124     readBytes(socket, messageBuf, messageSize - MESSAGE_HEADER_SIZE);
125 
126     switch (type)
127     {
128     case MESSAGETYPE_HELLO:
129         return new HelloMessage(&messageBuf[0], (int)messageBuf.size());
130     case MESSAGETYPE_TEST:
131         return new TestMessage(&messageBuf[0], (int)messageBuf.size());
132     case MESSAGETYPE_PROCESS_LOG_DATA:
133         return new ProcessLogDataMessage(&messageBuf[0], (int)messageBuf.size());
134     case MESSAGETYPE_INFO:
135         return new InfoMessage(&messageBuf[0], (int)messageBuf.size());
136     case MESSAGETYPE_PROCESS_LAUNCH_FAILED:
137         return new ProcessLaunchFailedMessage(&messageBuf[0], (int)messageBuf.size());
138     case MESSAGETYPE_PROCESS_FINISHED:
139         return new ProcessFinishedMessage(&messageBuf[0], (int)messageBuf.size());
140     default:
141         XS_FAIL("Unknown message");
142     }
143 }
144 
145 class CommandLine
146 {
147 public:
148     de::SocketAddress address;
149     std::string program;
150     std::string params;
151     std::string workingDir;
152     std::string caseList;
153     std::string dstFileName;
154 };
155 
156 class Client
157 {
158 public:
159     Client(const CommandLine &cmdLine);
160     ~Client(void);
161 
162     void run(void);
163 
164 private:
165     const CommandLine &m_cmdLine;
166     de::Socket m_socket;
167 };
168 
Client(const CommandLine & cmdLine)169 Client::Client(const CommandLine &cmdLine) : m_cmdLine(cmdLine)
170 {
171 }
172 
~Client(void)173 Client::~Client(void)
174 {
175 }
176 
run(void)177 void Client::run(void)
178 {
179     // Connect to server.
180     m_socket.connect(m_cmdLine.address);
181 
182     printf("Connected to %s:%d!\n", m_cmdLine.address.getHost(), m_cmdLine.address.getPort());
183 
184     // Open result file.
185     std::fstream out(m_cmdLine.dstFileName.c_str(), std::fstream::out | std::fstream::binary);
186 
187     printf("  writing to %s\n", m_cmdLine.dstFileName.c_str());
188 
189     // Send execution request.
190     {
191         ExecuteBinaryMessage msg;
192 
193         msg.name     = m_cmdLine.program;
194         msg.params   = m_cmdLine.params;
195         msg.workDir  = m_cmdLine.workingDir;
196         msg.caseList = m_cmdLine.caseList;
197 
198         sendMessage(m_socket, msg);
199         printf("  execution request sent.\n");
200     }
201 
202     // Run client loop.
203     bool isRunning = true;
204     while (isRunning)
205     {
206         ScopedMsgPtr msg(readMessage(m_socket));
207 
208         switch (msg->type)
209         {
210         case MESSAGETYPE_HELLO:
211             printf("  HelloMessage\n");
212             break;
213 
214         case MESSAGETYPE_KEEPALIVE:
215         {
216             printf("  KeepAliveMessage\n");
217 
218             // Reply with keepalive.
219             sendMessage(m_socket, KeepAliveMessage());
220             break;
221         }
222 
223         case MESSAGETYPE_INFO:
224             printf("  InfoMessage: '%s'\n", static_cast<InfoMessage *>(msg.get())->info.c_str());
225             break;
226 
227         case MESSAGETYPE_PROCESS_STARTED:
228             printf("  ProcessStartedMessage\n");
229             break;
230 
231         case MESSAGETYPE_PROCESS_FINISHED:
232             printf("  ProcessFinished: exit code = %d\n", static_cast<ProcessFinishedMessage *>(msg.get())->exitCode);
233             isRunning = false;
234             break;
235 
236         case MESSAGETYPE_PROCESS_LAUNCH_FAILED:
237             printf("  ProcessLaunchFailed: '%s'\n",
238                    static_cast<ProcessLaunchFailedMessage *>(msg.get())->reason.c_str());
239             isRunning = false;
240             break;
241 
242         case MESSAGETYPE_PROCESS_LOG_DATA:
243         {
244             ProcessLogDataMessage *logDataMsg = static_cast<ProcessLogDataMessage *>(msg.get());
245             printf("  ProcessLogDataMessage: %d bytes\n", (int)logDataMsg->logData.length());
246             out << logDataMsg->logData;
247             break;
248         }
249 
250         default:
251             XS_FAIL("Unknown message");
252         }
253     }
254 
255     // Close output file.
256     out.close();
257 
258     // Close connection.
259     m_socket.shutdown();
260     m_socket.close();
261 
262     printf("Done!\n");
263 }
264 
parseString(const char * str)265 string parseString(const char *str)
266 {
267     if (str[0] == '\'' || str[0] == '"')
268     {
269         const char *p = str;
270         char endChar  = *p++;
271         std::ostringstream o;
272 
273         while (*p != endChar && *p)
274         {
275             if (*p == '\\')
276             {
277                 switch (p[1])
278                 {
279                 case 0:
280                     DE_ASSERT(false);
281                     break;
282                 case 'n':
283                     o << '\n';
284                     break;
285                 case 't':
286                     o << '\t';
287                     break;
288                 default:
289                     o << p[1];
290                     break;
291                 }
292 
293                 p += 2;
294             }
295             else
296                 o << *p++;
297         }
298 
299         return o.str();
300     }
301     else
302         return string(str);
303 }
304 
printHelp(const char * binName)305 void printHelp(const char *binName)
306 {
307     printf("%s:\n", binName);
308     printf("  --host=[host]          Connect to host [host]\n");
309     printf("  --port=[name]          Use port [port]\n");
310     printf("  --program=[program]    Test program\n");
311     printf("  --params=[params]      Test program params\n");
312     printf("  --workdir=[dir]        Working directory\n");
313     printf("  --caselist=[caselist]  Test case list\n");
314     printf("  --out=filename         Test result file\n");
315 }
316 
runClient(int argc,const char * const * argv)317 int runClient(int argc, const char *const *argv)
318 {
319     CommandLine cmdLine;
320 
321     // Defaults.
322     cmdLine.address.setHost("127.0.0.1");
323     cmdLine.address.setPort(50016);
324     cmdLine.dstFileName = "TestResults.qpa";
325 
326     // Parse command line.
327     for (int argNdx = 1; argNdx < argc; argNdx++)
328     {
329         const char *arg = argv[argNdx];
330 
331         if (deStringBeginsWith(arg, "--port="))
332             cmdLine.address.setPort(atoi(arg + 7));
333         else if (deStringBeginsWith(arg, "--host="))
334             cmdLine.address.setHost(parseString(arg + 7).c_str());
335         else if (deStringBeginsWith(arg, "--program="))
336             cmdLine.program = parseString(arg + 10);
337         else if (deStringBeginsWith(arg, "--params="))
338             cmdLine.params = parseString(arg + 9);
339         else if (deStringBeginsWith(arg, "--workdir="))
340             cmdLine.workingDir = parseString(arg + 10);
341         else if (deStringBeginsWith(arg, "--caselist="))
342             cmdLine.caseList = parseString(arg + 11);
343         else if (deStringBeginsWith(arg, "--out="))
344             cmdLine.dstFileName = parseString(arg + 6);
345         else
346         {
347             printHelp(argv[0]);
348             return -1;
349         }
350     }
351 
352     // Run client.
353     try
354     {
355         Client client(cmdLine);
356         client.run();
357     }
358     catch (const std::exception &e)
359     {
360         printf("%s\n", e.what());
361         return -1;
362     }
363 
364     return 0;
365 }
366 
367 } // namespace xs
368 
main(int argc,const char * const * argv)369 int main(int argc, const char *const *argv)
370 {
371     return xs::runClient(argc, argv);
372 }
373