Home | History | Annotate | Download | only in execserver
      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 TestProcess implementation for Unix-like systems.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "xsPosixTestProcess.hpp"
     25 #include "deFilePath.hpp"
     26 #include "deClock.h"
     27 
     28 #include <string.h>
     29 #include <stdio.h>
     30 
     31 using std::string;
     32 using std::vector;
     33 
     34 namespace xs
     35 {
     36 
     37 namespace posix
     38 {
     39 
     40 CaseListWriter::CaseListWriter (void)
     41 	: m_file	(DE_NULL)
     42 	, m_run		(false)
     43 {
     44 }
     45 
     46 CaseListWriter::~CaseListWriter (void)
     47 {
     48 }
     49 
     50 void CaseListWriter::start (const char* caseList, deFile* dst)
     51 {
     52 	DE_ASSERT(!isStarted());
     53 	m_file	= dst;
     54 	m_run	= true;
     55 
     56 	int caseListSize = (int)strlen(caseList)+1;
     57 	m_caseList.resize(caseListSize);
     58 	std::copy(caseList, caseList+caseListSize, m_caseList.begin());
     59 
     60 	// Set to non-blocking mode.
     61 	if (!deFile_setFlags(m_file, DE_FILE_NONBLOCKING))
     62 		XS_FAIL("Failed to set non-blocking mode");
     63 
     64 	de::Thread::start();
     65 }
     66 
     67 void CaseListWriter::run (void)
     68 {
     69 	deInt64 pos = 0;
     70 
     71 	while (m_run && pos < (deInt64)m_caseList.size())
     72 	{
     73 		deInt64			numWritten	= 0;
     74 		deFileResult	result		= deFile_write(m_file, &m_caseList[0] + pos, m_caseList.size()-pos, &numWritten);
     75 
     76 		if (result == DE_FILERESULT_SUCCESS)
     77 			pos += numWritten;
     78 		else if (result == DE_FILERESULT_WOULD_BLOCK)
     79 			deSleep(1); // Yield.
     80 		else
     81 			break; // Error.
     82 	}
     83 }
     84 
     85 void CaseListWriter::stop (void)
     86 {
     87 	if (!isStarted())
     88 		return; // Nothing to do.
     89 
     90 	m_run = false;
     91 
     92 	// Join thread.
     93 	join();
     94 
     95 	m_file = DE_NULL;
     96 }
     97 
     98 PipeReader::PipeReader (ThreadedByteBuffer* dst)
     99 	: m_file	(DE_NULL)
    100 	, m_buf		(dst)
    101 {
    102 }
    103 
    104 PipeReader::~PipeReader (void)
    105 {
    106 }
    107 
    108 void PipeReader::start (deFile* file)
    109 {
    110 	DE_ASSERT(!isStarted());
    111 
    112 	// Set to non-blocking mode.
    113 	if (!deFile_setFlags(file, DE_FILE_NONBLOCKING))
    114 		XS_FAIL("Failed to set non-blocking mode");
    115 
    116 	m_file = file;
    117 
    118 	de::Thread::start();
    119 }
    120 
    121 void PipeReader::run (void)
    122 {
    123 	std::vector<deUint8>	tmpBuf		(FILEREADER_TMP_BUFFER_SIZE);
    124 	deInt64					numRead		= 0;
    125 
    126 	while (!m_buf->isCanceled())
    127 	{
    128 		deFileResult result = deFile_read(m_file, &tmpBuf[0], (deInt64)tmpBuf.size(), &numRead);
    129 
    130 		if (result == DE_FILERESULT_SUCCESS)
    131 		{
    132 			// Write to buffer.
    133 			try
    134 			{
    135 				m_buf->write((int)numRead, &tmpBuf[0]);
    136 				m_buf->flush();
    137 			}
    138 			catch (const ThreadedByteBuffer::CanceledException&)
    139 			{
    140 				// Canceled.
    141 				break;
    142 			}
    143 		}
    144 		else if (result == DE_FILERESULT_END_OF_FILE ||
    145 				 result == DE_FILERESULT_WOULD_BLOCK)
    146 		{
    147 			// Wait for more data.
    148 			deSleep(FILEREADER_IDLE_SLEEP);
    149 		}
    150 		else
    151 			break; // Error.
    152 	}
    153 }
    154 
    155 void PipeReader::stop (void)
    156 {
    157 	if (!isStarted())
    158 		return; // Nothing to do.
    159 
    160 	// Buffer must be in canceled state or otherwise stopping reader might block.
    161 	DE_ASSERT(m_buf->isCanceled());
    162 
    163 	// Join thread.
    164 	join();
    165 
    166 	m_file = DE_NULL;
    167 }
    168 
    169 } // unix
    170 
    171 PosixTestProcess::PosixTestProcess (void)
    172 	: m_process				(DE_NULL)
    173 	, m_processStartTime	(0)
    174 	, m_infoBuffer			(INFO_BUFFER_BLOCK_SIZE, INFO_BUFFER_NUM_BLOCKS)
    175 	, m_stdOutReader		(&m_infoBuffer)
    176 	, m_stdErrReader		(&m_infoBuffer)
    177 	, m_logReader			(LOG_BUFFER_BLOCK_SIZE, LOG_BUFFER_NUM_BLOCKS)
    178 {
    179 }
    180 
    181 PosixTestProcess::~PosixTestProcess (void)
    182 {
    183 	delete m_process;
    184 }
    185 
    186 void PosixTestProcess::start (const char* name, const char* params, const char* workingDir, const char* caseList)
    187 {
    188 	bool hasCaseList = strlen(caseList) > 0;
    189 
    190 	XS_CHECK(!m_process);
    191 
    192 	de::FilePath logFilePath = de::FilePath::join(workingDir, "TestResults.qpa");
    193 	m_logFileName = logFilePath.getPath();
    194 
    195 	// Remove old file if such exists.
    196 	if (deFileExists(m_logFileName.c_str()))
    197 	{
    198 		if (!deDeleteFile(m_logFileName.c_str()) || deFileExists(m_logFileName.c_str()))
    199 			throw TestProcessException(string("Failed to remove '") + m_logFileName + "'");
    200 	}
    201 
    202 	// Construct command line.
    203 	string cmdLine = de::FilePath(name).isAbsolutePath() ? name : de::FilePath::join(workingDir, name).normalize().getPath();
    204 	cmdLine += string(" --deqp-log-filename=") + logFilePath.getBaseName();
    205 
    206 	if (hasCaseList)
    207 		cmdLine += " --deqp-stdin-caselist";
    208 
    209 	if (strlen(params) > 0)
    210 		cmdLine += string(" ") + params;
    211 
    212 	DE_ASSERT(!m_process);
    213 	m_process = new de::Process();
    214 
    215 	try
    216 	{
    217 		m_process->start(cmdLine.c_str(), strlen(workingDir) > 0 ? workingDir : DE_NULL);
    218 	}
    219 	catch (const de::ProcessError& e)
    220 	{
    221 		delete m_process;
    222 		m_process = DE_NULL;
    223 		throw TestProcessException(e.what());
    224 	}
    225 
    226 	m_processStartTime = deGetMicroseconds();
    227 
    228 	// Create stdout & stderr readers.
    229 	if (m_process->getStdOut())
    230 		m_stdOutReader.start(m_process->getStdOut());
    231 
    232 	if (m_process->getStdErr())
    233 		m_stdErrReader.start(m_process->getStdErr());
    234 
    235 	// Start case list writer.
    236 	if (hasCaseList)
    237 	{
    238 		deFile* dst = m_process->getStdIn();
    239 		if (dst)
    240 			m_caseListWriter.start(caseList, dst);
    241 		else
    242 		{
    243 			cleanup();
    244 			throw TestProcessException("Failed to write case list");
    245 		}
    246 	}
    247 }
    248 
    249 void PosixTestProcess::terminate (void)
    250 {
    251 	if (m_process)
    252 	{
    253 		try
    254 		{
    255 			m_process->kill();
    256 		}
    257 		catch (const std::exception& e)
    258 		{
    259 			printf("PosixTestProcess::terminate(): Failed to kill process: %s\n", e.what());
    260 		}
    261 	}
    262 }
    263 
    264 void PosixTestProcess::cleanup (void)
    265 {
    266 	m_caseListWriter.stop();
    267 	m_logReader.stop();
    268 
    269 	// \note Info buffer must be canceled before stopping pipe readers.
    270 	m_infoBuffer.cancel();
    271 
    272 	m_stdErrReader.stop();
    273 	m_stdOutReader.stop();
    274 
    275 	// Reset info buffer.
    276 	m_infoBuffer.clear();
    277 
    278 	if (m_process)
    279 	{
    280 		try
    281 		{
    282 			if (m_process->isRunning())
    283 			{
    284 				m_process->kill();
    285 				m_process->waitForFinish();
    286 			}
    287 		}
    288 		catch (const de::ProcessError& e)
    289 		{
    290 			printf("PosixTestProcess::stop(): Failed to kill process: %s\n", e.what());
    291 		}
    292 
    293 		delete m_process;
    294 		m_process = DE_NULL;
    295 	}
    296 }
    297 
    298 bool PosixTestProcess::isRunning (void)
    299 {
    300 	if (m_process)
    301 		return m_process->isRunning();
    302 	else
    303 		return false;
    304 }
    305 
    306 int PosixTestProcess::getExitCode (void) const
    307 {
    308 	if (m_process)
    309 		return m_process->getExitCode();
    310 	else
    311 		return -1;
    312 }
    313 
    314 int PosixTestProcess::readTestLog (deUint8* dst, int numBytes)
    315 {
    316 	if (!m_logReader.isRunning())
    317 	{
    318 		if (deGetMicroseconds() - m_processStartTime > LOG_FILE_TIMEOUT*1000)
    319 		{
    320 			// Timeout, kill process.
    321 			terminate();
    322 			return 0; // \todo [2013-08-13 pyry] Throw exception?
    323 		}
    324 
    325 		if (!deFileExists(m_logFileName.c_str()))
    326 			return 0;
    327 
    328 		// Start reader.
    329 		m_logReader.start(m_logFileName.c_str());
    330 	}
    331 
    332 	DE_ASSERT(m_logReader.isRunning());
    333 	return m_logReader.read(dst, numBytes);
    334 }
    335 
    336 } // xs
    337