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