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